From 6c975275f82c79eed2c7757d55283c95d23ca5b8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 11 Jan 2021 20:38:34 +0900 Subject: Registry (#7073) * wip * wip * wip * wip * wip * Update registry.value.vue * wip * wip * wip * wip * typo --- src/server/api/endpoints/i/notifications.ts | 2 +- src/server/api/endpoints/i/registry/get-all.ts | 33 ++++++++++++ src/server/api/endpoints/i/registry/get-detail.ts | 48 +++++++++++++++++ src/server/api/endpoints/i/registry/get.ts | 45 ++++++++++++++++ .../api/endpoints/i/registry/keys-with-type.ts | 41 +++++++++++++++ src/server/api/endpoints/i/registry/keys.ts | 28 ++++++++++ src/server/api/endpoints/i/registry/remove.ts | 45 ++++++++++++++++ src/server/api/endpoints/i/registry/scopes.ts | 30 +++++++++++ src/server/api/endpoints/i/registry/set.ts | 61 ++++++++++++++++++++++ .../api/endpoints/i/update-client-setting.ts | 40 -------------- 10 files changed, 332 insertions(+), 41 deletions(-) create mode 100644 src/server/api/endpoints/i/registry/get-all.ts create mode 100644 src/server/api/endpoints/i/registry/get-detail.ts create mode 100644 src/server/api/endpoints/i/registry/get.ts create mode 100644 src/server/api/endpoints/i/registry/keys-with-type.ts create mode 100644 src/server/api/endpoints/i/registry/keys.ts create mode 100644 src/server/api/endpoints/i/registry/remove.ts create mode 100644 src/server/api/endpoints/i/registry/scopes.ts create mode 100644 src/server/api/endpoints/i/registry/set.ts delete mode 100644 src/server/api/endpoints/i/update-client-setting.ts (limited to 'src/server/api/endpoints/i') diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index fd355dab83..0e09bc73fd 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -80,7 +80,7 @@ export default define(meta, async (ps, user) => { .where('muting.muterId = :muterId', { muterId: user.id }); const suspendedQuery = Users.createQueryBuilder('users') - .select('id') + .select('users.id') .where('users.isSuspended = TRUE'); const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) diff --git a/src/server/api/endpoints/i/registry/get-all.ts b/src/server/api/endpoints/i/registry/get-all.ts new file mode 100644 index 0000000000..ce8653f22b --- /dev/null +++ b/src/server/api/endpoints/i/registry/get-all.ts @@ -0,0 +1,33 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { RegistryItems } from '../../../../../models'; + +export const meta = { + requireCredential: true as const, + + secure: true, + + params: { + scope: { + validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), + default: [], + }, + } +}; + +export default define(meta, async (ps, user) => { + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const items = await query.getMany(); + + const res = {} as Record; + + for (const item of items) { + res[item.key] = item.value; + } + + return res; +}); diff --git a/src/server/api/endpoints/i/registry/get-detail.ts b/src/server/api/endpoints/i/registry/get-detail.ts new file mode 100644 index 0000000000..441833d3d7 --- /dev/null +++ b/src/server/api/endpoints/i/registry/get-detail.ts @@ -0,0 +1,48 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { RegistryItems } from '../../../../../models'; +import { ApiError } from '../../../error'; + +export const meta = { + requireCredential: true as const, + + secure: true, + + params: { + key: { + validator: $.str + }, + + scope: { + validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), + default: [], + }, + }, + + errors: { + noSuchKey: { + message: 'No such key.', + code: 'NO_SUCH_KEY', + id: '97a1e8e7-c0f7-47d2-957a-92e61256e01a' + }, + }, +}; + +export default define(meta, async (ps, user) => { + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const item = await query.getOne(); + + if (item == null) { + throw new ApiError(meta.errors.noSuchKey); + } + + return { + updatedAt: item.updatedAt, + value: item.value, + }; +}); diff --git a/src/server/api/endpoints/i/registry/get.ts b/src/server/api/endpoints/i/registry/get.ts new file mode 100644 index 0000000000..275e660cb6 --- /dev/null +++ b/src/server/api/endpoints/i/registry/get.ts @@ -0,0 +1,45 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { RegistryItems } from '../../../../../models'; +import { ApiError } from '../../../error'; + +export const meta = { + requireCredential: true as const, + + secure: true, + + params: { + key: { + validator: $.str + }, + + scope: { + validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), + default: [], + }, + }, + + errors: { + noSuchKey: { + message: 'No such key.', + code: 'NO_SUCH_KEY', + id: 'ac3ed68a-62f0-422b-a7bc-d5e09e8f6a6a' + }, + }, +}; + +export default define(meta, async (ps, user) => { + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const item = await query.getOne(); + + if (item == null) { + throw new ApiError(meta.errors.noSuchKey); + } + + return item.value; +}); diff --git a/src/server/api/endpoints/i/registry/keys-with-type.ts b/src/server/api/endpoints/i/registry/keys-with-type.ts new file mode 100644 index 0000000000..06d77acbeb --- /dev/null +++ b/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -0,0 +1,41 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { RegistryItems } from '../../../../../models'; + +export const meta = { + requireCredential: true as const, + + secure: true, + + params: { + scope: { + validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), + default: [], + }, + } +}; + +export default define(meta, async (ps, user) => { + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const items = await query.getMany(); + + const res = {} as Record; + + for (const item of items) { + const type = typeof item.value; + res[item.key] = + item.value === null ? 'null' : + Array.isArray(item.value) ? 'array' : + type === 'number' ? 'number' : + type === 'string' ? 'string' : + type === 'boolean' ? 'boolean' : + type === 'object' ? 'object' : + null as never; + } + + return res; +}); diff --git a/src/server/api/endpoints/i/registry/keys.ts b/src/server/api/endpoints/i/registry/keys.ts new file mode 100644 index 0000000000..e4dd5044b4 --- /dev/null +++ b/src/server/api/endpoints/i/registry/keys.ts @@ -0,0 +1,28 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { RegistryItems } from '../../../../../models'; + +export const meta = { + requireCredential: true as const, + + secure: true, + + params: { + scope: { + validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), + default: [], + }, + } +}; + +export default define(meta, async (ps, user) => { + const query = RegistryItems.createQueryBuilder('item') + .select('item.key') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const items = await query.getMany(); + + return items.map(x => x.key); +}); diff --git a/src/server/api/endpoints/i/registry/remove.ts b/src/server/api/endpoints/i/registry/remove.ts new file mode 100644 index 0000000000..e73444efd2 --- /dev/null +++ b/src/server/api/endpoints/i/registry/remove.ts @@ -0,0 +1,45 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { RegistryItems } from '../../../../../models'; +import { ApiError } from '../../../error'; + +export const meta = { + requireCredential: true as const, + + secure: true, + + params: { + key: { + validator: $.str + }, + + scope: { + validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), + default: [], + }, + }, + + errors: { + noSuchKey: { + message: 'No such key.', + code: 'NO_SUCH_KEY', + id: '1fac4e8a-a6cd-4e39-a4a5-3a7e11f1b019' + }, + }, +}; + +export default define(meta, async (ps, user) => { + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const item = await query.getOne(); + + if (item == null) { + throw new ApiError(meta.errors.noSuchKey); + } + + RegistryItems.remove(item); +}); diff --git a/src/server/api/endpoints/i/registry/scopes.ts b/src/server/api/endpoints/i/registry/scopes.ts new file mode 100644 index 0000000000..8b0e1a7fd8 --- /dev/null +++ b/src/server/api/endpoints/i/registry/scopes.ts @@ -0,0 +1,30 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { RegistryItems } from '../../../../../models'; + +export const meta = { + requireCredential: true as const, + + secure: true, + + params: { + } +}; + +export default define(meta, async (ps, user) => { + const query = RegistryItems.createQueryBuilder('item') + .select('item.scope') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }); + + const items = await query.getMany(); + + const res = [] as string[][]; + + for (const item of items) { + if (res.some(scope => scope.join('.') === item.scope.join('.'))) continue; + res.push(item.scope); + } + + return res; +}); diff --git a/src/server/api/endpoints/i/registry/set.ts b/src/server/api/endpoints/i/registry/set.ts new file mode 100644 index 0000000000..c732cfc8f5 --- /dev/null +++ b/src/server/api/endpoints/i/registry/set.ts @@ -0,0 +1,61 @@ +import $ from 'cafy'; +import { publishMainStream } from '../../../../../services/stream'; +import define from '../../../define'; +import { RegistryItems } from '../../../../../models'; +import { genId } from '../../../../../misc/gen-id'; + +export const meta = { + requireCredential: true as const, + + secure: true, + + params: { + key: { + validator: $.str.min(1) + }, + + value: { + validator: $.nullable.any + }, + + scope: { + validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), + default: [], + }, + } +}; + +export default define(meta, async (ps, user) => { + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const existingItem = await query.getOne(); + + if (existingItem) { + await RegistryItems.update(existingItem.id, { + updatedAt: new Date(), + value: ps.value + }); + } else { + await RegistryItems.insert({ + id: genId(), + createdAt: new Date(), + updatedAt: new Date(), + userId: user.id, + domain: null, + scope: ps.scope, + key: ps.key, + value: ps.value + }); + } + + // TODO: サードパーティアプリが傍受出来てしまうのでどうにかする + publishMainStream(user.id, 'registryUpdated', { + scope: ps.scope, + key: ps.key, + value: ps.value + }); +}); diff --git a/src/server/api/endpoints/i/update-client-setting.ts b/src/server/api/endpoints/i/update-client-setting.ts deleted file mode 100644 index 5143d3d9ba..0000000000 --- a/src/server/api/endpoints/i/update-client-setting.ts +++ /dev/null @@ -1,40 +0,0 @@ -import $ from 'cafy'; -import { publishMainStream } from '../../../../services/stream'; -import define from '../../define'; -import { UserProfiles } from '../../../../models'; -import { ensure } from '../../../../prelude/ensure'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - name: { - validator: $.str.match(/^[a-zA-Z]+$/) - }, - - value: { - validator: $.nullable.any - } - } -}; - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); - - await UserProfiles.createQueryBuilder().update() - .set({ - clientData: Object.assign(profile.clientData, { - [ps.name]: ps.value - }), - }) - .where('userId = :id', { id: user.id }) - .execute(); - - // Publish event - publishMainStream(user.id, 'clientSettingUpdated', { - key: ps.name, - value: ps.value - }); -}); -- cgit v1.2.3-freya