merge: upstream
This commit is contained in:
commit
5db583a3eb
701 changed files with 50809 additions and 13660 deletions
|
@ -138,7 +138,7 @@ export class ActivityPubServerService {
|
|||
return;
|
||||
}
|
||||
|
||||
const algo = match[1];
|
||||
const algo = match[1].toUpperCase();
|
||||
const digestValue = match[2];
|
||||
|
||||
if (algo !== 'SHA-256') {
|
||||
|
@ -195,11 +195,11 @@ export class ActivityPubServerService {
|
|||
//#region Check ff visibility
|
||||
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
|
||||
|
||||
if (profile.ffVisibility === 'private') {
|
||||
if (profile.followersVisibility === 'private') {
|
||||
reply.code(403);
|
||||
reply.header('Cache-Control', 'public, max-age=30');
|
||||
return;
|
||||
} else if (profile.ffVisibility === 'followers') {
|
||||
} else if (profile.followersVisibility === 'followers') {
|
||||
reply.code(403);
|
||||
reply.header('Cache-Control', 'public, max-age=30');
|
||||
return;
|
||||
|
@ -287,11 +287,11 @@ export class ActivityPubServerService {
|
|||
//#region Check ff visibility
|
||||
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
|
||||
|
||||
if (profile.ffVisibility === 'private') {
|
||||
if (profile.followingVisibility === 'private') {
|
||||
reply.code(403);
|
||||
reply.header('Cache-Control', 'public, max-age=30');
|
||||
return;
|
||||
} else if (profile.ffVisibility === 'followers') {
|
||||
} else if (profile.followingVisibility === 'followers') {
|
||||
reply.code(403);
|
||||
reply.header('Cache-Control', 'public, max-age=30');
|
||||
return;
|
||||
|
@ -370,8 +370,9 @@ export class ActivityPubServerService {
|
|||
order: { id: 'DESC' },
|
||||
});
|
||||
|
||||
const pinnedNotes = await Promise.all(pinings.map(pining =>
|
||||
this.notesRepository.findOneByOrFail({ id: pining.noteId })));
|
||||
const pinnedNotes = (await Promise.all(pinings.map(pining =>
|
||||
this.notesRepository.findOneByOrFail({ id: pining.noteId }))))
|
||||
.filter(note => !note.localOnly && ['public', 'home'].includes(note.visibility));
|
||||
|
||||
const renderedNotes = await Promise.all(pinnedNotes.map(note => this.apRendererService.renderNote(note)));
|
||||
|
||||
|
@ -492,8 +493,7 @@ export class ActivityPubServerService {
|
|||
|
||||
@bindThis
|
||||
public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
|
||||
// addConstraintStrategy の型定義がおかしいため
|
||||
(fastify.addConstraintStrategy as any)({
|
||||
fastify.addConstraintStrategy({
|
||||
name: 'apOrHtml',
|
||||
storage() {
|
||||
const store = {} as any;
|
||||
|
|
|
@ -61,7 +61,9 @@ export class FileServerService {
|
|||
public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
|
||||
fastify.addHook('onRequest', (request, reply, done) => {
|
||||
reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\'');
|
||||
reply.header('Access-Control-Allow-Origin', '*');
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
reply.header('Access-Control-Allow-Origin', '*');
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
|
|
|
@ -96,6 +96,11 @@ export class NodeinfoServerService {
|
|||
metadata: {
|
||||
nodeName: meta.name,
|
||||
nodeDescription: meta.description,
|
||||
nodeAdmins: [{
|
||||
name: meta.maintainerName,
|
||||
email: meta.maintainerEmail,
|
||||
}],
|
||||
// deprecated
|
||||
maintainer: {
|
||||
name: meta.maintainerName,
|
||||
email: meta.maintainerEmail,
|
||||
|
|
|
@ -122,8 +122,8 @@ export class ServerService implements OnApplicationShutdown {
|
|||
return;
|
||||
}
|
||||
|
||||
const name = path.split('@')[0].replace('.webp', '');
|
||||
const host = path.split('@')[1]?.replace('.webp', '');
|
||||
const name = path.split('@')[0].replace(/\.webp$/i, '');
|
||||
const host = path.split('@')[1]?.replace(/\.webp$/i, '');
|
||||
|
||||
const emoji = await this.emojisRepository.findOneBy({
|
||||
// `@.` is the spec of ReactionService.decodeReaction
|
||||
|
|
|
@ -24,6 +24,8 @@ import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-d
|
|||
import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
|
||||
import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
|
||||
import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
|
||||
import * as ep___admin_unsetUserAvatar from './endpoints/admin/unset-user-avatar.js';
|
||||
import * as ep___admin_unsetUserBanner from './endpoints/admin/unset-user-banner.js';
|
||||
import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js';
|
||||
import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js';
|
||||
import * as ep___admin_drive_files from './endpoints/admin/drive/files.js';
|
||||
|
@ -396,6 +398,8 @@ const $admin_avatarDecorations_delete: Provider = { provide: 'ep:admin/avatar-de
|
|||
const $admin_avatarDecorations_list: Provider = { provide: 'ep:admin/avatar-decorations/list', useClass: ep___admin_avatarDecorations_list.default };
|
||||
const $admin_avatarDecorations_update: Provider = { provide: 'ep:admin/avatar-decorations/update', useClass: ep___admin_avatarDecorations_update.default };
|
||||
const $admin_deleteAllFilesOfAUser: Provider = { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default };
|
||||
const $admin_unsetUserAvatar: Provider = { provide: 'ep:admin/unset-user-avatar', useClass: ep___admin_unsetUserAvatar.default };
|
||||
const $admin_unsetUserBanner: Provider = { provide: 'ep:admin/unset-user-banner', useClass: ep___admin_unsetUserBanner.default };
|
||||
const $admin_drive_cleanRemoteFiles: Provider = { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default };
|
||||
const $admin_drive_cleanup: Provider = { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default };
|
||||
const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default };
|
||||
|
@ -772,6 +776,8 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de
|
|||
$admin_avatarDecorations_list,
|
||||
$admin_avatarDecorations_update,
|
||||
$admin_deleteAllFilesOfAUser,
|
||||
$admin_unsetUserAvatar,
|
||||
$admin_unsetUserBanner,
|
||||
$admin_drive_cleanRemoteFiles,
|
||||
$admin_drive_cleanup,
|
||||
$admin_drive_files,
|
||||
|
@ -1142,6 +1148,8 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de
|
|||
$admin_avatarDecorations_list,
|
||||
$admin_avatarDecorations_update,
|
||||
$admin_deleteAllFilesOfAUser,
|
||||
$admin_unsetUserAvatar,
|
||||
$admin_unsetUserBanner,
|
||||
$admin_drive_cleanRemoteFiles,
|
||||
$admin_drive_cleanup,
|
||||
$admin_drive_files,
|
||||
|
|
|
@ -139,7 +139,7 @@ export class SignupApiService {
|
|||
code: invitationCode,
|
||||
});
|
||||
|
||||
if (ticket == null) {
|
||||
if (ticket == null || ticket.usedById != null) {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-d
|
|||
import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
|
||||
import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
|
||||
import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
|
||||
import * as ep___admin_unsetUserAvatar from './endpoints/admin/unset-user-avatar.js';
|
||||
import * as ep___admin_unsetUserBanner from './endpoints/admin/unset-user-banner.js';
|
||||
import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js';
|
||||
import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js';
|
||||
import * as ep___admin_drive_files from './endpoints/admin/drive/files.js';
|
||||
|
@ -394,6 +396,8 @@ const eps = [
|
|||
['admin/avatar-decorations/list', ep___admin_avatarDecorations_list],
|
||||
['admin/avatar-decorations/update', ep___admin_avatarDecorations_update],
|
||||
['admin/delete-all-files-of-a-user', ep___admin_deleteAllFilesOfAUser],
|
||||
['admin/unset-user-avatar', ep___admin_unsetUserAvatar],
|
||||
['admin/unset-user-banner', ep___admin_unsetUserBanner],
|
||||
['admin/drive/clean-remote-files', ep___admin_drive_cleanRemoteFiles],
|
||||
['admin/drive/cleanup', ep___admin_drive_cleanup],
|
||||
['admin/drive/files', ep___admin_drive_files],
|
||||
|
|
|
@ -13,6 +13,8 @@ import { AbuseUserReportEntityService } from '@/core/entities/AbuseUserReportEnt
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ import { DI } from '@/di-symbols.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
|
|
|
@ -14,6 +14,8 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
} as const;
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ApiError } from '@/server/api/error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
|
@ -23,6 +25,11 @@ export const meta = {
|
|||
id: 'cb865949-8af5-4062-a88c-ef55e8786d1d',
|
||||
},
|
||||
},
|
||||
res: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
ref: 'User',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -13,8 +13,16 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: 'object',
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: 'Ad',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@ -61,7 +69,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
ad: ad,
|
||||
});
|
||||
|
||||
return ad;
|
||||
return {
|
||||
id: ad.id,
|
||||
expiresAt: ad.expiresAt.toISOString(),
|
||||
startsAt: ad.startsAt.toISOString(),
|
||||
dayOfWeek: ad.dayOfWeek,
|
||||
url: ad.url,
|
||||
imageUrl: ad.imageUrl,
|
||||
priority: ad.priority,
|
||||
ratio: ad.ratio,
|
||||
place: ad.place,
|
||||
memo: ad.memo,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -12,8 +12,21 @@ import { DI } from '@/di-symbols.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: 'array',
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: 'object',
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: 'Ad',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@ -22,7 +35,7 @@ export const paramDef = {
|
|||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
publishing: { type: 'boolean', default: false },
|
||||
publishing: { type: 'boolean', default: null, nullable: true },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
@ -37,12 +50,25 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId);
|
||||
if (ps.publishing) {
|
||||
if (ps.publishing === true) {
|
||||
query.andWhere('ad.expiresAt > :now', { now: new Date() }).andWhere('ad.startsAt <= :now', { now: new Date() });
|
||||
} else if (ps.publishing === false) {
|
||||
query.andWhere('ad.expiresAt <= :now', { now: new Date() }).orWhere('ad.startsAt > :now', { now: new Date() });
|
||||
}
|
||||
const ads = await query.limit(ps.limit).getMany();
|
||||
|
||||
return ads;
|
||||
return ads.map(ad => ({
|
||||
id: ad.id,
|
||||
expiresAt: ad.expiresAt.toISOString(),
|
||||
startsAt: ad.startsAt.toISOString(),
|
||||
dayOfWeek: ad.dayOfWeek,
|
||||
url: ad.url,
|
||||
imageUrl: ad.imageUrl,
|
||||
memo: ad.memo,
|
||||
place: ad.place,
|
||||
priority: ad.priority,
|
||||
ratio: ad.ratio,
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import { AnnouncementService } from '@/core/AnnouncementService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ import { IdService } from '@/core/IdService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageAvatarDecorations',
|
||||
} as const;
|
||||
|
|
|
@ -12,6 +12,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageAvatarDecorations',
|
||||
errors: {
|
||||
|
|
|
@ -15,6 +15,8 @@ import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageAvatarDecorations',
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageAvatarDecorations',
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import { DI } from '@/di-symbols.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import { DI } from '@/di-symbols.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
} as const;
|
||||
|
|
|
@ -10,6 +10,8 @@ import { QueueService } from '@/core/QueueService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -13,6 +13,8 @@ import { DI } from '@/di-symbols.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -13,6 +13,8 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
|
|
@ -14,6 +14,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
|
@ -29,6 +31,8 @@ export const meta = {
|
|||
id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
|
||||
},
|
||||
},
|
||||
|
||||
ref: 'EmojiDetailed',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -7,17 +7,18 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||
import { IsNull } from 'typeorm';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { EmojisRepository } from '@/models/_.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import type { MiDriveFile } from '@/models/DriveFile.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { DriveService } from '@/core/DriveService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
|
@ -62,50 +63,43 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
constructor(
|
||||
@Inject(DI.emojisRepository)
|
||||
private emojisRepository: EmojisRepository,
|
||||
|
||||
private emojiEntityService: EmojiEntityService,
|
||||
private idService: IdService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private customEmojiService: CustomEmojiService,
|
||||
private driveService: DriveService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const emoji = await this.emojisRepository.findOneBy({ id: ps.emojiId });
|
||||
|
||||
if (emoji == null) {
|
||||
throw new ApiError(meta.errors.noSuchEmoji);
|
||||
}
|
||||
|
||||
const isDuplicate = await this.emojisRepository.findOneBy({ name: emoji.name, host: IsNull() } );
|
||||
if (isDuplicate) throw new ApiError(meta.errors.duplicateName);
|
||||
|
||||
let driveFile: MiDriveFile;
|
||||
|
||||
try {
|
||||
// Create file
|
||||
driveFile = await this.driveService.uploadFromUrl({ url: emoji.originalUrl, user: null, force: true });
|
||||
} catch (e) {
|
||||
// TODO: need to return Drive Error
|
||||
throw new ApiError();
|
||||
}
|
||||
|
||||
const copied = await this.emojisRepository.insert({
|
||||
id: this.idService.gen(),
|
||||
updatedAt: new Date(),
|
||||
// Duplication Check
|
||||
const isDuplicate = await this.customEmojiService.checkDuplicate(emoji.name);
|
||||
if (isDuplicate) throw new ApiError(meta.errors.duplicateName);
|
||||
|
||||
const addedEmoji = await this.customEmojiService.add({
|
||||
driveFile,
|
||||
name: emoji.name,
|
||||
category: emoji.category,
|
||||
aliases: emoji.aliases,
|
||||
host: null,
|
||||
aliases: [],
|
||||
originalUrl: driveFile.url,
|
||||
publicUrl: driveFile.webpublicUrl ?? driveFile.url,
|
||||
type: driveFile.webpublicType ?? driveFile.type,
|
||||
license: emoji.license,
|
||||
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
||||
isSensitive: emoji.isSensitive,
|
||||
localOnly: emoji.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||
}, me);
|
||||
|
||||
this.globalEventService.publishBroadcastStream('emojiAdded', {
|
||||
emoji: await this.emojiEntityService.packDetailed(copied.id),
|
||||
});
|
||||
|
||||
return {
|
||||
id: copied.id,
|
||||
};
|
||||
return this.emojiEntityService.packDetailed(addedEmoji);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
|
|
@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
|||
import { QueueService } from '@/core/QueueService.js';
|
||||
|
||||
export const meta = {
|
||||
secure: true,
|
||||
kind: 'write:admin',
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
|
|
@ -15,6 +15,8 @@ import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
|
|
@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
|
|
@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
|
|
@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import { DI } from '@/di-symbols.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -13,6 +13,8 @@ import { DI } from '@/di-symbols.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -12,6 +12,8 @@ import { QueueService } from '@/core/QueueService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -14,6 +14,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -12,7 +12,19 @@ export const meta = {
|
|||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
tags: ['admin'],
|
||||
res: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
tablename: { type: 'string' },
|
||||
indexname: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -12,6 +12,8 @@ export const meta = {
|
|||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
res: {
|
||||
|
|
|
@ -12,8 +12,29 @@ import { IdService } from '@/core/IdService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: 'array',
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: 'object',
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
ip: { type: 'string' },
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: 'date-time',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -16,6 +16,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
@ -33,13 +35,7 @@ export const meta = {
|
|||
items: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
properties: {
|
||||
code: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
example: 'GR6S02ERUA5VR',
|
||||
},
|
||||
},
|
||||
ref: 'InviteCode',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
|
|
@ -12,6 +12,8 @@ import { DI } from '@/di-symbols.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
@ -21,6 +23,7 @@ export const meta = {
|
|||
items: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
ref: 'InviteCode',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
|
|
@ -13,6 +13,8 @@ import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
|||
export const meta = {
|
||||
tags: ['meta'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
|
@ -282,6 +284,14 @@ export const meta = {
|
|||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
enableVerifymailApi: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
verifymailAuthKey: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
enableChartsForRemoteUser: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
|
@ -338,6 +348,82 @@ export const meta = {
|
|||
type: 'number',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
backgroundImageUrl: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
deeplAuthKey: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
deeplIsPro: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
defaultDarkTheme: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
defaultLightTheme: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
disableRegistration: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
impressumUrl: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
maintainerEmail: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
maintainerName: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
objectStorageS3ForcePathStyle: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
privacyPolicyUrl: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
repositoryUrl: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
summalyProxy: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
themeColor: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
tosUrl: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
uri: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
version: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
@ -444,6 +530,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
deeplIsPro: instance.deeplIsPro,
|
||||
enableIpLogging: instance.enableIpLogging,
|
||||
enableActiveEmailValidation: instance.enableActiveEmailValidation,
|
||||
enableVerifymailApi: instance.enableVerifymailApi,
|
||||
verifymailAuthKey: instance.verifymailAuthKey,
|
||||
enableChartsForRemoteUser: instance.enableChartsForRemoteUser,
|
||||
enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances,
|
||||
enableServerMachineStats: instance.enableServerMachineStats,
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import { QueueService } from '@/core/QueueService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -11,6 +11,8 @@ import type { DeliverQueue } from '@/core/QueueModule.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import type { InboxQueue } from '@/core/QueueModule.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import { QueueService } from '@/core/QueueService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -10,6 +10,8 @@ import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, Obj
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import { RelayService } from '@/core/RelayService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import { RelayService } from '@/core/RelayService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -15,6 +15,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -13,6 +13,8 @@ import { RoleService } from '@/core/RoleService.js';
|
|||
export const meta = {
|
||||
tags: ['admin', 'role'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -11,8 +11,16 @@ import { RoleService } from '@/core/RoleService.js';
|
|||
export const meta = {
|
||||
tags: ['admin', 'role'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
ref: 'Role',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -13,6 +13,8 @@ import { RoleService } from '@/core/RoleService.js';
|
|||
export const meta = {
|
||||
tags: ['admin', 'role'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
|
|
|
@ -12,8 +12,20 @@ import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
|
|||
export const meta = {
|
||||
tags: ['admin', 'role'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
optional: false, nullable: false,
|
||||
items: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
ref: 'Role',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -13,6 +13,8 @@ import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
|
|||
export const meta = {
|
||||
tags: ['admin', 'role'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
@ -23,6 +25,12 @@ export const meta = {
|
|||
id: '07dc7d34-c0d8-49b7-96c6-db3ce64ee0b3',
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
ref: 'Role',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -13,6 +13,8 @@ import { RoleService } from '@/core/RoleService.js';
|
|||
export const meta = {
|
||||
tags: ['admin', 'role'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import { MetaService } from '@/core/MetaService.js';
|
|||
export const meta = {
|
||||
tags: ['admin', 'role'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
} as const;
|
||||
|
|
|
@ -14,6 +14,8 @@ import { RoleService } from '@/core/RoleService.js';
|
|||
export const meta = {
|
||||
tags: ['admin', 'role'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ import { ApiError } from '../../../error.js';
|
|||
export const meta = {
|
||||
tags: ['admin', 'role', 'users'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
requireCredential: false,
|
||||
requireAdmin: true,
|
||||
|
||||
|
@ -26,6 +28,20 @@ export const meta = {
|
|||
id: '224eff5e-2488-4b18-b3e7-f50d94421648',
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', format: 'misskey:id' },
|
||||
createdAt: { type: 'string', format: 'date-time' },
|
||||
user: { ref: 'UserDetailed' },
|
||||
expiresAt: { type: 'string', format: 'date-time', nullable: true },
|
||||
},
|
||||
required: ['id', 'createdAt', 'user'],
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@ -78,7 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
id: assign.id,
|
||||
createdAt: this.idService.parse(assign.id).date.toISOString(),
|
||||
user: await this.userEntityService.pack(assign.user!, me, { detail: true }),
|
||||
expiresAt: assign.expiresAt,
|
||||
expiresAt: assign.expiresAt?.toISOString() ?? null,
|
||||
})));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import { EmailService } from '@/core/EmailService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -17,6 +17,8 @@ export const meta = {
|
|||
|
||||
tags: ['admin', 'meta'],
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
|
|
|
@ -14,7 +14,9 @@ export const meta = {
|
|||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
requireAdmin: true,
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
|
|
|
@ -17,6 +17,8 @@ export const meta = {
|
|||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
nullable: false, optional: false,
|
||||
|
|
|
@ -17,6 +17,8 @@ export const meta = {
|
|||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
kind: 'read:admin',
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
nullable: false, optional: false,
|
||||
|
|
|
@ -19,6 +19,8 @@ import { QueueService } from '@/core/QueueService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { UsersRepository } from '@/models/_.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: 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
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
constructor(
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const user = await this.usersRepository.findOneBy({ id: ps.userId });
|
||||
|
||||
if (user == null) {
|
||||
throw new Error('user not found');
|
||||
}
|
||||
|
||||
if (user.avatarId == null) return;
|
||||
|
||||
await this.usersRepository.update(user.id, {
|
||||
avatar: null,
|
||||
avatarId: null,
|
||||
avatarUrl: null,
|
||||
avatarBlurhash: null,
|
||||
});
|
||||
|
||||
this.moderationLogService.log(me, 'unsetUserAvatar', {
|
||||
userId: user.id,
|
||||
userUsername: user.username,
|
||||
userHost: user.host,
|
||||
fileId: user.avatarId,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { UsersRepository } from '@/models/_.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: 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
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
constructor(
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const user = await this.usersRepository.findOneBy({ id: ps.userId });
|
||||
|
||||
if (user == null) {
|
||||
throw new Error('user not found');
|
||||
}
|
||||
|
||||
if (user.bannerId == null) return;
|
||||
|
||||
await this.usersRepository.update(user.id, {
|
||||
banner: null,
|
||||
bannerId: null,
|
||||
bannerUrl: null,
|
||||
bannerBlurhash: null,
|
||||
});
|
||||
|
||||
this.moderationLogService.log(me, 'unsetUserBanner', {
|
||||
userId: user.id,
|
||||
userUsername: user.username,
|
||||
userHost: user.host,
|
||||
fileId: user.bannerId,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -13,6 +13,8 @@ import { DI } from '@/di-symbols.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -12,6 +12,8 @@ import { MetaService } from '@/core/MetaService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
} as const;
|
||||
|
@ -116,6 +118,8 @@ export const paramDef = {
|
|||
objectStorageS3ForcePathStyle: { type: 'boolean' },
|
||||
enableIpLogging: { type: 'boolean' },
|
||||
enableActiveEmailValidation: { type: 'boolean' },
|
||||
enableVerifymailApi: { type: 'boolean' },
|
||||
verifymailAuthKey: { type: 'string', nullable: true },
|
||||
enableChartsForRemoteUser: { type: 'boolean' },
|
||||
enableChartsForFederatedInstances: { type: 'boolean' },
|
||||
enableServerMachineStats: { type: 'boolean' },
|
||||
|
@ -455,6 +459,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
set.enableActiveEmailValidation = ps.enableActiveEmailValidation;
|
||||
}
|
||||
|
||||
if (ps.enableVerifymailApi !== undefined) {
|
||||
set.enableVerifymailApi = ps.enableVerifymailApi;
|
||||
}
|
||||
|
||||
if (ps.verifymailAuthKey !== undefined) {
|
||||
if (ps.verifymailAuthKey === '') {
|
||||
set.verifymailAuthKey = null;
|
||||
} else {
|
||||
set.verifymailAuthKey = ps.verifymailAuthKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (ps.enableChartsForRemoteUser !== undefined) {
|
||||
set.enableChartsForRemoteUser = ps.enableChartsForRemoteUser;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
|
|||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
kind: 'write:admin',
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
} as const;
|
||||
|
|
|
@ -12,7 +12,8 @@ import { NoteReadService } from '@/core/NoteReadService.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
|
||||
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
@ -70,7 +71,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private noteEntityService: NoteEntityService,
|
||||
private queryService: QueryService,
|
||||
private noteReadService: NoteReadService,
|
||||
private funoutTimelineService: FunoutTimelineService,
|
||||
private fanoutTimelineService: FanoutTimelineService,
|
||||
private globalEventService: GlobalEventService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
|
||||
|
@ -85,12 +87,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchAntenna);
|
||||
}
|
||||
|
||||
this.antennasRepository.update(antenna.id, {
|
||||
isActive: true,
|
||||
lastUsedAt: new Date(),
|
||||
});
|
||||
// falseだった場合はアンテナの配信先が増えたことを通知したい
|
||||
const needPublishEvent = !antenna.isActive;
|
||||
|
||||
let noteIds = await this.funoutTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId);
|
||||
antenna.isActive = true;
|
||||
antenna.lastUsedAt = new Date();
|
||||
this.antennasRepository.update(antenna.id, antenna);
|
||||
|
||||
if (needPublishEvent) {
|
||||
this.globalEventService.publishInternalEvent('antennaUpdated', antenna);
|
||||
}
|
||||
|
||||
let noteIds = await this.fanoutTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId);
|
||||
noteIds = noteIds.slice(0, ps.limit);
|
||||
if (noteIds.length === 0) {
|
||||
return [];
|
||||
|
|
|
@ -4,17 +4,17 @@
|
|||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as Redis from 'ioredis';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { ChannelsRepository, MiNote, NotesRepository } from '@/models/_.js';
|
||||
import type { ChannelsRepository, NotesRepository } from '@/models/_.js';
|
||||
import { QueryService } from '@/core/QueryService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import ActiveUsersChart from '@/core/chart/charts/active-users.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
|
||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
|
||||
import { MiLocalUser } from '@/models/User.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
@ -50,6 +50,7 @@ export const paramDef = {
|
|||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
sinceDate: { type: 'integer' },
|
||||
untilDate: { type: 'integer' },
|
||||
allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
|
||||
},
|
||||
required: ['channelId'],
|
||||
} as const;
|
||||
|
@ -57,9 +58,6 @@ export const paramDef = {
|
|||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
@Inject(DI.redisForTimelines)
|
||||
private redisForTimelines: Redis.Redis,
|
||||
|
||||
@Inject(DI.notesRepository)
|
||||
private notesRepository: NotesRepository,
|
||||
|
||||
|
@ -69,14 +67,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private idService: IdService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
private queryService: QueryService,
|
||||
private funoutTimelineService: FunoutTimelineService,
|
||||
private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
|
||||
private cacheService: CacheService,
|
||||
private activeUsersChart: ActiveUsersChart,
|
||||
private metaService: MetaService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
|
||||
const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null);
|
||||
const isRangeSpecified = untilId != null && sinceId != null;
|
||||
|
||||
const serverSettings = await this.metaService.fetch();
|
||||
|
||||
const channel = await this.channelsRepository.findOneBy({
|
||||
id: ps.channelId,
|
||||
|
@ -88,64 +88,48 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
if (me) this.activeUsersChart.read(me);
|
||||
|
||||
if (isRangeSpecified || sinceId == null) {
|
||||
const [
|
||||
userIdsWhoMeMuting,
|
||||
] = me ? await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(me.id),
|
||||
]) : [new Set<string>()];
|
||||
|
||||
let noteIds = await this.funoutTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId);
|
||||
noteIds = noteIds.slice(0, ps.limit);
|
||||
|
||||
if (noteIds.length > 0) {
|
||||
const query = this.notesRepository.createQueryBuilder('note')
|
||||
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.leftJoinAndSelect('note.channel', 'channel');
|
||||
|
||||
let timeline = await query.getMany();
|
||||
|
||||
timeline = timeline.filter(note => {
|
||||
if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// TODO: フィルタで件数が減った場合の埋め合わせ処理
|
||||
|
||||
timeline.sort((a, b) => a.id > b.id ? -1 : 1);
|
||||
|
||||
if (timeline.length > 0) {
|
||||
return await this.noteEntityService.packMany(timeline, me);
|
||||
}
|
||||
}
|
||||
if (!serverSettings.enableFanoutTimeline) {
|
||||
return await this.noteEntityService.packMany(await this.getFromDb({ untilId, sinceId, limit: ps.limit, channelId: channel.id }, me), me);
|
||||
}
|
||||
|
||||
//#region fallback to database
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
|
||||
.andWhere('note.channelId = :channelId', { channelId: channel.id })
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.leftJoinAndSelect('note.channel', 'channel');
|
||||
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQuery(query, me);
|
||||
this.queryService.generateBlockedUserQuery(query, me);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
const timeline = await query.limit(ps.limit).getMany();
|
||||
|
||||
return await this.noteEntityService.packMany(timeline, me);
|
||||
//#endregion
|
||||
return await this.fanoutTimelineEndpointService.timeline({
|
||||
untilId,
|
||||
sinceId,
|
||||
limit: ps.limit,
|
||||
allowPartial: ps.allowPartial,
|
||||
me,
|
||||
useDbFallback: true,
|
||||
redisTimelines: [`channelTimeline:${channel.id}`],
|
||||
excludePureRenotes: false,
|
||||
dbFallback: async (untilId, sinceId, limit) => {
|
||||
return await this.getFromDb({ untilId, sinceId, limit, channelId: channel.id }, me);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async getFromDb(ps: {
|
||||
untilId: string | null,
|
||||
sinceId: string | null,
|
||||
limit: number,
|
||||
channelId: string
|
||||
}, me: MiLocalUser | null) {
|
||||
//#region fallback to database
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||
.andWhere('note.channelId = :channelId', { channelId: ps.channelId })
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.leftJoinAndSelect('note.channel', 'channel');
|
||||
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQuery(query, me);
|
||||
this.queryService.generateBlockedUserQuery(query, me);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
return await query.limit(ps.limit).getMany();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,23 @@ export const meta = {
|
|||
requireCredential: false,
|
||||
|
||||
tags: ['meta'],
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
nullable: true,
|
||||
properties: {
|
||||
params: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
type: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -36,14 +36,33 @@ export const paramDef = {
|
|||
blocked: { type: 'boolean', nullable: true },
|
||||
notResponding: { type: 'boolean', nullable: true },
|
||||
suspended: { type: 'boolean', nullable: true },
|
||||
silenced: { type: "boolean", nullable: true },
|
||||
silenced: { type: 'boolean', nullable: true },
|
||||
federating: { type: 'boolean', nullable: true },
|
||||
subscribing: { type: 'boolean', nullable: true },
|
||||
publishing: { type: 'boolean', nullable: true },
|
||||
nsfw: { type: 'boolean', nullable: true },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 },
|
||||
offset: { type: 'integer', default: 0 },
|
||||
sort: { type: 'string' },
|
||||
sort: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
enum: [
|
||||
'+pubSub',
|
||||
'-pubSub',
|
||||
'+notes',
|
||||
'-notes',
|
||||
'+users',
|
||||
'-users',
|
||||
'+following',
|
||||
'-following',
|
||||
'+followers',
|
||||
'-followers',
|
||||
'+firstRetrievedAt',
|
||||
'-firstRetrievedAt',
|
||||
'+latestRequestReceivedAt',
|
||||
'-latestRequestReceivedAt',
|
||||
],
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
@ -112,18 +131,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
}
|
||||
}
|
||||
|
||||
if (typeof ps.silenced === "boolean") {
|
||||
if (typeof ps.silenced === 'boolean') {
|
||||
const meta = await this.metaService.fetch(true);
|
||||
|
||||
if (ps.silenced) {
|
||||
if (meta.silencedHosts.length === 0) {
|
||||
return [];
|
||||
}
|
||||
query.andWhere("instance.host IN (:...silences)", {
|
||||
query.andWhere('instance.host IN (:...silences)', {
|
||||
silences: meta.silencedHosts,
|
||||
});
|
||||
} else if (meta.silencedHosts.length > 0) {
|
||||
query.andWhere("instance.host NOT IN (:...silences)", {
|
||||
query.andWhere('instance.host NOT IN (:...silences)', {
|
||||
silences: meta.silencedHosts,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,12 +16,9 @@ export const meta = {
|
|||
requireCredential: false,
|
||||
|
||||
res: {
|
||||
oneOf: [{
|
||||
type: 'object',
|
||||
ref: 'FederationInstance',
|
||||
}, {
|
||||
type: 'null',
|
||||
}],
|
||||
type: 'object',
|
||||
optional: false, nullable: true,
|
||||
ref: 'FederationInstance',
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
|
|
@ -18,6 +18,92 @@ export const meta = {
|
|||
|
||||
allowGet: true,
|
||||
cacheSec: 60 * 60,
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
topSubInstances: {
|
||||
type: 'array',
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
firstRetrievedAt: { type: 'string' },
|
||||
host: { type: 'string' },
|
||||
usersCount: { type: 'number' },
|
||||
notesCount: { type: 'number' },
|
||||
followingCount: { type: 'number' },
|
||||
followersCount: { type: 'number' },
|
||||
isNotResponding: { type: 'boolean' },
|
||||
isSuspended: { type: 'boolean' },
|
||||
isBlocked: { type: 'boolean' },
|
||||
softwareName: { type: 'string' },
|
||||
softwareVersion: { type: 'string' },
|
||||
openRegistrations: { type: 'boolean' },
|
||||
name: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
maintainerName: { type: 'string' },
|
||||
maintainerEmail: { type: 'string' },
|
||||
isSilenced: { type: 'boolean' },
|
||||
iconUrl: { type: 'string' },
|
||||
faviconUrl: { type: 'string' },
|
||||
themeColor: { type: 'string' },
|
||||
infoUpdatedAt: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
latestRequestReceivedAt: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
otherFollowersCount: { type: 'number' },
|
||||
topPubInstances: {
|
||||
type: 'array',
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
firstRetrievedAt: { type: 'string' },
|
||||
host: { type: 'string' },
|
||||
usersCount: { type: 'number' },
|
||||
notesCount: { type: 'number' },
|
||||
followingCount: { type: 'number' },
|
||||
followersCount: { type: 'number' },
|
||||
isNotResponding: { type: 'boolean' },
|
||||
isSuspended: { type: 'boolean' },
|
||||
isBlocked: { type: 'boolean' },
|
||||
softwareName: { type: 'string' },
|
||||
softwareVersion: { type: 'string' },
|
||||
openRegistrations: { type: 'boolean' },
|
||||
name: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
maintainerName: { type: 'string' },
|
||||
maintainerEmail: { type: 'string' },
|
||||
isSilenced: { type: 'boolean' },
|
||||
iconUrl: { type: 'string' },
|
||||
faviconUrl: { type: 'string' },
|
||||
themeColor: { type: 'string' },
|
||||
infoUpdatedAt: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
latestRequestReceivedAt: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
otherFollowingCount: { type: 'number' },
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -32,6 +32,18 @@ export const meta = {
|
|||
id: '693ba8ba-b486-40df-a174-72f8279b56a4',
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: {
|
||||
type: 'string',
|
||||
},
|
||||
data: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -16,6 +16,18 @@ export const meta = {
|
|||
requireCredential: false,
|
||||
allowGet: true,
|
||||
cacheSec: 60 * 3,
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
items: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -27,6 +27,12 @@ export const meta = {
|
|||
|
||||
errors: {
|
||||
},
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
ref: 'Flash',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
|||
import type { GalleryPostsRepository } from '@/models/_.js';
|
||||
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { FeaturedService } from '@/core/FeaturedService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['gallery'],
|
||||
|
@ -27,25 +28,49 @@ export const meta = {
|
|||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
properties: {
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
private galleryPostsRankingCache: string[] = [];
|
||||
private galleryPostsRankingCacheLastFetchedAt = 0;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.galleryPostsRepository)
|
||||
private galleryPostsRepository: GalleryPostsRepository,
|
||||
|
||||
private galleryPostEntityService: GalleryPostEntityService,
|
||||
private featuredService: FeaturedService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const query = this.galleryPostsRepository.createQueryBuilder('post')
|
||||
.andWhere('post.createdAt > :date', { date: new Date(Date.now() - (1000 * 60 * 60 * 24 * 3)) })
|
||||
.andWhere('post.likedCount > 0')
|
||||
.orderBy('post.likedCount', 'DESC');
|
||||
let postIds: string[];
|
||||
if (this.galleryPostsRankingCacheLastFetchedAt !== 0 && (Date.now() - this.galleryPostsRankingCacheLastFetchedAt < 1000 * 60 * 30)) {
|
||||
postIds = this.galleryPostsRankingCache;
|
||||
} else {
|
||||
postIds = await this.featuredService.getGalleryPostsRanking(100);
|
||||
this.galleryPostsRankingCache = postIds;
|
||||
this.galleryPostsRankingCacheLastFetchedAt = Date.now();
|
||||
}
|
||||
|
||||
const posts = await query.limit(10).getMany();
|
||||
postIds.sort((a, b) => a > b ? -1 : 1);
|
||||
if (ps.untilId) {
|
||||
postIds = postIds.filter(id => id < ps.untilId!);
|
||||
}
|
||||
postIds = postIds.slice(0, ps.limit);
|
||||
|
||||
if (postIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const query = this.galleryPostsRepository.createQueryBuilder('post')
|
||||
.where('post.id IN (:...postIds)', { postIds: postIds });
|
||||
|
||||
const posts = await query.getMany();
|
||||
|
||||
return await this.galleryPostEntityService.packMany(posts, me);
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/_.js';
|
||||
import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
|
@ -57,6 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
@Inject(DI.galleryLikesRepository)
|
||||
private galleryLikesRepository: GalleryLikesRepository,
|
||||
|
||||
private featuredService: FeaturedService,
|
||||
private idService: IdService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
|
@ -88,6 +90,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
userId: me.id,
|
||||
});
|
||||
|
||||
// ランキング更新
|
||||
if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
|
||||
await this.featuredService.updateGalleryPostsRanking(post.id, 1);
|
||||
}
|
||||
|
||||
this.galleryPostsRepository.increment({ id: post.id }, 'likedCount', 1);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { GalleryPostsRepository, GalleryLikesRepository } from '@/models/_.js';
|
||||
import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
|
||||
|
@ -49,6 +51,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
@Inject(DI.galleryLikesRepository)
|
||||
private galleryLikesRepository: GalleryLikesRepository,
|
||||
|
||||
private featuredService: FeaturedService,
|
||||
private idService: IdService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId });
|
||||
|
@ -68,6 +73,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
// Delete like
|
||||
await this.galleryLikesRepository.delete(exist.id);
|
||||
|
||||
// ランキング更新
|
||||
if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
|
||||
await this.featuredService.updateGalleryPostsRanking(post.id, -1);
|
||||
}
|
||||
|
||||
this.galleryPostsRepository.decrement({ id: post.id }, 'likedCount', 1);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,6 +16,16 @@ export const meta = {
|
|||
requireCredential: false,
|
||||
allowGet: true,
|
||||
cacheSec: 60 * 1,
|
||||
res: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
properties: {
|
||||
count: {
|
||||
type: 'number',
|
||||
nullable: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -33,6 +33,16 @@ export const meta = {
|
|||
id: '798d6847-b1ed-4f9c-b1f9-163c42655995',
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
nullable: false,
|
||||
optional: false,
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -37,6 +37,140 @@ export const meta = {
|
|||
id: 'bf32b864-449b-47b8-974e-f9a5468546f1',
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
nullable: false,
|
||||
optional: false,
|
||||
properties: {
|
||||
rp: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
displayName: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
challenge: {
|
||||
type: 'string',
|
||||
},
|
||||
pubKeyCredParams: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: {
|
||||
type: 'string',
|
||||
},
|
||||
alg: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
timeout: {
|
||||
type: 'number',
|
||||
nullable: true,
|
||||
},
|
||||
excludeCredentials: {
|
||||
type: 'array',
|
||||
nullable: true,
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
},
|
||||
transports: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
"ble",
|
||||
"cable",
|
||||
"hybrid",
|
||||
"internal",
|
||||
"nfc",
|
||||
"smart-card",
|
||||
"usb",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
authenticatorSelection: {
|
||||
type: 'object',
|
||||
nullable: true,
|
||||
properties: {
|
||||
authenticatorAttachment: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
"cross-platform",
|
||||
"platform",
|
||||
],
|
||||
},
|
||||
requireResidentKey: {
|
||||
type: 'boolean',
|
||||
},
|
||||
userVerification: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
"discouraged",
|
||||
"preferred",
|
||||
"required",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
attestation: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
enum: [
|
||||
"direct",
|
||||
"enterprise",
|
||||
"indirect",
|
||||
"none",
|
||||
],
|
||||
},
|
||||
extensions: {
|
||||
type: 'object',
|
||||
nullable: true,
|
||||
properties: {
|
||||
appid: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
credProps: {
|
||||
type: 'boolean',
|
||||
nullable: true,
|
||||
},
|
||||
hmacCreateSecret: {
|
||||
type: 'boolean',
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -27,6 +27,19 @@ export const meta = {
|
|||
id: '78d6c839-20c9-4c66-b90a-fc0542168b48',
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
type: 'object',
|
||||
nullable: false,
|
||||
optional: false,
|
||||
properties: {
|
||||
qr: { type: 'string' },
|
||||
url: { type: 'string' },
|
||||
secret: { type: 'string' },
|
||||
label: { type: 'string' },
|
||||
issuer: { type: 'string' },
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
|
@ -13,6 +13,37 @@ export const meta = {
|
|||
requireCredential: true,
|
||||
|
||||
secure: true,
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
format: 'misskey:id',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
},
|
||||
lastUsedAt: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
},
|
||||
permission: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
items: {
|
||||
type: 'string'
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@ -50,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
id: token.id,
|
||||
name: token.name ?? token.app?.name,
|
||||
createdAt: this.idService.parse(token.id).date.toISOString(),
|
||||
lastUsedAt: token.lastUsedAt,
|
||||
lastUsedAt: token.lastUsedAt?.toISOString(),
|
||||
permission: token.permission,
|
||||
})));
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue