This commit is contained in:
tamaina 2023-05-22 01:43:00 +00:00
parent 8adf2701d2
commit 810d065d21
19 changed files with 144 additions and 543 deletions

View file

@ -6,6 +6,7 @@ import { ApiError } from './error.js';
import { endpoints } from 'misskey-js/built/endpoints.js'; import { endpoints } from 'misskey-js/built/endpoints.js';
import type { IEndpointMeta, ResponseOf, SchemaOrUndefined } from 'misskey-js/built/endpoints.types.js'; import type { IEndpointMeta, ResponseOf, SchemaOrUndefined } from 'misskey-js/built/endpoints.types.js';
import type { Endpoints } from 'misskey-js'; import type { Endpoints } from 'misskey-js';
import { WeakSerialized } from 'schema-type';
const ajv = new Ajv({ const ajv = new Ajv({
useDefaults: true, useDefaults: true,
@ -29,7 +30,7 @@ export type Executor<T extends IEndpointMeta, P = SchemaOrUndefined<T['defines']
cleanup?: () => any, cleanup?: () => any,
ip?: string | null, ip?: string | null,
headers?: Record<string, string> | null headers?: Record<string, string> | null
) => Promise<ResponseOf<T, P>>; ) => Promise<WeakSerialized<ResponseOf<T, P>>>;
// ExecutorWrapperの型はあえて緩くしておく // ExecutorWrapperの型はあえて緩くしておく
export type ExecutorWrapper = export type ExecutorWrapper =

View file

@ -7,34 +7,10 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { localUsernameSchema, passwordSchema } from '@/models/entities/User.js'; import { localUsernameSchema, passwordSchema } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
export const meta = {
tags: ['admin'],
res: {
type: 'object',
optional: false, nullable: false,
ref: 'User',
properties: {
token: {
type: 'string',
optional: false, nullable: false,
},
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
username: localUsernameSchema,
password: passwordSchema,
},
required: ['username', 'password'],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/accounts/create'> {
name = 'admin/accounts/create' as const;
constructor( constructor(
@Inject(DI.usersRepository) @Inject(DI.usersRepository)
private usersRepository: UsersRepository, private usersRepository: UsersRepository,
@ -42,7 +18,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private signupService: SignupService, private signupService: SignupService,
) { ) {
super(meta, paramDef, async (ps, _me) => { super(async (ps, _me) => {
const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null; const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null;
const noUsers = (await this.usersRepository.countBy({ const noUsers = (await this.usersRepository.countBy({
host: IsNull(), host: IsNull(),
@ -55,14 +31,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
ignorePreservedUsernames: true, ignorePreservedUsernames: true,
}); });
const res = await this.userEntityService.pack(account, account, { const res = await this.userEntityService.pack<true, true>(account, account, {
detail: true, detail: true,
includeSecrets: true, includeSecrets: true,
}); });
(res as any).token = secret; return { ...res, token: secret };
return res;
}); });
} }
} }

View file

@ -7,24 +7,10 @@ import { UserSuspendService } from '@/core/UserSuspendService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireAdmin: true,
} as const;
export const paramDef = {
type: 'object',
properties: {
userId: { type: 'string', format: 'misskey:id' },
},
required: ['userId'],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/accounts/delete'> {
name = 'admin/accounts/delete' as const;
constructor( constructor(
@Inject(DI.usersRepository) @Inject(DI.usersRepository)
private usersRepository: UsersRepository, private usersRepository: UsersRepository,
@ -34,7 +20,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private globalEventService: GlobalEventService, private globalEventService: GlobalEventService,
private userSuspendService: UserSuspendService, private userSuspendService: UserSuspendService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const user = await this.usersRepository.findOneBy({ id: ps.userId }); const user = await this.usersRepository.findOneBy({ id: ps.userId });
if (user == null) { if (user == null) {

View file

@ -4,38 +4,17 @@ import type { AdsRepository } from '@/models/index.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
} as const;
export const paramDef = {
type: 'object',
properties: {
url: { type: 'string', minLength: 1 },
memo: { type: 'string' },
place: { type: 'string' },
priority: { type: 'string' },
ratio: { type: 'integer' },
expiresAt: { type: 'integer' },
startsAt: { type: 'integer' },
imageUrl: { type: 'string', minLength: 1 },
},
required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt', 'imageUrl'],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/ad/create'> {
name = 'admin/ad/create' as const;
constructor( constructor(
@Inject(DI.adsRepository) @Inject(DI.adsRepository)
private adsRepository: AdsRepository, private adsRepository: AdsRepository,
private idService: IdService, private idService: IdService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
await this.adsRepository.insert({ await this.adsRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
createdAt: new Date(), createdAt: new Date(),

View file

@ -4,40 +4,18 @@ import type { AdsRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
errors: {
noSuchAd: {
message: 'No such ad.',
code: 'NO_SUCH_AD',
id: 'ccac9863-3a03-416e-b899-8a64041118b1',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: { type: 'string', format: 'misskey:id' },
},
required: ['id'],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/ad/delete'> {
name = 'admin/ad/delete' as const;
constructor( constructor(
@Inject(DI.adsRepository) @Inject(DI.adsRepository)
private adsRepository: AdsRepository, private adsRepository: AdsRepository,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const ad = await this.adsRepository.findOneBy({ id: ps.id }); const ad = await this.adsRepository.findOneBy({ id: ps.id });
if (ad == null) throw new ApiError(meta.errors.noSuchAd); if (ad == null) throw new ApiError(this.meta.errors.noSuchAd);
await this.adsRepository.delete(ad.id); await this.adsRepository.delete(ad.id);
}); });

View file

@ -23,14 +23,15 @@ export const paramDef = {
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/ad/list'> {
name = 'admin/ad/list' as const;
constructor( constructor(
@Inject(DI.adsRepository) @Inject(DI.adsRepository)
private adsRepository: AdsRepository, private adsRepository: AdsRepository,
private queryService: QueryService, private queryService: QueryService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId); const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId);
const ads = await query.take(ps.limit).getMany(); const ads = await query.take(ps.limit).getMany();

View file

@ -4,37 +4,6 @@ import type { AdsRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
errors: {
noSuchAd: {
message: 'No such ad.',
code: 'NO_SUCH_AD',
id: 'b7aa1727-1354-47bc-a182-3a9c3973d300',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: { type: 'string', format: 'misskey:id' },
memo: { type: 'string' },
url: { type: 'string', minLength: 1 },
imageUrl: { type: 'string', minLength: 1 },
place: { type: 'string' },
priority: { type: 'string' },
ratio: { type: 'integer' },
expiresAt: { type: 'integer' },
startsAt: { type: 'integer' },
},
required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt'],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<'admin/ad/update'> { export default class extends Endpoint<'admin/ad/update'> {

View file

@ -4,68 +4,17 @@ import type { AnnouncementsRepository } from '@/models/index.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
res: {
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',
},
title: {
type: 'string',
optional: false, nullable: false,
},
text: {
type: 'string',
optional: false, nullable: false,
},
imageUrl: {
type: 'string',
optional: false, nullable: true,
},
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
title: { type: 'string', minLength: 1 },
text: { type: 'string', minLength: 1 },
imageUrl: { type: 'string', nullable: true, minLength: 1 },
},
required: ['title', 'text', 'imageUrl'],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/announcements/create'> {
name = 'admin/announcements/create' as const;
constructor( constructor(
@Inject(DI.announcementsRepository) @Inject(DI.announcementsRepository)
private announcementsRepository: AnnouncementsRepository, private announcementsRepository: AnnouncementsRepository,
private idService: IdService, private idService: IdService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const announcement = await this.announcementsRepository.insert({ const announcement = await this.announcementsRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
createdAt: new Date(), createdAt: new Date(),

View file

@ -4,40 +4,18 @@ import type { AnnouncementsRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
errors: {
noSuchAnnouncement: {
message: 'No such announcement.',
code: 'NO_SUCH_ANNOUNCEMENT',
id: 'ecad8040-a276-4e85-bda9-015a708d291e',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: { type: 'string', format: 'misskey:id' },
},
required: ['id'],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/announcements/delete'> {
name = 'admin/announcements/delete' as const;
constructor( constructor(
@Inject(DI.announcementsRepository) @Inject(DI.announcementsRepository)
private announcementsRepository: AnnouncementsRepository, private announcementsRepository: AnnouncementsRepository,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const announcement = await this.announcementsRepository.findOneBy({ id: ps.id }); const announcement = await this.announcementsRepository.findOneBy({ id: ps.id });
if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); if (announcement == null) throw new ApiError(this.meta.errors.noSuchAnnouncement);
await this.announcementsRepository.delete(announcement.id); await this.announcementsRepository.delete(announcement.id);
}); });

View file

@ -5,69 +5,10 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueryService } from '@/core/QueryService.js'; import { QueryService } from '@/core/QueryService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.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',
},
text: {
type: 'string',
optional: false, nullable: false,
},
title: {
type: 'string',
optional: false, nullable: false,
},
imageUrl: {
type: 'string',
optional: false, nullable: true,
},
reads: {
type: 'number',
optional: false, nullable: false,
},
},
},
},
} 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' },
},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/announcements/list'> {
name = 'admin/announcements/list' as const;
constructor( constructor(
@Inject(DI.announcementsRepository) @Inject(DI.announcementsRepository)
private announcementsRepository: AnnouncementsRepository, private announcementsRepository: AnnouncementsRepository,
@ -77,7 +18,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private queryService: QueryService, private queryService: QueryService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId);
const announcements = await query.take(ps.limit).getMany(); const announcements = await query.take(ps.limit).getMany();

View file

@ -4,43 +4,18 @@ import type { AnnouncementsRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
errors: {
noSuchAnnouncement: {
message: 'No such announcement.',
code: 'NO_SUCH_ANNOUNCEMENT',
id: 'd3aae5a7-6372-4cb4-b61c-f511ffc2d7cc',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: { type: 'string', format: 'misskey:id' },
title: { type: 'string', minLength: 1 },
text: { type: 'string', minLength: 1 },
imageUrl: { type: 'string', nullable: true, minLength: 1 },
},
required: ['id', 'title', 'text', 'imageUrl'],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/announcements/update'> {
name = 'admin/announcements/update' as const;
constructor( constructor(
@Inject(DI.announcementsRepository) @Inject(DI.announcementsRepository)
private announcementsRepository: AnnouncementsRepository, private announcementsRepository: AnnouncementsRepository,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const announcement = await this.announcementsRepository.findOneBy({ id: ps.id }); const announcement = await this.announcementsRepository.findOneBy({ id: ps.id });
if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); if (announcement == null) throw new ApiError(this.meta.errors.noSuchAnnouncement);
await this.announcementsRepository.update(announcement.id, { await this.announcementsRepository.update(announcement.id, {
updatedAt: new Date(), updatedAt: new Date(),

View file

@ -2,26 +2,14 @@ import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
} as const;
export const paramDef = {
type: 'object',
properties: {},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/drive/clean-remote-files'> {
name = 'admin/drive/clean-remote-files' as const;
constructor( constructor(
private queueService: QueueService, private queueService: QueueService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
this.queueService.createCleanRemoteFilesJob(); this.queueService.createCleanRemoteFilesJob();
}); });
} }

View file

@ -5,29 +5,17 @@ import type { DriveFilesRepository } from '@/models/index.js';
import { DriveService } from '@/core/DriveService.js'; import { DriveService } from '@/core/DriveService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
} as const;
export const paramDef = {
type: 'object',
properties: {},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/drive/cleanup'> {
name = 'admin/drive/cleanup' as const;
constructor( constructor(
@Inject(DI.driveFilesRepository) @Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository, private driveFilesRepository: DriveFilesRepository,
private driveService: DriveService, private driveService: DriveService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const files = await this.driveFilesRepository.findBy({ const files = await this.driveFilesRepository.findBy({
userId: IsNull(), userId: IsNull(),
}); });

View file

@ -5,45 +5,10 @@ import { QueryService } from '@/core/QueryService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
ref: 'DriveFile',
},
},
} 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 },
type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) },
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' },
hostname: {
type: 'string',
nullable: true,
default: null,
description: 'The local host is represented with `null`.',
},
},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/drive/files'> {
name = 'admin/drive/files' as const;
constructor( constructor(
@Inject(DI.driveFilesRepository) @Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository, private driveFilesRepository: DriveFilesRepository,
@ -51,7 +16,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private driveFileEntityService: DriveFileEntityService, private driveFileEntityService: DriveFileEntityService,
private queryService: QueryService, private queryService: QueryService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId); const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId);
if (ps.userId) { if (ps.userId) {

View file

@ -5,152 +5,10 @@ import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js'; import { RoleService } from '@/core/RoleService.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
errors: {
noSuchFile: {
message: 'No such file.',
code: 'NO_SUCH_FILE',
id: 'caf3ca38-c6e5-472e-a30c-b05377dcc240',
},
},
res: {
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',
},
userId: {
type: 'string',
optional: false, nullable: true,
format: 'id',
example: 'xxxxxxxxxx',
},
userHost: {
type: 'string',
optional: false, nullable: true,
description: 'The local host is represented with `null`.',
},
md5: {
type: 'string',
optional: false, nullable: false,
format: 'md5',
example: '15eca7fba0480996e2245f5185bf39f2',
},
name: {
type: 'string',
optional: false, nullable: false,
example: 'lenna.jpg',
},
type: {
type: 'string',
optional: false, nullable: false,
example: 'image/jpeg',
},
size: {
type: 'number',
optional: false, nullable: false,
example: 51469,
},
comment: {
type: 'string',
optional: false, nullable: true,
},
blurhash: {
type: 'string',
optional: false, nullable: true,
},
properties: {
type: 'object',
optional: false, nullable: false,
},
storedInternal: {
type: 'boolean',
optional: false, nullable: true,
example: true,
},
url: {
type: 'string',
optional: false, nullable: true,
format: 'url',
},
thumbnailUrl: {
type: 'string',
optional: false, nullable: true,
format: 'url',
},
webpublicUrl: {
type: 'string',
optional: false, nullable: true,
format: 'url',
},
accessKey: {
type: 'string',
optional: false, nullable: true,
},
thumbnailAccessKey: {
type: 'string',
optional: false, nullable: true,
},
webpublicAccessKey: {
type: 'string',
optional: false, nullable: true,
},
uri: {
type: 'string',
optional: false, nullable: true,
},
src: {
type: 'string',
optional: false, nullable: true,
},
folderId: {
type: 'string',
optional: false, nullable: true,
format: 'id',
example: 'xxxxxxxxxx',
},
isSensitive: {
type: 'boolean',
optional: false, nullable: false,
},
isLink: {
type: 'boolean',
optional: false, nullable: false,
},
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
fileId: { type: 'string', format: 'misskey:id' },
url: { type: 'string' },
},
anyOf: [
{ required: ['fileId'] },
{ required: ['url'] },
],
} as const;
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { export default class extends Endpoint<'admin/drive/show-file'> {
name = 'admin/drive/show-file' as const;
constructor( constructor(
@Inject(DI.driveFilesRepository) @Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository, private driveFilesRepository: DriveFilesRepository,
@ -160,7 +18,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private roleService: RoleService, private roleService: RoleService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(async (ps, me) => {
const file = ps.fileId ? await this.driveFilesRepository.findOneBy({ id: ps.fileId }) : await this.driveFilesRepository.findOne({ const file = ps.fileId ? await this.driveFilesRepository.findOneBy({ id: ps.fileId }) : await this.driveFilesRepository.findOne({
where: [{ where: [{
url: ps.url, url: ps.url,
@ -172,7 +30,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
}); });
if (file == null) { if (file == null) {
throw new ApiError(meta.errors.noSuchFile); throw new ApiError(this.meta.errors.noSuchFile);
} }
const owner = file.userId ? await this.usersRepository.findOneByOrFail({ const owner = file.userId ? await this.usersRepository.findOneByOrFail({

View file

@ -1,4 +1,5 @@
import type { Endpoints, SchemaOrUndefined, IEndpointMeta, ResponseOf } from './endpoints.types.js'; import type { Endpoints, SchemaOrUndefined, IEndpointMeta, ResponseOf } from './endpoints.types.js';
import type { Serialized, WeakSerialized } from 'schema-type';
const MK_API_ERROR = Symbol(); const MK_API_ERROR = Symbol();
@ -42,9 +43,10 @@ export class APIClient {
this.fetch = opts.fetch ?? ((...args) => fetch(...args)); this.fetch = opts.fetch ?? ((...args) => fetch(...args));
} }
// WeakSerialized<P>で推論が効くかは知らない
public request<E extends keyof Endpoints, P extends SchemaOrUndefined<M['defines'][number]['req']>, M extends IEndpointMeta = Endpoints[E], R = ResponseOf<M, P>>( public request<E extends keyof Endpoints, P extends SchemaOrUndefined<M['defines'][number]['req']>, M extends IEndpointMeta = Endpoints[E], R = ResponseOf<M, P>>(
endpoint: E, params: P, credential?: string | null | undefined, endpoint: E, params: WeakSerialized<P>, credential?: string | null | undefined,
): Promise<R> ): Promise<Serialized<R>>
{ {
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
this.fetch(`${this.origin}/api/${endpoint}`, { this.fetch(`${this.origin}/api/${endpoint}`, {

View file

@ -269,7 +269,7 @@ export const endpoints = {
res: undefined, res: undefined,
}], }],
}, },
"admin/drive/clenaup": { "admin/drive/cleanup": {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
@ -354,24 +354,94 @@ export const endpoints = {
], ],
}, },
res: { res: {
allOf: [{ type: 'object',
$ref: 'https://misskey-hub.net/api/schemas/DriveFile', properties: {
}, { id: { $ref: 'https://misskey-hub.net/api/schemas/Id' },
type: 'object', createdAt: { type: 'string', format: 'date-time' },
properties: { userId: {
requestIp: { oneOf: [
type: ['string', 'null'], { $ref: 'https://misskey-hub.net/api/schemas/Id' },
}, { type: 'null' },
requestHeaders: { ]
oneOf: [{
type: 'object',
}, {
type: 'null',
}],
}
}, },
required: ['requestIp', 'requestHeaders'], userHost: { type: ['string', 'null'] },
}], md5: { type: 'string', format: 'md5', examples: '1bc29b36f623ba82aaf6724fd3b16718' },
name: { type: 'string', examples: 'lenna.jpg' },
type: { type: 'string', examples: 'image/jpeg' },
size: { type: 'number', examples: 51469 },
comment: { type: ['string', 'null'] },
blurhash: { type: ['string', 'null'] },
properties: { type: 'object' },
storedInternal: { type: ['boolean', 'null'], examples: true },
url: {
oneOf: [
{ type: 'string', format: 'url' },
{ type: 'null' },
],
},
thumbnailUrl: {
oneOf: [
{ type: 'string', format: 'url' },
{ type: 'null' },
],
},
webpublicUrl: {
oneOf: [
{ type: 'string', format: 'url' },
{ type: 'null' },
],
},
accessKey: { type: ['string', 'null'] },
thumbnailAccessKey: { type: ['string', 'null'] },
webpublicAccessKey: { type: ['string', 'null'] },
uri: { type: ['string', 'null'] },
src: { type: ['string', 'null'] },
folderId: {
oneOf: [
{ $ref: 'https://misskey-hub.net/api/schemas/Id' },
{ type: 'null' },
]
},
isSensitive: { type: 'boolean' },
isLink: { type: 'boolean' },
requestIp: {
type: ['string', 'null'],
},
requestHeaders: {
oneOf: [{
type: 'object',
}, {
type: 'null',
}],
}
},
required: [
'id',
'createdAt',
'userId',
'userHost',
'md5',
'name',
'type',
'size',
'comment',
'blurhash',
'properties',
'storedInternal',
'url',
'thumbnailUrl',
'webpublicUrl',
'accessKey',
'thumbnailAccessKey',
'webpublicAccessKey',
'uri',
'src',
'folderId',
'isSensitive',
'isLink',
'requestIp',
'requestHeaders',
],
}, },
}], }],
}, },

View file

@ -40,7 +40,6 @@ export const packedAdSchema = {
'expiresAt', 'expiresAt',
'startsAt', 'startsAt',
'place', 'place',
'property',
'ratio', 'ratio',
'imageUrl', 'imageUrl',
'memo', 'memo',

View file

@ -626,7 +626,7 @@ importers:
version: 29.5.0 version: 29.5.0
schema-type: schema-type:
specifier: github:misskey-dev/schema-type specifier: github:misskey-dev/schema-type
version: github.com/misskey-dev/schema-type/e24efd7bba40f638b3b687298873fa56de56aab3(typescript@5.0.4) version: github.com/misskey-dev/schema-type/3e1f60a3486ad51a01912bd7bb092d5bcf47473e(typescript@5.0.4)
packages/frontend: packages/frontend:
dependencies: dependencies:
@ -1026,7 +1026,7 @@ importers:
version: 4.4.0 version: 4.4.0
schema-type: schema-type:
specifier: github:misskey-dev/schema-type specifier: github:misskey-dev/schema-type
version: github.com/misskey-dev/schema-type/e24efd7bba40f638b3b687298873fa56de56aab3(typescript@5.0.4) version: github.com/misskey-dev/schema-type/3e1f60a3486ad51a01912bd7bb092d5bcf47473e(typescript@5.0.4)
ts-essentials: ts-essentials:
specifier: ^9.3.2 specifier: ^9.3.2
version: 9.3.2(typescript@5.0.4) version: 9.3.2(typescript@5.0.4)
@ -20455,9 +20455,9 @@ packages:
version: 0.0.0 version: 0.0.0
dev: false dev: false
github.com/misskey-dev/schema-type/e24efd7bba40f638b3b687298873fa56de56aab3(typescript@5.0.4): github.com/misskey-dev/schema-type/3e1f60a3486ad51a01912bd7bb092d5bcf47473e(typescript@5.0.4):
resolution: {tarball: https://codeload.github.com/misskey-dev/schema-type/tar.gz/e24efd7bba40f638b3b687298873fa56de56aab3} resolution: {tarball: https://codeload.github.com/misskey-dev/schema-type/tar.gz/3e1f60a3486ad51a01912bd7bb092d5bcf47473e}
id: github.com/misskey-dev/schema-type/e24efd7bba40f638b3b687298873fa56de56aab3 id: github.com/misskey-dev/schema-type/3e1f60a3486ad51a01912bd7bb092d5bcf47473e
name: schema-type name: schema-type
version: 1.0.0 version: 1.0.0
dependencies: dependencies: