wip
This commit is contained in:
parent
61bc8fb378
commit
f236b44f26
|
@ -14,8 +14,15 @@ export class SystemAccounts1740121393164 {
|
||||||
|
|
||||||
const instanceActor = await queryRunner.query(`SELECT "id" FROM "user" WHERE "username" = 'instance.actor'`);
|
const instanceActor = await queryRunner.query(`SELECT "id" FROM "user" WHERE "username" = 'instance.actor'`);
|
||||||
if (instanceActor.length > 0) {
|
if (instanceActor.length > 0) {
|
||||||
await queryRunner.query(`INSERT INTO "system_account" ("id", "userId", "type") VALUES ('TODO', '${instanceActor[0].id}', 'instance.actor')`);
|
await queryRunner.query(`INSERT INTO "system_account" ("id", "userId", "type") VALUES ('${instanceActor[0].id}', '${instanceActor[0].id}', 'actor')`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const relayActor = await queryRunner.query(`SELECT "id" FROM "user" WHERE "username" = 'relay.actor'`);
|
||||||
|
if (relayActor.length > 0) {
|
||||||
|
await queryRunner.query(`INSERT INTO "system_account" ("id", "userId", "type") VALUES ('${relayActor[0].id}', '${relayActor[0].id}', 'relay')`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
async down(queryRunner) {
|
async down(queryRunner) {
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class SystemAccounts21740129169650 {
|
||||||
|
name = 'SystemAccounts21740129169650'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP CONSTRAINT "FK_ab1bc0c1e209daa77b8e8d212ad"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "proxyAccountId"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "proxyAccountId" character varying(32)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD CONSTRAINT "FK_ab1bc0c1e209daa77b8e8d212ad" FOREIGN KEY ("proxyAccountId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import { bindThis } from '@/decorators.js';
|
||||||
import type { AbuseUserReportsRepository, MiAbuseUserReport, MiUser, UsersRepository } from '@/models/_.js';
|
import type { AbuseUserReportsRepository, MiAbuseUserReport, MiUser, UsersRepository } from '@/models/_.js';
|
||||||
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
|
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
import { InstanceActorService } from '@/core/SystemAccountService.js';
|
||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
import { IdService } from './IdService.js';
|
import { IdService } from './IdService.js';
|
||||||
|
|
|
@ -24,7 +24,6 @@ import { AppLockService } from './AppLockService.js';
|
||||||
import { AchievementService } from './AchievementService.js';
|
import { AchievementService } from './AchievementService.js';
|
||||||
import { AvatarDecorationService } from './AvatarDecorationService.js';
|
import { AvatarDecorationService } from './AvatarDecorationService.js';
|
||||||
import { CaptchaService } from './CaptchaService.js';
|
import { CaptchaService } from './CaptchaService.js';
|
||||||
import { CreateSystemUserService } from './CreateSystemUserService.js';
|
|
||||||
import { CustomEmojiService } from './CustomEmojiService.js';
|
import { CustomEmojiService } from './CustomEmojiService.js';
|
||||||
import { DeleteAccountService } from './DeleteAccountService.js';
|
import { DeleteAccountService } from './DeleteAccountService.js';
|
||||||
import { DownloadService } from './DownloadService.js';
|
import { DownloadService } from './DownloadService.js';
|
||||||
|
@ -37,7 +36,7 @@ import { HashtagService } from './HashtagService.js';
|
||||||
import { HttpRequestService } from './HttpRequestService.js';
|
import { HttpRequestService } from './HttpRequestService.js';
|
||||||
import { IdService } from './IdService.js';
|
import { IdService } from './IdService.js';
|
||||||
import { ImageProcessingService } from './ImageProcessingService.js';
|
import { ImageProcessingService } from './ImageProcessingService.js';
|
||||||
import { InstanceActorService } from './InstanceActorService.js';
|
import { SystemAccountService } from './SystemAccountService.js';
|
||||||
import { InternalStorageService } from './InternalStorageService.js';
|
import { InternalStorageService } from './InternalStorageService.js';
|
||||||
import { MetaService } from './MetaService.js';
|
import { MetaService } from './MetaService.js';
|
||||||
import { MfmService } from './MfmService.js';
|
import { MfmService } from './MfmService.js';
|
||||||
|
@ -69,7 +68,6 @@ import { UserSuspendService } from './UserSuspendService.js';
|
||||||
import { UserAuthService } from './UserAuthService.js';
|
import { UserAuthService } from './UserAuthService.js';
|
||||||
import { VideoProcessingService } from './VideoProcessingService.js';
|
import { VideoProcessingService } from './VideoProcessingService.js';
|
||||||
import { UserWebhookService } from './UserWebhookService.js';
|
import { UserWebhookService } from './UserWebhookService.js';
|
||||||
import { ProxyAccountService } from './ProxyAccountService.js';
|
|
||||||
import { UtilityService } from './UtilityService.js';
|
import { UtilityService } from './UtilityService.js';
|
||||||
import { FileInfoService } from './FileInfoService.js';
|
import { FileInfoService } from './FileInfoService.js';
|
||||||
import { SearchService } from './SearchService.js';
|
import { SearchService } from './SearchService.js';
|
||||||
|
@ -167,7 +165,6 @@ const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppL
|
||||||
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
|
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
|
||||||
const $AvatarDecorationService: Provider = { provide: 'AvatarDecorationService', useExisting: AvatarDecorationService };
|
const $AvatarDecorationService: Provider = { provide: 'AvatarDecorationService', useExisting: AvatarDecorationService };
|
||||||
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService };
|
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService };
|
||||||
const $CreateSystemUserService: Provider = { provide: 'CreateSystemUserService', useExisting: CreateSystemUserService };
|
|
||||||
const $CustomEmojiService: Provider = { provide: 'CustomEmojiService', useExisting: CustomEmojiService };
|
const $CustomEmojiService: Provider = { provide: 'CustomEmojiService', useExisting: CustomEmojiService };
|
||||||
const $DeleteAccountService: Provider = { provide: 'DeleteAccountService', useExisting: DeleteAccountService };
|
const $DeleteAccountService: Provider = { provide: 'DeleteAccountService', useExisting: DeleteAccountService };
|
||||||
const $DownloadService: Provider = { provide: 'DownloadService', useExisting: DownloadService };
|
const $DownloadService: Provider = { provide: 'DownloadService', useExisting: DownloadService };
|
||||||
|
@ -180,7 +177,6 @@ const $HashtagService: Provider = { provide: 'HashtagService', useExisting: Hash
|
||||||
const $HttpRequestService: Provider = { provide: 'HttpRequestService', useExisting: HttpRequestService };
|
const $HttpRequestService: Provider = { provide: 'HttpRequestService', useExisting: HttpRequestService };
|
||||||
const $IdService: Provider = { provide: 'IdService', useExisting: IdService };
|
const $IdService: Provider = { provide: 'IdService', useExisting: IdService };
|
||||||
const $ImageProcessingService: Provider = { provide: 'ImageProcessingService', useExisting: ImageProcessingService };
|
const $ImageProcessingService: Provider = { provide: 'ImageProcessingService', useExisting: ImageProcessingService };
|
||||||
const $InstanceActorService: Provider = { provide: 'InstanceActorService', useExisting: InstanceActorService };
|
|
||||||
const $InternalStorageService: Provider = { provide: 'InternalStorageService', useExisting: InternalStorageService };
|
const $InternalStorageService: Provider = { provide: 'InternalStorageService', useExisting: InternalStorageService };
|
||||||
const $MetaService: Provider = { provide: 'MetaService', useExisting: MetaService };
|
const $MetaService: Provider = { provide: 'MetaService', useExisting: MetaService };
|
||||||
const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService };
|
const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService };
|
||||||
|
@ -191,7 +187,7 @@ const $NotePiningService: Provider = { provide: 'NotePiningService', useExisting
|
||||||
const $NoteReadService: Provider = { provide: 'NoteReadService', useExisting: NoteReadService };
|
const $NoteReadService: Provider = { provide: 'NoteReadService', useExisting: NoteReadService };
|
||||||
const $NotificationService: Provider = { provide: 'NotificationService', useExisting: NotificationService };
|
const $NotificationService: Provider = { provide: 'NotificationService', useExisting: NotificationService };
|
||||||
const $PollService: Provider = { provide: 'PollService', useExisting: PollService };
|
const $PollService: Provider = { provide: 'PollService', useExisting: PollService };
|
||||||
const $ProxyAccountService: Provider = { provide: 'ProxyAccountService', useExisting: ProxyAccountService };
|
const $SystemAccountService: Provider = { provide: 'SystemAccountService', useExisting: SystemAccountService };
|
||||||
const $PushNotificationService: Provider = { provide: 'PushNotificationService', useExisting: PushNotificationService };
|
const $PushNotificationService: Provider = { provide: 'PushNotificationService', useExisting: PushNotificationService };
|
||||||
const $QueryService: Provider = { provide: 'QueryService', useExisting: QueryService };
|
const $QueryService: Provider = { provide: 'QueryService', useExisting: QueryService };
|
||||||
const $ReactionService: Provider = { provide: 'ReactionService', useExisting: ReactionService };
|
const $ReactionService: Provider = { provide: 'ReactionService', useExisting: ReactionService };
|
||||||
|
@ -318,7 +314,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
AchievementService,
|
AchievementService,
|
||||||
AvatarDecorationService,
|
AvatarDecorationService,
|
||||||
CaptchaService,
|
CaptchaService,
|
||||||
CreateSystemUserService,
|
|
||||||
CustomEmojiService,
|
CustomEmojiService,
|
||||||
DeleteAccountService,
|
DeleteAccountService,
|
||||||
DownloadService,
|
DownloadService,
|
||||||
|
@ -331,7 +326,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
HttpRequestService,
|
HttpRequestService,
|
||||||
IdService,
|
IdService,
|
||||||
ImageProcessingService,
|
ImageProcessingService,
|
||||||
InstanceActorService,
|
|
||||||
InternalStorageService,
|
InternalStorageService,
|
||||||
MetaService,
|
MetaService,
|
||||||
MfmService,
|
MfmService,
|
||||||
|
@ -342,7 +336,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
NoteReadService,
|
NoteReadService,
|
||||||
NotificationService,
|
NotificationService,
|
||||||
PollService,
|
PollService,
|
||||||
ProxyAccountService,
|
SystemAccountService,
|
||||||
PushNotificationService,
|
PushNotificationService,
|
||||||
QueryService,
|
QueryService,
|
||||||
ReactionService,
|
ReactionService,
|
||||||
|
@ -465,7 +459,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$AchievementService,
|
$AchievementService,
|
||||||
$AvatarDecorationService,
|
$AvatarDecorationService,
|
||||||
$CaptchaService,
|
$CaptchaService,
|
||||||
$CreateSystemUserService,
|
|
||||||
$CustomEmojiService,
|
$CustomEmojiService,
|
||||||
$DeleteAccountService,
|
$DeleteAccountService,
|
||||||
$DownloadService,
|
$DownloadService,
|
||||||
|
@ -478,7 +471,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$HttpRequestService,
|
$HttpRequestService,
|
||||||
$IdService,
|
$IdService,
|
||||||
$ImageProcessingService,
|
$ImageProcessingService,
|
||||||
$InstanceActorService,
|
|
||||||
$InternalStorageService,
|
$InternalStorageService,
|
||||||
$MetaService,
|
$MetaService,
|
||||||
$MfmService,
|
$MfmService,
|
||||||
|
@ -489,7 +481,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$NoteReadService,
|
$NoteReadService,
|
||||||
$NotificationService,
|
$NotificationService,
|
||||||
$PollService,
|
$PollService,
|
||||||
$ProxyAccountService,
|
$SystemAccountService,
|
||||||
$PushNotificationService,
|
$PushNotificationService,
|
||||||
$QueryService,
|
$QueryService,
|
||||||
$ReactionService,
|
$ReactionService,
|
||||||
|
@ -613,7 +605,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
AchievementService,
|
AchievementService,
|
||||||
AvatarDecorationService,
|
AvatarDecorationService,
|
||||||
CaptchaService,
|
CaptchaService,
|
||||||
CreateSystemUserService,
|
|
||||||
CustomEmojiService,
|
CustomEmojiService,
|
||||||
DeleteAccountService,
|
DeleteAccountService,
|
||||||
DownloadService,
|
DownloadService,
|
||||||
|
@ -626,7 +617,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
HttpRequestService,
|
HttpRequestService,
|
||||||
IdService,
|
IdService,
|
||||||
ImageProcessingService,
|
ImageProcessingService,
|
||||||
InstanceActorService,
|
|
||||||
InternalStorageService,
|
InternalStorageService,
|
||||||
MetaService,
|
MetaService,
|
||||||
MfmService,
|
MfmService,
|
||||||
|
@ -637,7 +627,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
NoteReadService,
|
NoteReadService,
|
||||||
NotificationService,
|
NotificationService,
|
||||||
PollService,
|
PollService,
|
||||||
ProxyAccountService,
|
SystemAccountService,
|
||||||
PushNotificationService,
|
PushNotificationService,
|
||||||
QueryService,
|
QueryService,
|
||||||
ReactionService,
|
ReactionService,
|
||||||
|
@ -759,7 +749,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$AchievementService,
|
$AchievementService,
|
||||||
$AvatarDecorationService,
|
$AvatarDecorationService,
|
||||||
$CaptchaService,
|
$CaptchaService,
|
||||||
$CreateSystemUserService,
|
|
||||||
$CustomEmojiService,
|
$CustomEmojiService,
|
||||||
$DeleteAccountService,
|
$DeleteAccountService,
|
||||||
$DownloadService,
|
$DownloadService,
|
||||||
|
@ -772,7 +761,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$HttpRequestService,
|
$HttpRequestService,
|
||||||
$IdService,
|
$IdService,
|
||||||
$ImageProcessingService,
|
$ImageProcessingService,
|
||||||
$InstanceActorService,
|
|
||||||
$InternalStorageService,
|
$InternalStorageService,
|
||||||
$MetaService,
|
$MetaService,
|
||||||
$MfmService,
|
$MfmService,
|
||||||
|
@ -783,7 +771,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$NoteReadService,
|
$NoteReadService,
|
||||||
$NotificationService,
|
$NotificationService,
|
||||||
$PollService,
|
$PollService,
|
||||||
$ProxyAccountService,
|
$SystemAccountService,
|
||||||
$PushNotificationService,
|
$PushNotificationService,
|
||||||
$QueryService,
|
$QueryService,
|
||||||
$ReactionService,
|
$ReactionService,
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { randomUUID } from 'node:crypto';
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import bcrypt from 'bcryptjs';
|
|
||||||
import { IsNull, DataSource } from 'typeorm';
|
|
||||||
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
|
|
||||||
import { MiUser } from '@/models/User.js';
|
|
||||||
import { MiUserProfile } from '@/models/UserProfile.js';
|
|
||||||
import { IdService } from '@/core/IdService.js';
|
|
||||||
import { MiUserKeypair } from '@/models/UserKeypair.js';
|
|
||||||
import { MiUsedUsername } from '@/models/UsedUsername.js';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import generateNativeUserToken from '@/misc/generate-native-user-token.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CreateSystemUserService {
|
|
||||||
constructor(
|
|
||||||
@Inject(DI.db)
|
|
||||||
private db: DataSource,
|
|
||||||
|
|
||||||
private idService: IdService,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async createSystemUser(username: string): Promise<MiUser> {
|
|
||||||
const password = randomUUID();
|
|
||||||
|
|
||||||
// Generate hash of password
|
|
||||||
const salt = await bcrypt.genSalt(8);
|
|
||||||
const hash = await bcrypt.hash(password, salt);
|
|
||||||
|
|
||||||
// Generate secret
|
|
||||||
const secret = generateNativeUserToken();
|
|
||||||
|
|
||||||
const keyPair = await genRsaKeyPair();
|
|
||||||
|
|
||||||
let account!: MiUser;
|
|
||||||
|
|
||||||
// Start transaction
|
|
||||||
await this.db.transaction(async transactionalEntityManager => {
|
|
||||||
const exist = await transactionalEntityManager.findOneBy(MiUser, {
|
|
||||||
usernameLower: username.toLowerCase(),
|
|
||||||
host: IsNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (exist) throw new Error('the user is already exists');
|
|
||||||
|
|
||||||
account = await transactionalEntityManager.insert(MiUser, {
|
|
||||||
id: this.idService.gen(),
|
|
||||||
username: username,
|
|
||||||
usernameLower: username.toLowerCase(),
|
|
||||||
host: null,
|
|
||||||
token: secret,
|
|
||||||
isRoot: false,
|
|
||||||
isLocked: true,
|
|
||||||
isExplorable: false,
|
|
||||||
isBot: true,
|
|
||||||
}).then(x => transactionalEntityManager.findOneByOrFail(MiUser, x.identifiers[0]));
|
|
||||||
|
|
||||||
await transactionalEntityManager.insert(MiUserKeypair, {
|
|
||||||
publicKey: keyPair.publicKey,
|
|
||||||
privateKey: keyPair.privateKey,
|
|
||||||
userId: account.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await transactionalEntityManager.insert(MiUserProfile, {
|
|
||||||
userId: account.id,
|
|
||||||
autoAcceptFollowed: false,
|
|
||||||
password: hash,
|
|
||||||
});
|
|
||||||
|
|
||||||
await transactionalEntityManager.insert(MiUsedUsername, {
|
|
||||||
createdAt: new Date(),
|
|
||||||
username: username.toLowerCase(),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { IsNull, Not } from 'typeorm';
|
|
||||||
import type { MiLocalUser } from '@/models/User.js';
|
|
||||||
import type { UsersRepository } from '@/models/_.js';
|
|
||||||
import { MemorySingleCache } from '@/misc/cache.js';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
|
|
||||||
const ACTOR_USERNAME = 'instance.actor' as const;
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class InstanceActorService {
|
|
||||||
private cache: MemorySingleCache<MiLocalUser>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
private createSystemUserService: CreateSystemUserService,
|
|
||||||
) {
|
|
||||||
this.cache = new MemorySingleCache<MiLocalUser>(Infinity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async realLocalUsersPresent(): Promise<boolean> {
|
|
||||||
return await this.usersRepository.existsBy({
|
|
||||||
host: IsNull(),
|
|
||||||
username: Not(ACTOR_USERNAME),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async getInstanceActor(): Promise<MiLocalUser> {
|
|
||||||
const cached = this.cache.get();
|
|
||||||
if (cached) return cached;
|
|
||||||
|
|
||||||
const user = await this.usersRepository.findOneBy({
|
|
||||||
host: IsNull(),
|
|
||||||
username: ACTOR_USERNAME,
|
|
||||||
}) as MiLocalUser | undefined;
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
this.cache.set(user);
|
|
||||||
return user;
|
|
||||||
} else {
|
|
||||||
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as MiLocalUser;
|
|
||||||
this.cache.set(created);
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import type { MiMeta, UsersRepository } from '@/models/_.js';
|
|
||||||
import type { MiLocalUser } from '@/models/User.js';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ProxyAccountService {
|
|
||||||
constructor(
|
|
||||||
@Inject(DI.meta)
|
|
||||||
private meta: MiMeta,
|
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async fetch(): Promise<MiLocalUser | null> {
|
|
||||||
if (this.meta.proxyAccountId == null) return null;
|
|
||||||
return await this.usersRepository.findOneByOrFail({ id: this.meta.proxyAccountId }) as MiLocalUser;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,53 +4,34 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { IsNull } from 'typeorm';
|
import type { MiUser } from '@/models/User.js';
|
||||||
import type { MiLocalUser, MiUser } from '@/models/User.js';
|
import type { RelaysRepository } from '@/models/_.js';
|
||||||
import type { RelaysRepository, UsersRepository } from '@/models/_.js';
|
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { MemorySingleCache } from '@/misc/cache.js';
|
import { MemorySingleCache } from '@/misc/cache.js';
|
||||||
import type { MiRelay } from '@/models/Relay.js';
|
import type { MiRelay } from '@/models/Relay.js';
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
|
|
||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { deepClone } from '@/misc/clone.js';
|
import { deepClone } from '@/misc/clone.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { SystemAccountService } from '@/core/SystemAccountService.js';
|
||||||
const ACTOR_USERNAME = 'relay.actor' as const;
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RelayService {
|
export class RelayService {
|
||||||
private relaysCache: MemorySingleCache<MiRelay[]>;
|
private relaysCache: MemorySingleCache<MiRelay[]>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
@Inject(DI.relaysRepository)
|
@Inject(DI.relaysRepository)
|
||||||
private relaysRepository: RelaysRepository,
|
private relaysRepository: RelaysRepository,
|
||||||
|
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
private createSystemUserService: CreateSystemUserService,
|
private systemAccountService: SystemAccountService,
|
||||||
private apRendererService: ApRendererService,
|
private apRendererService: ApRendererService,
|
||||||
) {
|
) {
|
||||||
this.relaysCache = new MemorySingleCache<MiRelay[]>(1000 * 60 * 10); // 10m
|
this.relaysCache = new MemorySingleCache<MiRelay[]>(1000 * 60 * 10); // 10m
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
|
||||||
private async getRelayActor(): Promise<MiLocalUser> {
|
|
||||||
const user = await this.usersRepository.findOneBy({
|
|
||||||
host: IsNull(),
|
|
||||||
username: ACTOR_USERNAME,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (user) return user as MiLocalUser;
|
|
||||||
|
|
||||||
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME);
|
|
||||||
return created as MiLocalUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async addRelay(inbox: string): Promise<MiRelay> {
|
public async addRelay(inbox: string): Promise<MiRelay> {
|
||||||
const relay = await this.relaysRepository.insertOne({
|
const relay = await this.relaysRepository.insertOne({
|
||||||
|
@ -59,8 +40,8 @@ export class RelayService {
|
||||||
status: 'requesting',
|
status: 'requesting',
|
||||||
});
|
});
|
||||||
|
|
||||||
const relayActor = await this.getRelayActor();
|
const relayActor = await this.systemAccountService.fetch('relay');
|
||||||
const follow = await this.apRendererService.renderFollowRelay(relay, relayActor);
|
const follow = this.apRendererService.renderFollowRelay(relay, relayActor);
|
||||||
const activity = this.apRendererService.addContext(follow);
|
const activity = this.apRendererService.addContext(follow);
|
||||||
this.queueService.deliver(relayActor, activity, relay.inbox, false);
|
this.queueService.deliver(relayActor, activity, relay.inbox, false);
|
||||||
|
|
||||||
|
@ -77,7 +58,7 @@ export class RelayService {
|
||||||
throw new Error('relay not found');
|
throw new Error('relay not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const relayActor = await this.getRelayActor();
|
const relayActor = await this.systemAccountService.fetch('relay');
|
||||||
const follow = this.apRendererService.renderFollowRelay(relay, relayActor);
|
const follow = this.apRendererService.renderFollowRelay(relay, relayActor);
|
||||||
const undo = this.apRendererService.renderUndo(follow, relayActor);
|
const undo = this.apRendererService.renderUndo(follow, relayActor);
|
||||||
const activity = this.apRendererService.addContext(undo);
|
const activity = this.apRendererService.addContext(undo);
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { MiUserKeypair } from '@/models/UserKeypair.js';
|
||||||
import { MiUsedUsername } from '@/models/UsedUsername.js';
|
import { MiUsedUsername } from '@/models/UsedUsername.js';
|
||||||
import generateUserToken from '@/misc/generate-native-user-token.js';
|
import generateUserToken from '@/misc/generate-native-user-token.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
import { InstanceActorService } from '@/core/SystemAccountService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import UsersChart from '@/core/chart/charts/users.js';
|
import UsersChart from '@/core/chart/charts/users.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { randomUUID } from 'node:crypto';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { DataSource, IsNull } from 'typeorm';
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
|
import { MiLocalUser, MiUser } from '@/models/User.js';
|
||||||
|
import { MiSystemAccount, MiUsedUsername, MiUserKeypair, MiUserProfile, type UsersRepository, type SystemAccountsRepository } from '@/models/_.js';
|
||||||
|
import type { UserProfilesRepository } from '@/models/_.js';
|
||||||
|
import { MemoryKVCache } from '@/misc/cache.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import generateNativeUserToken from '@/misc/generate-native-user-token.js';
|
||||||
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
|
||||||
|
|
||||||
|
export const SYSTEM_ACCOUNT_TYPES = ['actor', 'relay', 'proxy'] as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SystemAccountService {
|
||||||
|
private cache: MemoryKVCache<MiLocalUser>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.db)
|
||||||
|
private db: DataSource,
|
||||||
|
|
||||||
|
@Inject(DI.systemAccountsRepository)
|
||||||
|
private systemAccountsRepository: SystemAccountsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.usersRepository)
|
||||||
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userProfilesRepository)
|
||||||
|
private userProfilesRepository: UserProfilesRepository,
|
||||||
|
|
||||||
|
private idService: IdService,
|
||||||
|
) {
|
||||||
|
this.cache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 10); // 10m
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async fetch(type: typeof SYSTEM_ACCOUNT_TYPES[number]): Promise<MiLocalUser> {
|
||||||
|
const cached = this.cache.get(type);
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
const systemAccount = await this.systemAccountsRepository.findOne({
|
||||||
|
where: { type: type },
|
||||||
|
relations: ['user'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (systemAccount) {
|
||||||
|
this.cache.set(type, systemAccount.user as MiLocalUser);
|
||||||
|
return systemAccount.user as MiLocalUser;
|
||||||
|
} else {
|
||||||
|
const created = await this.createCorrespondingUser(type, {
|
||||||
|
username: `system.${type}`,
|
||||||
|
});
|
||||||
|
this.cache.set(type, created);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async createCorrespondingUser(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: {
|
||||||
|
username: string;
|
||||||
|
name?: string;
|
||||||
|
}): Promise<MiLocalUser> {
|
||||||
|
const password = randomUUID();
|
||||||
|
|
||||||
|
// Generate hash of password
|
||||||
|
const salt = await bcrypt.genSalt(8);
|
||||||
|
const hash = await bcrypt.hash(password, salt);
|
||||||
|
|
||||||
|
// Generate secret
|
||||||
|
const secret = generateNativeUserToken();
|
||||||
|
|
||||||
|
const keyPair = await genRsaKeyPair();
|
||||||
|
|
||||||
|
let account!: MiUser;
|
||||||
|
|
||||||
|
// Start transaction
|
||||||
|
await this.db.transaction(async transactionalEntityManager => {
|
||||||
|
const exist = await transactionalEntityManager.findOneBy(MiUser, {
|
||||||
|
usernameLower: extra.username.toLowerCase(),
|
||||||
|
host: IsNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exist) throw new Error('the user is already exists');
|
||||||
|
|
||||||
|
account = await transactionalEntityManager.insert(MiUser, {
|
||||||
|
id: this.idService.gen(),
|
||||||
|
username: extra.username,
|
||||||
|
usernameLower: extra.username.toLowerCase(),
|
||||||
|
host: null,
|
||||||
|
token: secret,
|
||||||
|
isRoot: false,
|
||||||
|
isLocked: true,
|
||||||
|
isExplorable: false,
|
||||||
|
isBot: true,
|
||||||
|
name: extra.name,
|
||||||
|
}).then(x => transactionalEntityManager.findOneByOrFail(MiUser, x.identifiers[0]));
|
||||||
|
|
||||||
|
await transactionalEntityManager.insert(MiUserKeypair, {
|
||||||
|
publicKey: keyPair.publicKey,
|
||||||
|
privateKey: keyPair.privateKey,
|
||||||
|
userId: account.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transactionalEntityManager.insert(MiUserProfile, {
|
||||||
|
userId: account.id,
|
||||||
|
autoAcceptFollowed: false,
|
||||||
|
password: hash,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transactionalEntityManager.insert(MiUsedUsername, {
|
||||||
|
createdAt: new Date(),
|
||||||
|
username: extra.username.toLowerCase(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await transactionalEntityManager.insert(MiSystemAccount, {
|
||||||
|
id: this.idService.gen(),
|
||||||
|
userId: account.id,
|
||||||
|
type: type,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return account as MiLocalUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async updateCorrespondingUserProfile(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: {
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
}): Promise<MiLocalUser> {
|
||||||
|
const user = await this.fetch(type);
|
||||||
|
|
||||||
|
const updates = {} as Partial<MiUser>;
|
||||||
|
if (extra.name !== undefined) updates.name = extra.name;
|
||||||
|
|
||||||
|
await this.usersRepository.update(user.id, updates);
|
||||||
|
|
||||||
|
const profileUpdates = {} as Partial<MiUserProfile>;
|
||||||
|
if (extra.description !== undefined) profileUpdates.description = extra.description;
|
||||||
|
|
||||||
|
await this.userProfilesRepository.update(user.id, profileUpdates);
|
||||||
|
|
||||||
|
const updated = await this.usersRepository.findOneByOrFail({ id: user.id }) as MiLocalUser;
|
||||||
|
this.cache.set(type, updated);
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { IsNull, Not } from 'typeorm';
|
import { IsNull, Not } from 'typeorm';
|
||||||
import type { MiLocalUser, MiRemoteUser } from '@/models/User.js';
|
import type { MiLocalUser, MiRemoteUser } from '@/models/User.js';
|
||||||
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
import { InstanceActorService } from '@/core/SystemAccountService.js';
|
||||||
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository, FollowRequestsRepository, MiMeta } from '@/models/_.js';
|
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository, FollowRequestsRepository, MiMeta } from '@/models/_.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
|
|
|
@ -12,7 +12,7 @@ import type { AdsRepository } from '@/models/_.js';
|
||||||
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
import { InstanceActorService } from '@/core/SystemAccountService.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
|
@ -148,14 +148,12 @@ export class MetaEntityService {
|
||||||
|
|
||||||
const packed = await this.pack(instance);
|
const packed = await this.pack(instance);
|
||||||
|
|
||||||
const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null;
|
|
||||||
|
|
||||||
const packDetailed: Packed<'MetaDetailed'> = {
|
const packDetailed: Packed<'MetaDetailed'> = {
|
||||||
...packed,
|
...packed,
|
||||||
cacheRemoteFiles: instance.cacheRemoteFiles,
|
cacheRemoteFiles: instance.cacheRemoteFiles,
|
||||||
cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles,
|
cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles,
|
||||||
requireSetup: !await this.instanceActorService.realLocalUsersPresent(),
|
requireSetup: !await this.instanceActorService.realLocalUsersPresent(),
|
||||||
proxyAccountName: proxyAccount ? proxyAccount.username : null,
|
proxyAccountName: proxyAccount ? proxyAccount.username : null, // TODO
|
||||||
features: {
|
features: {
|
||||||
localTimeline: instance.policies.ltlAvailable,
|
localTimeline: instance.policies.ltlAvailable,
|
||||||
globalTimeline: instance.policies.gtlAvailable,
|
globalTimeline: instance.policies.gtlAvailable,
|
||||||
|
|
|
@ -172,18 +172,6 @@ export class MiMeta {
|
||||||
})
|
})
|
||||||
public cacheRemoteSensitiveFiles: boolean;
|
public cacheRemoteSensitiveFiles: boolean;
|
||||||
|
|
||||||
@Column({
|
|
||||||
...id(),
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
public proxyAccountId: MiUser['id'] | null;
|
|
||||||
|
|
||||||
@ManyToOne(type => MiUser, {
|
|
||||||
onDelete: 'SET NULL',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public proxyAccount: MiUser | null;
|
|
||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
|
|
@ -70,6 +70,7 @@ export class NodeinfoServerService {
|
||||||
const activeHalfyear = null;
|
const activeHalfyear = null;
|
||||||
const activeMonth = null;
|
const activeMonth = null;
|
||||||
|
|
||||||
|
// TODO
|
||||||
const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId).catch(() => null) : null;
|
const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId).catch(() => null) : null;
|
||||||
|
|
||||||
const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
|
const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { UsersRepository } from '@/models/_.js';
|
import type { UsersRepository } from '@/models/_.js';
|
||||||
import { SignupService } from '@/core/SignupService.js';
|
import { SignupService } from '@/core/SignupService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
import { InstanceActorService } from '@/core/SystemAccountService.js';
|
||||||
import { localUsernameSchema, passwordSchema } from '@/models/User.js';
|
import { localUsernameSchema, passwordSchema } from '@/models/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
|
|
|
@ -231,11 +231,6 @@ export const meta = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
proxyAccountId: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: true,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
email: {
|
email: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
@ -608,7 +603,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
sensitiveMediaDetectionSensitivity: instance.sensitiveMediaDetectionSensitivity,
|
sensitiveMediaDetectionSensitivity: instance.sensitiveMediaDetectionSensitivity,
|
||||||
setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically,
|
setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically,
|
||||||
enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos,
|
enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos,
|
||||||
proxyAccountId: instance.proxyAccountId,
|
|
||||||
email: instance.email,
|
email: instance.email,
|
||||||
smtpSecure: instance.smtpSecure,
|
smtpSecure: instance.smtpSecure,
|
||||||
smtpHost: instance.smtpHost,
|
smtpHost: instance.smtpHost,
|
||||||
|
|
|
@ -88,7 +88,6 @@ export const paramDef = {
|
||||||
sensitiveMediaDetectionSensitivity: { type: 'string', enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'] },
|
sensitiveMediaDetectionSensitivity: { type: 'string', enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'] },
|
||||||
setSensitiveFlagAutomatically: { type: 'boolean' },
|
setSensitiveFlagAutomatically: { type: 'boolean' },
|
||||||
enableSensitiveMediaDetectionForVideos: { type: 'boolean' },
|
enableSensitiveMediaDetectionForVideos: { type: 'boolean' },
|
||||||
proxyAccountId: { type: 'string', format: 'misskey:id', nullable: true },
|
|
||||||
maintainerName: { type: 'string', nullable: true },
|
maintainerName: { type: 'string', nullable: true },
|
||||||
maintainerEmail: { type: 'string', nullable: true },
|
maintainerEmail: { type: 'string', nullable: true },
|
||||||
langs: {
|
langs: {
|
||||||
|
@ -387,10 +386,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
set.enableSensitiveMediaDetectionForVideos = ps.enableSensitiveMediaDetectionForVideos;
|
set.enableSensitiveMediaDetectionForVideos = ps.enableSensitiveMediaDetectionForVideos;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.proxyAccountId !== undefined) {
|
|
||||||
set.proxyAccountId = ps.proxyAccountId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ps.maintainerName !== undefined) {
|
if (ps.maintainerName !== undefined) {
|
||||||
set.maintainerName = ps.maintainerName;
|
set.maintainerName = ps.maintainerName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* 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 { RoleService } from '@/core/RoleService.js';
|
||||||
|
import {
|
||||||
|
descriptionSchema,
|
||||||
|
nameSchema,
|
||||||
|
} from '@/models/User.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
|
import { SystemAccountService } from '@/core/SystemAccountService.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['admin'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
requireModerator: true,
|
||||||
|
kind: 'write:admin:update-proxy-account',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
accessDenied: {
|
||||||
|
message: 'Only administrators can edit members of the role.',
|
||||||
|
code: 'ACCESS_DENIED',
|
||||||
|
id: '101f9105-27cb-489c-842a-69b6d2092c03',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
ref: 'UserDetailed',
|
||||||
|
},
|
||||||
|
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: { ...nameSchema, nullable: true },
|
||||||
|
description: { ...descriptionSchema, nullable: true },
|
||||||
|
avatarId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
|
bannerId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.usersRepository)
|
||||||
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
|
private roleService: RoleService,
|
||||||
|
private userEntityService: UserEntityService,
|
||||||
|
private moderationLogService: ModerationLogService,
|
||||||
|
private systemAccountService: SystemAccountService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const _me = await this.usersRepository.findOneByOrFail({ id: me.id });
|
||||||
|
if (!await this.roleService.isModerator(_me)) {
|
||||||
|
throw new ApiError(meta.errors.accessDenied);
|
||||||
|
}
|
||||||
|
|
||||||
|
const proxy = await this.systemAccountService.updateCorrespondingUserProfile('proxy', {
|
||||||
|
description: ps.description,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updated = await this.userEntityService.pack(proxy.id, proxy, {
|
||||||
|
schema: 'MeDetailed',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.moderationLogService.log(me, 'updateUser', {
|
||||||
|
userId: proxy.id,
|
||||||
|
userUsername: proxy.username,
|
||||||
|
userHost: proxy.host,
|
||||||
|
});
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue