Skip to content

Commit

Permalink
refine roles, define sever roles
Browse files Browse the repository at this point in the history
  • Loading branch information
arily committed Apr 26, 2024
1 parent de79fe4 commit e0b3da2
Show file tree
Hide file tree
Showing 17 changed files with 164 additions and 40 deletions.
1 change: 0 additions & 1 deletion src/common/options/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export const variant: Record<UserRole, { class: string }> = {
[UserRole.Disabled]: { class: 'badge-ghost' },
[UserRole.Restricted]: { class: 'badge-error' },
[UserRole.Inactive]: { class: 'badge-ghost' },
[UserRole.Normal]: { class: 'badge-neutral' },
[UserRole.Supported]: { class: 'badge-neutral' },
[UserRole.Supporter]: { class: 'badge-primary' },
[UserRole.Verified]: { class: 'badge-outline' },
Expand Down
65 changes: 65 additions & 0 deletions src/components/T/multi-checkbox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script setup lang="ts" generic="T extends string | number">
import { match } from 'switch-pattern'
import type { HTMLAttributes } from 'vue'
interface Option {
label: string | number
value: T
disabled?: boolean
attrs?: HTMLAttributes
}
defineProps<{
options: Option[]
size?: 'xs' | 'sm' | 'lg'
}>()
const modelValue = defineModel<T[]>('modelValue')
function toggle(value: T, checked: boolean) {
const _v = [modelValue.value!.includes(value), checked] as const
const { exact, patterns: m } = match(_v)
switch (m) {
case (exact([true, true])):
case (exact([false, false])):
{
return
}
case (exact([false, true])): {
modelValue.value?.push(value)
return
}
case (exact([true, false])): {
modelValue.value?.splice(modelValue.value.findIndex(i => i === value), 1)
}
}
}
// checkbox-xs checkbox-md checkbox-lg
</script>

<template>
<div>
<div
v-for="opt in options"
:key="opt.value"
class="form-control"
>
<label class="label cursor-pointer justify-start gap-4">
<input
class="checkbox"
:class="{
[`checkbox-${size}`]: size,
}"
type="checkbox"
v-bind="opt.attrs"
:checked="modelValue?.includes(opt.value)"
@change="ev => toggle(opt.value, (ev.target as any).checked)"
>
<span class="label-text text-nowrap">{{ opt.label }}</span>
</label>
</div>
</div>
</template>
2 changes: 2 additions & 0 deletions src/composables/useAdapterConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
leaderboardRankingSystems as supportedLeaderboardRankingSystems,
modes as supportedModes,
rankingSystems as supportedRankingSystems,
userRoles as supportedRoles,
rulesets as supportedRulesets,
} from '~/server/trpc/config'

Expand All @@ -19,5 +20,6 @@ export default () => {
hasRuleset,
hasLeaderboardRankingSystem,
hasRankingSystem,
supportedRoles,
}
}
9 changes: 3 additions & 6 deletions src/def/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,11 @@ export enum UserRole {
// restricted type
Disabled = 'disabled',
Restricted = 'restricted',

// // registered without login
// Registered = 'registered',
Inactive = 'inactive',
Normal = 'normal',
Supported = 'supported',
Supporter = 'supporter',

// bancho.py privileges
Verified = 'whitelisted',
Verified = 'verified',

// bancho privileges
Alumni = 'alumni',
Expand All @@ -68,6 +63,8 @@ export enum UserRole {
Bot = 'bot',
}

export const userRoles = Object.values(UserRole).filter((value): value is UserRole => typeof value === 'number') as readonly UserRole[]

export interface UserOldName {
from: Date
to: Date
Expand Down
1 change: 0 additions & 1 deletion src/locales/base/en-GB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export default {
[UserRole.Restricted]: 'Restricted',
// [UserRole.Registered]: 'Registered',
[UserRole.Inactive]: 'Inactive',
[UserRole.Normal]: 'Normal',
[UserRole.Supported]: 'Supported',
[UserRole.Supporter]: 'Supporter',
[UserRole.Verified]: 'Verified',
Expand Down
1 change: 0 additions & 1 deletion src/locales/base/fr-FR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export default {
[UserRole.Restricted]: 'Restreint',
// [UserRole.Registered]: 'Enregistré',
[UserRole.Inactive]: 'Inactif',
[UserRole.Normal]: 'Normal',
[UserRole.Supported]: 'Supporté',
[UserRole.Supporter]: 'Supporter',
[UserRole.Verified]: 'Vérifié',
Expand Down
1 change: 0 additions & 1 deletion src/locales/base/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export default {
[UserRole.Restricted]: '已被封禁',
// [UserRole.Registered]: '已注册',
[UserRole.Inactive]: '不活跃的',
[UserRole.Normal]: '普通',
[UserRole.Supported]: '赞助过',
[UserRole.Supporter]: '赞助者',
[UserRole.Verified]: '已认证',
Expand Down
63 changes: 55 additions & 8 deletions src/pages/admin/users/[id].vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
<script setup lang="ts">
import md5 from 'md5'
import type { HTMLAttributes, InputHTMLAttributes } from 'vue'
import { CountryCode } from '~/def/country-code'
import { roles as options } from '~/common/options'
import { UserRole } from '~/def/user'
import { useSession } from '~/store/session'
const app = useNuxtApp()
const { t } = useI18n()
const route = useRoute('admin-users-id')
const adapter = useAdapterConfig()
const session = useSession()
const disallowUserEditItselfRole = new Set([
UserRole.Owner,
UserRole.Admin,
UserRole.Staff,
])
const error = ref<Error>()
const f = await app.$client.admin.userManagement.detail.query(route.params.id)
const detail = ref(f as typeof f & { password?: string })
// eslint-disable-next-line antfu/no-const-enum
const enum Status {
Idle,
Expand All @@ -24,6 +35,23 @@ const icon = {
} as const
const status = ref(Status.Idle)
const _opts = createOptions(UserRole, (_, v) => t(localeKey.role(v)))
const opts = computed(() =>
_opts
.map((item) => {
const attrs: HTMLAttributes & InputHTMLAttributes = {}
if (
(session.user?.id === detail.value.id && disallowUserEditItselfRole.has(item.value)) // prevent self from removing its priv
|| !isEditable(session.role, item.value)
) {
attrs.disabled = true
}
return { ...item, attrs }
})
.filter(i => adapter.supportedRoles.includes(i.value))
)
async function save() {
error.value = undefined
status.value = Status.Pending
Expand All @@ -40,6 +68,23 @@ async function save() {
}
}
}
// TODO server validation impl same logic
function isEditable(stat: Record<'admin' | 'owner' | 'staff', boolean>, role: UserRole) {
switch (role) {
case UserRole.Admin: {
return stat.owner
}
case UserRole.Staff: {
return stat.admin || stat.owner
}
case UserRole.Owner: {
return stat.owner
}
default:
return true
}
}
</script>

<i18n lang="yaml">
Expand Down Expand Up @@ -78,7 +123,12 @@ fr-FR:
<template>
<div v-if="detail" class="container custom-container">
<div v-if="error" class="overflow-x-auto text-left alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none" viewBox="0 0 24 24">
<path
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<pre>{{ formatGucchoError(error) }}</pre>
</div>
<dl>
Expand Down Expand Up @@ -143,11 +193,8 @@ fr-FR:
<img :alt="detail.flag" :src="getFlagURL(detail.flag)" class="w-6">
<select v-model="detail.flag" class="w-full select select-sm">
<option
v-for="countryCode in CountryCode"
:key="countryCode"
:disabled="countryCode === detail.flag"
:selected="countryCode === detail.flag"
:value="countryCode"
v-for="countryCode in CountryCode" :key="countryCode" :disabled="countryCode === detail.flag"
:selected="countryCode === detail.flag" :value="countryCode"
>
<template v-if="countryCode === CountryCode.Unknown">
Expand All @@ -169,7 +216,7 @@ fr-FR:
{{ t('roles') }}
</dt>
<dd class="striped-text">
<t-multi-select v-model="detail.roles" class="w-full" size="sm" :options="options($t)" />
<t-multi-checkbox v-model="detail.roles" size="sm" :options="opts" />
</dd>
</div>
</dl>
Expand Down
1 change: 1 addition & 0 deletions src/pages/admin/users/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { type UserCompact, type UserOptional, UserRole } from '~/def/user'
const { t, locale } = useI18n()
const route = useRoute('admin-users')
const validator = object({
page: coerce.number().default(0),
perPage: coerce.number().default(10),
Expand Down
1 change: 1 addition & 0 deletions src/server/backend/$base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ export const hasRuleset: HasRuleset = <M extends ActiveMode>(
): ruleset is AvailableRuleset<M> => false

export { modes, rulesets } from '~/def'
export { userRoles } from '~/def/user'

export const features = new Set<Feature>([])
1 change: 0 additions & 1 deletion src/server/backend/bancho.py/drizzle/schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { bigint, boolean, char, date, datetime, index, int, mysqlEnum, mysqlTable, primaryKey, timestamp, tinyint, unique, varchar } from 'drizzle-orm/mysql-core'
import { relations, sql } from 'drizzle-orm'
import { userRelations } from '../../../singleton/service'
import { decimal } from './fixed-point'

const bpyServerEnum = mysqlEnum('server', ['osu!', 'private'])
Expand Down
14 changes: 14 additions & 0 deletions src/server/backend/bancho.py/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import type { Feature } from '~/def/features'
import { UserRole } from '~/def/user'

export { modes, rulesets, rankingSystems, leaderboardRankingSystems } from '~/def'

export const userRoles = [
UserRole.Restricted,
UserRole.Verified,
UserRole.Supporter,
UserRole.Alumni,
UserRole.TournamentStaff,
UserRole.BeatmapNominator,
UserRole.Moderator,
UserRole.Admin,
UserRole.Owner,
UserRole.Bot,
]

export {
hasLeaderboardRankingSystem,
hasRankingSystem,
Expand Down
16 changes: 6 additions & 10 deletions src/server/backend/bancho.py/transforms/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ export function toRoles(priv: number): UserRole[] {
if ((priv & BanchoPyPrivilege.Normal) !== BanchoPyPrivilege.Normal) {
roles.push(UserRole.Restricted)
}
else if (priv & (BanchoPyPrivilege.Verified | BanchoPyPrivilege.Normal)) {
roles.push(UserRole.Normal)
}

if (priv & BanchoPyPrivilege.Whitelisted) {
roles.push(UserRole.Verified)
Expand Down Expand Up @@ -73,7 +70,10 @@ export function toRoles(priv: number): UserRole[] {
}

export function toBanchoPyPriv(input: UserRole[]): number {
let curr = 0
let curr = input.includes(UserRole.Restricted)
? BanchoPyPrivilege.Any
: BanchoPyPrivilege.Normal

for (const role of input) {
curr |= toOneBanchoPyPriv(role)
}
Expand All @@ -84,10 +84,6 @@ export function toOneBanchoPyPriv(role: UserRole): number {
switch (role) {
case UserRole.Restricted:
return BanchoPyPrivilege.Any
// case UserRole.Registered:
// return BanchoPyPrivilege.Normal
case UserRole.Normal:
return BanchoPyPrivilege.Verified | BanchoPyPrivilege.Normal
case UserRole.Verified:
return BanchoPyPrivilege.Whitelisted
case UserRole.Supporter:
Expand All @@ -100,8 +96,8 @@ export function toOneBanchoPyPriv(role: UserRole): number {
return BanchoPyPrivilege.Nominator
case UserRole.Moderator:
return BanchoPyPrivilege.Mod
case UserRole.Staff:
return BanchoPyPrivilege.Staff
// case UserRole.Staff:
// return BanchoPyPrivilege.Staff
case UserRole.Admin:
return BanchoPyPrivilege.Admin
case UserRole.Owner:
Expand Down
1 change: 1 addition & 0 deletions src/server/backend/[email protected]/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export {
rulesets,
rankingSystems,
leaderboardRankingSystems,
userRoles,
} from '../bancho.py'
export type { Id, ScoreId } from '../bancho.py'

Expand Down
1 change: 1 addition & 0 deletions src/server/trpc/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export {
rulesets,
rankingSystems,
leaderboardRankingSystems,
userRoles,
} from '$active'
Loading

0 comments on commit e0b3da2

Please sign in to comment.