parent
74910f8d70
commit
c5c40a73b7
9 changed files with 296 additions and 4 deletions
|
@ -938,7 +938,12 @@ _role:
|
||||||
name: "ロール名"
|
name: "ロール名"
|
||||||
description: "ロールの説明"
|
description: "ロールの説明"
|
||||||
permission: "ロールの権限"
|
permission: "ロールの権限"
|
||||||
descriptionOfType: "モデレーターは基本的なモデレーションに関する操作を行えます。\n管理者はインスタンスの全ての設定を変更できます。"
|
descriptionOfPermission: "<b>モデレーター</b>は基本的なモデレーションに関する操作を行えます。\n<b>管理者</b>はインスタンスの全ての設定を変更できます。"
|
||||||
|
assignTarget: "アサインターゲット"
|
||||||
|
descriptionOfAssignTarget: "<b>マニュアル</b>は誰がこのロールに含まれるかを手動で管理します。\n<b>コンディショナル</b>は条件を設定し、それに合致するユーザーが自動で含まれるようになります。"
|
||||||
|
manual: "マニュアル"
|
||||||
|
conditional: "コンディショナル"
|
||||||
|
condition: "条件"
|
||||||
isPublic: "ロールを公開"
|
isPublic: "ロールを公開"
|
||||||
descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができます。また、ユーザーのプロフィールでこのロールが表示されます。"
|
descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができます。また、ユーザーのプロフィールでこのロールが表示されます。"
|
||||||
options: "オプション"
|
options: "オプション"
|
||||||
|
@ -953,6 +958,14 @@ _role:
|
||||||
canPublicNote: "パブリック投稿の許可"
|
canPublicNote: "パブリック投稿の許可"
|
||||||
driveCapacity: "ドライブ容量"
|
driveCapacity: "ドライブ容量"
|
||||||
antennaMax: "アンテナの作成可能数"
|
antennaMax: "アンテナの作成可能数"
|
||||||
|
_condition:
|
||||||
|
isLocal: "ローカルユーザー"
|
||||||
|
isRemote: "リモートユーザー"
|
||||||
|
createdLessThan: "アカウント作成から~以内"
|
||||||
|
createdMoreThan: "アカウント作成から~経過"
|
||||||
|
and: "~かつ~"
|
||||||
|
or: "~または~"
|
||||||
|
not: "~ではない"
|
||||||
|
|
||||||
_sensitiveMediaDetection:
|
_sensitiveMediaDetection:
|
||||||
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"
|
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"
|
||||||
|
|
15
packages/backend/migration/1673570377815-RoleConditional.js
Normal file
15
packages/backend/migration/1673570377815-RoleConditional.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
export class RoleConditional1673570377815 {
|
||||||
|
name = 'RoleConditional1673570377815'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."role_target_enum" AS ENUM('manual', 'conditional')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "role" ADD "target" "public"."role_target_enum" NOT NULL DEFAULT 'manual'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "role" ADD "condFormula" jsonb NOT NULL DEFAULT '{}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "condFormula"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "target"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."role_target_enum"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,9 @@ import type { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/mode
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
|
import { UserCacheService } from '@/core/UserCacheService.js';
|
||||||
|
import { RoleCondFormulaValue } from '@/models/entities/Role.js';
|
||||||
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
export type RoleOptions = {
|
export type RoleOptions = {
|
||||||
|
@ -44,6 +47,8 @@ export class RoleService implements OnApplicationShutdown {
|
||||||
private roleAssignmentsRepository: RoleAssignmentsRepository,
|
private roleAssignmentsRepository: RoleAssignmentsRepository,
|
||||||
|
|
||||||
private metaService: MetaService,
|
private metaService: MetaService,
|
||||||
|
private userCacheService: UserCacheService,
|
||||||
|
private userEntityService: UserEntityService,
|
||||||
) {
|
) {
|
||||||
//this.onMessage = this.onMessage.bind(this);
|
//this.onMessage = this.onMessage.bind(this);
|
||||||
|
|
||||||
|
@ -111,12 +116,49 @@ export class RoleService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private evalCond(user: User, value: RoleCondFormulaValue): boolean {
|
||||||
|
try {
|
||||||
|
switch (value.type) {
|
||||||
|
case 'and': {
|
||||||
|
return value.values.every(v => this.evalCond(user, v));
|
||||||
|
}
|
||||||
|
case 'or': {
|
||||||
|
return value.values.some(v => this.evalCond(user, v));
|
||||||
|
}
|
||||||
|
case 'not': {
|
||||||
|
return !this.evalCond(user, value.value);
|
||||||
|
}
|
||||||
|
case 'isLocal': {
|
||||||
|
return this.userEntityService.isLocalUser(user);
|
||||||
|
}
|
||||||
|
case 'isRemote': {
|
||||||
|
return this.userEntityService.isRemoteUser(user);
|
||||||
|
}
|
||||||
|
case 'createdLessThan': {
|
||||||
|
return user.createdAt.getTime() > (Date.now() - (value.sec * 1000));
|
||||||
|
}
|
||||||
|
case 'createdMoreThan': {
|
||||||
|
return user.createdAt.getTime() < (Date.now() - (value.sec * 1000));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// TODO: log error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getUserRoles(userId: User['id']) {
|
public async getUserRoles(userId: User['id']) {
|
||||||
const assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
|
const assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
|
||||||
const assignedRoleIds = assigns.map(x => x.roleId);
|
const assignedRoleIds = assigns.map(x => x.roleId);
|
||||||
const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({}));
|
const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({}));
|
||||||
return roles.filter(r => assignedRoleIds.includes(r.id));
|
const assignedRoles = roles.filter(r => assignedRoleIds.includes(r.id));
|
||||||
|
const user = roles.some(r => r.target === 'conditional') ? await this.userCacheService.findById(userId) : null;
|
||||||
|
const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, r.condFormula));
|
||||||
|
return [...assignedRoles, ...matchedCondRoles];
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
|
|
@ -55,6 +55,8 @@ export class RoleEntityService {
|
||||||
name: role.name,
|
name: role.name,
|
||||||
description: role.description,
|
description: role.description,
|
||||||
color: role.color,
|
color: role.color,
|
||||||
|
target: role.target,
|
||||||
|
condFormula: role.condFormula,
|
||||||
isPublic: role.isPublic,
|
isPublic: role.isPublic,
|
||||||
isAdministrator: role.isAdministrator,
|
isAdministrator: role.isAdministrator,
|
||||||
isModerator: role.isModerator,
|
isModerator: role.isModerator,
|
||||||
|
|
|
@ -1,6 +1,48 @@
|
||||||
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
|
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
|
||||||
import { id } from '../id.js';
|
import { id } from '../id.js';
|
||||||
|
|
||||||
|
type CondFormulaValueAnd = {
|
||||||
|
type: 'and';
|
||||||
|
values: RoleCondFormulaValue[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueOr = {
|
||||||
|
type: 'or';
|
||||||
|
values: RoleCondFormulaValue[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueNot = {
|
||||||
|
type: 'not';
|
||||||
|
value: RoleCondFormulaValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueIsLocal = {
|
||||||
|
type: 'isLocal';
|
||||||
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueIsRemote = {
|
||||||
|
type: 'isRemote';
|
||||||
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueCreatedLessThan = {
|
||||||
|
type: 'createdLessThan';
|
||||||
|
sec: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CondFormulaValueCreatedMoreThan = {
|
||||||
|
type: 'createdMoreThan';
|
||||||
|
sec: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RoleCondFormulaValue =
|
||||||
|
CondFormulaValueAnd |
|
||||||
|
CondFormulaValueOr |
|
||||||
|
CondFormulaValueNot |
|
||||||
|
CondFormulaValueIsLocal |
|
||||||
|
CondFormulaValueIsRemote |
|
||||||
|
CondFormulaValueCreatedLessThan |
|
||||||
|
CondFormulaValueCreatedMoreThan;
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Role {
|
export class Role {
|
||||||
@PrimaryColumn(id())
|
@PrimaryColumn(id())
|
||||||
|
@ -36,6 +78,17 @@ export class Role {
|
||||||
})
|
})
|
||||||
public color: string | null;
|
public color: string | null;
|
||||||
|
|
||||||
|
@Column('enum', {
|
||||||
|
enum: ['manual', 'conditional'],
|
||||||
|
default: 'manual',
|
||||||
|
})
|
||||||
|
public target: 'manual' | 'conditional';
|
||||||
|
|
||||||
|
@Column('jsonb', {
|
||||||
|
default: { },
|
||||||
|
})
|
||||||
|
public condFormula: RoleCondFormulaValue;
|
||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
|
|
@ -19,6 +19,8 @@ export const paramDef = {
|
||||||
name: { type: 'string' },
|
name: { type: 'string' },
|
||||||
description: { type: 'string' },
|
description: { type: 'string' },
|
||||||
color: { type: 'string', nullable: true },
|
color: { type: 'string', nullable: true },
|
||||||
|
target: { type: 'string' },
|
||||||
|
condFormula: { type: 'object' },
|
||||||
isPublic: { type: 'boolean' },
|
isPublic: { type: 'boolean' },
|
||||||
isModerator: { type: 'boolean' },
|
isModerator: { type: 'boolean' },
|
||||||
isAdministrator: { type: 'boolean' },
|
isAdministrator: { type: 'boolean' },
|
||||||
|
@ -31,6 +33,8 @@ export const paramDef = {
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'color',
|
'color',
|
||||||
|
'target',
|
||||||
|
'condFormula',
|
||||||
'isPublic',
|
'isPublic',
|
||||||
'isModerator',
|
'isModerator',
|
||||||
'isAdministrator',
|
'isAdministrator',
|
||||||
|
@ -60,6 +64,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
name: ps.name,
|
name: ps.name,
|
||||||
description: ps.description,
|
description: ps.description,
|
||||||
color: ps.color,
|
color: ps.color,
|
||||||
|
target: ps.target,
|
||||||
|
condFormula: ps.condFormula,
|
||||||
isPublic: ps.isPublic,
|
isPublic: ps.isPublic,
|
||||||
isAdministrator: ps.isAdministrator,
|
isAdministrator: ps.isAdministrator,
|
||||||
isModerator: ps.isModerator,
|
isModerator: ps.isModerator,
|
||||||
|
|
|
@ -27,6 +27,8 @@ export const paramDef = {
|
||||||
name: { type: 'string' },
|
name: { type: 'string' },
|
||||||
description: { type: 'string' },
|
description: { type: 'string' },
|
||||||
color: { type: 'string', nullable: true },
|
color: { type: 'string', nullable: true },
|
||||||
|
target: { type: 'string' },
|
||||||
|
condFormula: { type: 'object' },
|
||||||
isPublic: { type: 'boolean' },
|
isPublic: { type: 'boolean' },
|
||||||
isModerator: { type: 'boolean' },
|
isModerator: { type: 'boolean' },
|
||||||
isAdministrator: { type: 'boolean' },
|
isAdministrator: { type: 'boolean' },
|
||||||
|
@ -40,6 +42,8 @@ export const paramDef = {
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'color',
|
'color',
|
||||||
|
'target',
|
||||||
|
'condFormula',
|
||||||
'isPublic',
|
'isPublic',
|
||||||
'isModerator',
|
'isModerator',
|
||||||
'isAdministrator',
|
'isAdministrator',
|
||||||
|
@ -69,6 +73,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
name: ps.name,
|
name: ps.name,
|
||||||
description: ps.description,
|
description: ps.description,
|
||||||
color: ps.color,
|
color: ps.color,
|
||||||
|
target: ps.target,
|
||||||
|
condFormula: ps.condFormula,
|
||||||
isPublic: ps.isPublic,
|
isPublic: ps.isPublic,
|
||||||
isModerator: ps.isModerator,
|
isModerator: ps.isModerator,
|
||||||
isAdministrator: ps.isAdministrator,
|
isAdministrator: ps.isAdministrator,
|
||||||
|
|
129
packages/frontend/src/pages/admin/RolesEditorFormula.vue
Normal file
129
packages/frontend/src/pages/admin/RolesEditorFormula.vue
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<template>
|
||||||
|
<div :class="$style.root" class="_gaps">
|
||||||
|
<div :class="$style.header">
|
||||||
|
<MkSelect v-model="type" :class="$style.typeSelect">
|
||||||
|
<option value="isLocal">{{ i18n.ts._role._condition.isLocal }}</option>
|
||||||
|
<option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option>
|
||||||
|
<option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option>
|
||||||
|
<option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option>
|
||||||
|
<option value="and">{{ i18n.ts._role._condition.and }}</option>
|
||||||
|
<option value="or">{{ i18n.ts._role._condition.or }}</option>
|
||||||
|
<option value="not">{{ i18n.ts._role._condition.not }}</option>
|
||||||
|
</MkSelect>
|
||||||
|
<button v-if="draggable" class="drag-handle _button" :class="$style.dragHandle">
|
||||||
|
<i class="ti ti-menu-2"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="type === 'and' || type === 'or'" :class="$style.values" class="_gaps">
|
||||||
|
<Sortable v-model="v.values" tag="div" class="_gaps" item-key="id" handle=".drag-handle" :group="{ name: 'roleFormula' }" :animation="150" :swap-threshold="0.5">
|
||||||
|
<template #item="{element}">
|
||||||
|
<div :class="$style.item">
|
||||||
|
<!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 -->
|
||||||
|
<RolesEditorFormula :model-value="element" draggable @update:model-value="updated => valuesItemUpdated(updated)"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Sortable>
|
||||||
|
<MkButton rounded style="margin: 0 auto;" @click="addValue"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="type === 'not'" :class="$style.item">
|
||||||
|
<RolesEditorFormula v-model="v.value"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MkInput v-else-if="type === 'createdLessThan' || type === 'createdMoreThan'" v-model="v.sec" type="number">
|
||||||
|
<template #suffix>sec</template>
|
||||||
|
</MkInput>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, defineAsyncComponent, ref, watch } from 'vue';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import MkInput from '@/components/MkInput.vue';
|
||||||
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import FormSlot from '@/components/form/slot.vue';
|
||||||
|
import * as os from '@/os';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
import { deepClone } from '@/scripts/clone';
|
||||||
|
|
||||||
|
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: 'update:modelValue', value: any): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: any;
|
||||||
|
draggable?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const v = ref(deepClone(props.modelValue));
|
||||||
|
|
||||||
|
watch(() => props.modelValue, () => {
|
||||||
|
if (JSON.stringify(props.modelValue) === JSON.stringify(v.value)) return;
|
||||||
|
v.value = deepClone(props.modelValue);
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
|
watch(v, () => {
|
||||||
|
emit('update:modelValue', v.value);
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
|
const type = computed({
|
||||||
|
get: () => v.value.type,
|
||||||
|
set: (t) => {
|
||||||
|
if (t === 'and') v.value.values = [];
|
||||||
|
if (t === 'or') v.value.values = [];
|
||||||
|
if (t === 'not') v.value.value = { id: uuid(), type: 'isRemote' };
|
||||||
|
if (t === 'createdLessThan') v.value.sec = 86400;
|
||||||
|
if (t === 'createdMoreThan') v.value.sec = 86400;
|
||||||
|
v.value.type = t;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function addValue() {
|
||||||
|
v.value.values.push({ id: uuid(), type: 'isRemote' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function valuesItemUpdated(item) {
|
||||||
|
const i = v.value.values.findIndex(_item => _item.id === item.id);
|
||||||
|
v.value.values[i] = item;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.root {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeSelect {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragHandle {
|
||||||
|
cursor: move;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
border: solid 2px var(--divider);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.values {
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -15,12 +15,26 @@
|
||||||
|
|
||||||
<MkSelect v-model="rolePermission" :readonly="readonly">
|
<MkSelect v-model="rolePermission" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts._role.permission }}</template>
|
<template #label>{{ i18n.ts._role.permission }}</template>
|
||||||
<template #caption><div v-html="i18n.ts._role.descriptionOfType.replaceAll('\n', '<br>')"></div></template>
|
<template #caption><div v-html="i18n.ts._role.descriptionOfPermission.replaceAll('\n', '<br>')"></div></template>
|
||||||
<option value="normal">{{ i18n.ts.normalUser }}</option>
|
<option value="normal">{{ i18n.ts.normalUser }}</option>
|
||||||
<option value="moderator">{{ i18n.ts.moderator }}</option>
|
<option value="moderator">{{ i18n.ts.moderator }}</option>
|
||||||
<option value="administrator">{{ i18n.ts.administrator }}</option>
|
<option value="administrator">{{ i18n.ts.administrator }}</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
|
|
||||||
|
<MkSelect v-model="target" :readonly="readonly">
|
||||||
|
<template #label>{{ i18n.ts._role.assignTarget }}</template>
|
||||||
|
<template #caption><div v-html="i18n.ts._role.descriptionOfAssignTarget.replaceAll('\n', '<br>')"></div></template>
|
||||||
|
<option value="manual">{{ i18n.ts._role.manual }}</option>
|
||||||
|
<option value="conditional">{{ i18n.ts._role.conditional }}</option>
|
||||||
|
</MkSelect>
|
||||||
|
|
||||||
|
<MkFolder v-if="target === 'conditional'" default-open>
|
||||||
|
<template #label>{{ i18n.ts._role.condition }}</template>
|
||||||
|
<div class="_gaps">
|
||||||
|
<RolesEditorFormula v-model="condFormula"/>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
|
||||||
<FormSlot>
|
<FormSlot>
|
||||||
<template #label>{{ i18n.ts._role.options }}</template>
|
<template #label>{{ i18n.ts._role.options }}</template>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
|
@ -107,7 +121,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, watch } from 'vue';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import RolesEditorFormula from './RolesEditorFormula.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
|
@ -134,6 +150,8 @@ let name = $ref(role?.name ?? 'New Role');
|
||||||
let description = $ref(role?.description ?? '');
|
let description = $ref(role?.description ?? '');
|
||||||
let rolePermission = $ref(role?.isAdministrator ? 'administrator' : role?.isModerator ? 'moderator' : 'normal');
|
let rolePermission = $ref(role?.isAdministrator ? 'administrator' : role?.isModerator ? 'moderator' : 'normal');
|
||||||
let color = $ref(role?.color ?? null);
|
let color = $ref(role?.color ?? null);
|
||||||
|
let target = $ref(role?.target ?? 'manual');
|
||||||
|
let condFormula = $ref(role?.condFormula ?? { id: uuid(), type: 'isRemote' });
|
||||||
let isPublic = $ref(role?.isPublic ?? false);
|
let isPublic = $ref(role?.isPublic ?? false);
|
||||||
let canEditMembersByModerator = $ref(role?.canEditMembersByModerator ?? false);
|
let canEditMembersByModerator = $ref(role?.canEditMembersByModerator ?? false);
|
||||||
let options_gtlAvailable_useDefault = $ref(role?.options?.gtlAvailable?.useDefault ?? true);
|
let options_gtlAvailable_useDefault = $ref(role?.options?.gtlAvailable?.useDefault ?? true);
|
||||||
|
@ -147,6 +165,10 @@ let options_driveCapacityMb_value = $ref(role?.options?.driveCapacityMb?.value ?
|
||||||
let options_antennaLimit_useDefault = $ref(role?.options?.antennaLimit?.useDefault ?? true);
|
let options_antennaLimit_useDefault = $ref(role?.options?.antennaLimit?.useDefault ?? true);
|
||||||
let options_antennaLimit_value = $ref(role?.options?.antennaLimit?.value ?? 0);
|
let options_antennaLimit_value = $ref(role?.options?.antennaLimit?.value ?? 0);
|
||||||
|
|
||||||
|
watch($$(condFormula), () => {
|
||||||
|
console.log(condFormula);
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
function getOptions() {
|
function getOptions() {
|
||||||
return {
|
return {
|
||||||
gtlAvailable: { useDefault: options_gtlAvailable_useDefault, value: options_gtlAvailable_value },
|
gtlAvailable: { useDefault: options_gtlAvailable_useDefault, value: options_gtlAvailable_value },
|
||||||
|
@ -165,6 +187,8 @@ async function save() {
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
color: color === '' ? null : color,
|
color: color === '' ? null : color,
|
||||||
|
target,
|
||||||
|
condFormula,
|
||||||
isAdministrator: rolePermission === 'administrator',
|
isAdministrator: rolePermission === 'administrator',
|
||||||
isModerator: rolePermission === 'moderator',
|
isModerator: rolePermission === 'moderator',
|
||||||
isPublic,
|
isPublic,
|
||||||
|
@ -177,6 +201,8 @@ async function save() {
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
color: color === '' ? null : color,
|
color: color === '' ? null : color,
|
||||||
|
target,
|
||||||
|
condFormula,
|
||||||
isAdministrator: rolePermission === 'administrator',
|
isAdministrator: rolePermission === 'administrator',
|
||||||
isModerator: rolePermission === 'moderator',
|
isModerator: rolePermission === 'moderator',
|
||||||
isPublic,
|
isPublic,
|
||||||
|
|
Loading…
Reference in a new issue