summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/api/endpoints
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-10-21 18:38:07 +0900
committerGitHub <noreply@github.com>2023-10-21 18:38:07 +0900
commit2c0a139da60ddf33e82353acbb985230c71c78da (patch)
treeb12d2762b332b7c885c5b210954053869618b62a /packages/backend/src/server/api/endpoints
parentUpdate CHANGELOG.md (diff)
downloadsharkey-2c0a139da60ddf33e82353acbb985230c71c78da.tar.gz
sharkey-2c0a139da60ddf33e82353acbb985230c71c78da.tar.bz2
sharkey-2c0a139da60ddf33e82353acbb985230c71c78da.zip
feat: Avatar decoration (#12096)
* wip * Update ja-JP.yml * Update profile.vue * .js * Update home.test.ts
Diffstat (limited to 'packages/backend/src/server/api/endpoints')
-rw-r--r--packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts44
-rw-r--r--packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts39
-rw-r--r--packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts101
-rw-r--r--packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts50
-rw-r--r--packages/backend/src/server/api/endpoints/get-avatar-decorations.ts79
-rw-r--r--packages/backend/src/server/api/endpoints/i/update.ts16
6 files changed, 329 insertions, 0 deletions
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
new file mode 100644
index 0000000000..c1869b141a
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
@@ -0,0 +1,44 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
+
+export const meta = {
+ tags: ['admin'],
+
+ requireCredential: true,
+ requireModerator: true,
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ name: { type: 'string', minLength: 1 },
+ description: { type: 'string' },
+ url: { type: 'string', minLength: 1 },
+ roleIdsThatCanBeUsedThisDecoration: { type: 'array', items: {
+ type: 'string',
+ } },
+ },
+ required: ['name', 'description', 'url'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ private avatarDecorationService: AvatarDecorationService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ await this.avatarDecorationService.create({
+ name: ps.name,
+ description: ps.description,
+ url: ps.url,
+ roleIdsThatCanBeUsedThisDecoration: ps.roleIdsThatCanBeUsedThisDecoration,
+ }, me);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts
new file mode 100644
index 0000000000..5aba24b426
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts
@@ -0,0 +1,39 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { DI } from '@/di-symbols.js';
+import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
+import { ApiError } from '../../../error.js';
+
+export const meta = {
+ tags: ['admin'],
+
+ requireCredential: true,
+ requireModerator: true,
+
+ errors: {
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ id: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['id'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ private avatarDecorationService: AvatarDecorationService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ await this.avatarDecorationService.delete(ps.id, me);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
new file mode 100644
index 0000000000..9a32a59081
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
@@ -0,0 +1,101 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import type { AnnouncementsRepository, AnnouncementReadsRepository } from '@/models/_.js';
+import type { MiAnnouncement } from '@/models/Announcement.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { QueryService } from '@/core/QueryService.js';
+import { DI } from '@/di-symbols.js';
+import { IdService } from '@/core/IdService.js';
+import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
+
+export const meta = {
+ tags: ['admin'],
+
+ requireCredential: true,
+ requireModerator: true,
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ id: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ example: 'xxxxxxxxxx',
+ },
+ createdAt: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'date-time',
+ },
+ updatedAt: {
+ type: 'string',
+ optional: false, nullable: true,
+ format: 'date-time',
+ },
+ name: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ description: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ url: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ roleIdsThatCanBeUsedThisDecoration: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ },
+ },
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+ sinceId: { type: 'string', format: 'misskey:id' },
+ untilId: { type: 'string', format: 'misskey:id' },
+ userId: { type: 'string', format: 'misskey:id', nullable: true },
+ },
+ required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ private avatarDecorationService: AvatarDecorationService,
+ private idService: IdService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const avatarDecorations = await this.avatarDecorationService.getAll(true);
+
+ return avatarDecorations.map(avatarDecoration => ({
+ id: avatarDecoration.id,
+ createdAt: this.idService.parse(avatarDecoration.id).date.toISOString(),
+ updatedAt: avatarDecoration.updatedAt?.toISOString() ?? null,
+ name: avatarDecoration.name,
+ description: avatarDecoration.description,
+ url: avatarDecoration.url,
+ roleIdsThatCanBeUsedThisDecoration: avatarDecoration.roleIdsThatCanBeUsedThisDecoration,
+ }));
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts
new file mode 100644
index 0000000000..564014a3df
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts
@@ -0,0 +1,50 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { DI } from '@/di-symbols.js';
+import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
+import { ApiError } from '../../../error.js';
+
+export const meta = {
+ tags: ['admin'],
+
+ requireCredential: true,
+ requireModerator: true,
+
+ errors: {
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ id: { type: 'string', format: 'misskey:id' },
+ name: { type: 'string', minLength: 1 },
+ description: { type: 'string' },
+ url: { type: 'string', minLength: 1 },
+ roleIdsThatCanBeUsedThisDecoration: { type: 'array', items: {
+ type: 'string',
+ } },
+ },
+ required: ['id'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ private avatarDecorationService: AvatarDecorationService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ await this.avatarDecorationService.update(ps.id, {
+ name: ps.name,
+ description: ps.description,
+ url: ps.url,
+ roleIdsThatCanBeUsedThisDecoration: ps.roleIdsThatCanBeUsedThisDecoration,
+ }, me);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts
new file mode 100644
index 0000000000..ec602a0dc5
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts
@@ -0,0 +1,79 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { IsNull } from 'typeorm';
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { DI } from '@/di-symbols.js';
+import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
+
+export const meta = {
+ tags: ['users'],
+
+ requireCredential: false,
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ id: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ example: 'xxxxxxxxxx',
+ },
+ name: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ description: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ url: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ roleIdsThatCanBeUsedThisDecoration: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ },
+ },
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {},
+ required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ private avatarDecorationService: AvatarDecorationService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const decorations = await this.avatarDecorationService.getAll(true);
+
+ return decorations.map(decoration => ({
+ id: decoration.id,
+ name: decoration.name,
+ description: decoration.description,
+ url: decoration.url,
+ roleIdsThatCanBeUsedThisDecoration: decoration.roleIdsThatCanBeUsedThisDecoration,
+ }));
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index 431bb4c60a..f1837e7082 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -32,6 +32,7 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j
import { HttpRequestService } from '@/core/HttpRequestService.js';
import type { Config } from '@/config.js';
import { safeForSql } from '@/misc/safe-for-sql.js';
+import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
import { ApiLoggerService } from '../../ApiLoggerService.js';
import { ApiError } from '../../error.js';
@@ -131,6 +132,9 @@ export const paramDef = {
birthday: { ...birthdaySchema, nullable: true },
lang: { type: 'string', enum: [null, ...Object.keys(langmap)] as string[], nullable: true },
avatarId: { type: 'string', format: 'misskey:id', nullable: true },
+ avatarDecorations: { type: 'array', maxItems: 1, items: {
+ type: 'string',
+ } },
bannerId: { type: 'string', format: 'misskey:id', nullable: true },
fields: {
type: 'array',
@@ -207,6 +211,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private roleService: RoleService,
private cacheService: CacheService,
private httpRequestService: HttpRequestService,
+ private avatarDecorationService: AvatarDecorationService,
) {
super(meta, paramDef, async (ps, _user, token) => {
const user = await this.usersRepository.findOneByOrFail({ id: _user.id }) as MiLocalUser;
@@ -296,6 +301,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
updates.bannerBlurhash = null;
}
+ if (ps.avatarDecorations) {
+ const decorations = await this.avatarDecorationService.getAll(true);
+ const myRoles = await this.roleService.getUserRoles(user.id);
+ const allRoles = await this.roleService.getRoles();
+ const decorationIds = decorations
+ .filter(d => d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id)))
+ .map(d => d.id);
+
+ updates.avatarDecorations = ps.avatarDecorations.filter(id => decorationIds.includes(id));
+ }
+
if (ps.pinnedPageId) {
const page = await this.pagesRepository.findOneBy({ id: ps.pinnedPageId });