Fixed 267U.pre2
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import define from "../../define.js";
|
||||
import { AbuseUserReports } from "../../../../models/index.js";
|
||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
nullable: false,
|
||||
optional: false,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx"
|
||||
},
|
||||
createdAt: {
|
||||
type: "string",
|
||||
nullable: false,
|
||||
optional: false,
|
||||
format: "date-time"
|
||||
},
|
||||
comment: {
|
||||
type: "string",
|
||||
nullable: false,
|
||||
optional: false
|
||||
},
|
||||
resolved: {
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
optional: false,
|
||||
example: false
|
||||
},
|
||||
reporterId: {
|
||||
type: "string",
|
||||
nullable: false,
|
||||
optional: false,
|
||||
format: "id"
|
||||
},
|
||||
targetUserId: {
|
||||
type: "string",
|
||||
nullable: false,
|
||||
optional: false,
|
||||
format: "id"
|
||||
},
|
||||
assigneeId: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
optional: false,
|
||||
format: "id"
|
||||
},
|
||||
reporter: {
|
||||
type: "object",
|
||||
nullable: false,
|
||||
optional: false,
|
||||
ref: "User"
|
||||
},
|
||||
targetUser: {
|
||||
type: "object",
|
||||
nullable: false,
|
||||
optional: false,
|
||||
ref: "User"
|
||||
},
|
||||
assignee: {
|
||||
type: "object",
|
||||
nullable: true,
|
||||
optional: true,
|
||||
ref: "User"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 10
|
||||
},
|
||||
sinceId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
untilId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
state: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
default: null
|
||||
},
|
||||
reporterOrigin: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"combined",
|
||||
"local",
|
||||
"remote"
|
||||
],
|
||||
default: "combined"
|
||||
},
|
||||
targetUserOrigin: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"combined",
|
||||
"local",
|
||||
"remote"
|
||||
],
|
||||
default: "combined"
|
||||
},
|
||||
forwarded: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const query = makePaginationQuery(AbuseUserReports.createQueryBuilder("report"), ps.sinceId, ps.untilId);
|
||||
switch(ps.state){
|
||||
case "resolved":
|
||||
query.andWhere("report.resolved = TRUE");
|
||||
break;
|
||||
case "unresolved":
|
||||
query.andWhere("report.resolved = FALSE");
|
||||
break;
|
||||
}
|
||||
switch(ps.reporterOrigin){
|
||||
case "local":
|
||||
query.andWhere("report.reporterHost IS NULL");
|
||||
break;
|
||||
case "remote":
|
||||
query.andWhere("report.reporterHost IS NOT NULL");
|
||||
break;
|
||||
}
|
||||
switch(ps.targetUserOrigin){
|
||||
case "local":
|
||||
query.andWhere("report.targetUserHost IS NULL");
|
||||
break;
|
||||
case "remote":
|
||||
query.andWhere("report.targetUserHost IS NOT NULL");
|
||||
break;
|
||||
}
|
||||
const reports = await query.take(ps.limit).getMany();
|
||||
return await AbuseUserReports.packMany(reports);
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import define from "../../../define.js";
|
||||
import { Users } from "../../../../../models/index.js";
|
||||
import { signup } from "../../../common/signup.js";
|
||||
import { IsNull } from "typeorm";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: "User",
|
||||
properties: {
|
||||
token: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
username: Users.localUsernameSchema,
|
||||
password: Users.passwordSchema
|
||||
},
|
||||
required: [
|
||||
"username",
|
||||
"password"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, _me)=>{
|
||||
const me = _me ? await Users.findOneByOrFail({
|
||||
id: _me.id
|
||||
}) : null;
|
||||
const noUsers = await Users.countBy({
|
||||
host: IsNull(),
|
||||
isAdmin: true
|
||||
}) === 0;
|
||||
if (!(noUsers || me?.isAdmin)) throw new Error("access denied");
|
||||
const { account, secret } = await signup({
|
||||
username: ps.username,
|
||||
password: ps.password
|
||||
});
|
||||
const res = await Users.pack(account, account, {
|
||||
detail: true,
|
||||
includeSecrets: true
|
||||
});
|
||||
res.token = secret;
|
||||
return res;
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
import define from "../../../define.js";
|
||||
import { Users } from "../../../../../models/index.js";
|
||||
import { doPostSuspend } from "../../../../../services/suspend-user.js";
|
||||
import { publishUserEvent } from "../../../../../services/stream.js";
|
||||
import { createDeleteAccountJob } from "../../../../../queue/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
if (user.isAdmin) {
|
||||
throw new Error("cannot suspend admin");
|
||||
}
|
||||
if (user.isModerator) {
|
||||
throw new Error("cannot suspend moderator");
|
||||
}
|
||||
if (Users.isLocalUser(user)) {
|
||||
// 物理削除する前にDelete activityを送信する
|
||||
await doPostSuspend(user).catch((e)=>{});
|
||||
createDeleteAccountJob(user, {
|
||||
soft: false
|
||||
});
|
||||
} else {
|
||||
createDeleteAccountJob(user, {
|
||||
soft: true
|
||||
});
|
||||
}
|
||||
await Users.update(user.id, {
|
||||
isDeleted: true
|
||||
});
|
||||
if (Users.isLocalUser(user)) {
|
||||
// Terminate streaming
|
||||
publishUserEvent(user.id, "terminate", {});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
import config from "../../../../../config/index.js";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
import define from "../../../define.js";
|
||||
import { Metas } from "../../../../../models/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const hostedConfig = config.isManagedHosting;
|
||||
const hosted = hostedConfig != null && hostedConfig === true;
|
||||
if (hosted) {
|
||||
const set = {};
|
||||
if (config.deepl.managed != null && config.deepl.managed === true) {
|
||||
if (typeof config.deepl.authKey === "boolean") {
|
||||
set.deeplAuthKey = config.deepl.authKey;
|
||||
}
|
||||
if (typeof config.deepl.isPro === "boolean") {
|
||||
set.deeplIsPro = config.deepl.isPro;
|
||||
}
|
||||
}
|
||||
if (config.libreTranslate.managed != null && config.libreTranslate.managed === true) {
|
||||
if (typeof config.libreTranslate.apiUrl === "string") {
|
||||
set.libreTranslateApiUrl = config.libreTranslate.apiUrl;
|
||||
}
|
||||
if (typeof config.libreTranslate.apiKey === "string") {
|
||||
set.libreTranslateApiKey = config.libreTranslate.apiKey;
|
||||
}
|
||||
}
|
||||
if (config.email.managed != null && config.email.managed === true) {
|
||||
set.enableEmail = true;
|
||||
if (typeof config.email.address === "string") {
|
||||
set.email = config.email.address;
|
||||
}
|
||||
if (typeof config.email.host === "string") {
|
||||
set.smtpHost = config.email.host;
|
||||
}
|
||||
if (typeof config.email.port === "number") {
|
||||
set.smtpPort = config.email.port;
|
||||
}
|
||||
if (typeof config.email.user === "string") {
|
||||
set.smtpUser = config.email.user;
|
||||
}
|
||||
if (typeof config.email.pass === "string") {
|
||||
set.smtpPass = config.email.pass;
|
||||
}
|
||||
if (typeof config.email.useImplicitSslTls === "boolean") {
|
||||
set.smtpSecure = config.email.useImplicitSslTls;
|
||||
}
|
||||
}
|
||||
if (config.objectStorage.managed != null && config.objectStorage.managed === true) {
|
||||
set.useObjectStorage = true;
|
||||
if (typeof config.objectStorage.baseUrl === "string") {
|
||||
set.objectStorageBaseUrl = config.objectStorage.baseUrl;
|
||||
}
|
||||
if (typeof config.objectStorage.bucket === "string") {
|
||||
set.objectStorageBucket = config.objectStorage.bucket;
|
||||
}
|
||||
if (typeof config.objectStorage.prefix === "string") {
|
||||
set.objectStoragePrefix = config.objectStorage.prefix;
|
||||
}
|
||||
if (typeof config.objectStorage.endpoint === "string") {
|
||||
set.objectStorageEndpoint = config.objectStorage.endpoint;
|
||||
}
|
||||
if (typeof config.objectStorage.region === "string") {
|
||||
set.objectStorageRegion = config.objectStorage.region;
|
||||
}
|
||||
if (typeof config.objectStorage.accessKey === "string") {
|
||||
set.objectStorageAccessKey = config.objectStorage.accessKey;
|
||||
}
|
||||
if (typeof config.objectStorage.secretKey === "string") {
|
||||
set.objectStorageSecretKey = config.objectStorage.secretKey;
|
||||
}
|
||||
if (typeof config.objectStorage.useSsl === "boolean") {
|
||||
set.objectStorageUseSSL = config.objectStorage.useSsl;
|
||||
}
|
||||
if (typeof config.objectStorage.connnectOverProxy === "boolean") {
|
||||
set.objectStorageUseProxy = config.objectStorage.connnectOverProxy;
|
||||
}
|
||||
if (typeof config.objectStorage.setPublicReadOnUpload === "boolean") {
|
||||
set.objectStorageSetPublicRead = config.objectStorage.setPublicReadOnUpload;
|
||||
}
|
||||
if (typeof config.objectStorage.s3ForcePathStyle === "boolean") {
|
||||
set.objectStorageS3ForcePathStyle = config.objectStorage.s3ForcePathStyle;
|
||||
}
|
||||
}
|
||||
if (config.summalyProxyUrl !== undefined) {
|
||||
set.summalyProxy = config.summalyProxyUrl;
|
||||
}
|
||||
const meta = await Metas.findOne({
|
||||
where: {},
|
||||
order: {
|
||||
id: "DESC"
|
||||
}
|
||||
});
|
||||
if (meta) await Metas.update(meta.id, set);
|
||||
else await Metas.save(set);
|
||||
insertModerationLog(me, "updateMeta");
|
||||
}
|
||||
return hosted;
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
import define from "../../../define.js";
|
||||
import { Announcements } from "../../../../../models/index.js";
|
||||
import { genId } from "../../../../../misc/gen-id.js";
|
||||
import { publishBroadcastStream } from "../../../../../services/stream.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx"
|
||||
},
|
||||
createdAt: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "date-time"
|
||||
},
|
||||
updatedAt: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
format: "date-time"
|
||||
},
|
||||
title: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
text: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
imageUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
showPopup: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
},
|
||||
isGoodNews: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
title: {
|
||||
type: "string",
|
||||
minLength: 1
|
||||
},
|
||||
text: {
|
||||
type: "string",
|
||||
minLength: 1
|
||||
},
|
||||
imageUrl: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
minLength: 1
|
||||
},
|
||||
showPopup: {
|
||||
type: "boolean"
|
||||
},
|
||||
isGoodNews: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"title",
|
||||
"text",
|
||||
"imageUrl"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const announcement = await Announcements.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
updatedAt: null,
|
||||
title: ps.title,
|
||||
text: ps.text,
|
||||
imageUrl: ps.imageUrl,
|
||||
showPopup: ps.showPopup ?? false,
|
||||
isGoodNews: ps.isGoodNews ?? false
|
||||
}).then((x)=>Announcements.findOneByOrFail(x.identifiers[0]));
|
||||
publishBroadcastStream("announcementAdded", announcement);
|
||||
return Object.assign({}, announcement, {
|
||||
createdAt: announcement.createdAt.toISOString(),
|
||||
updatedAt: null
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import define from "../../../define.js";
|
||||
import { Announcements } from "../../../../../models/index.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
import { publishBroadcastStream } from "../../../../../services/stream.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
errors: {
|
||||
noSuchAnnouncement: {
|
||||
message: "No such announcement.",
|
||||
code: "NO_SUCH_ANNOUNCEMENT",
|
||||
id: "ecad8040-a276-4e85-bda9-015a708d291e"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"id"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const announcement = await Announcements.findOneBy({
|
||||
id: ps.id
|
||||
});
|
||||
if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement);
|
||||
publishBroadcastStream("announcementDeleted", announcement.id);
|
||||
await Announcements.delete(announcement.id);
|
||||
});
|
||||
@@ -0,0 +1,112 @@
|
||||
import { Announcements, AnnouncementReads } from "../../../../../models/index.js";
|
||||
import define from "../../../define.js";
|
||||
import { makePaginationQuery } from "../../../common/make-pagination-query.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx"
|
||||
},
|
||||
createdAt: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "date-time"
|
||||
},
|
||||
updatedAt: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
format: "date-time"
|
||||
},
|
||||
text: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
title: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
imageUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
reads: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
showPopup: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
},
|
||||
isGoodNews: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 10
|
||||
},
|
||||
sinceId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
untilId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const query = makePaginationQuery(Announcements.createQueryBuilder("announcement"), ps.sinceId, ps.untilId);
|
||||
const announcements = await query.take(ps.limit).getMany();
|
||||
const reads = new Map();
|
||||
for (const announcement of announcements){
|
||||
reads.set(announcement, await AnnouncementReads.countBy({
|
||||
announcementId: announcement.id
|
||||
}));
|
||||
}
|
||||
return announcements.map((announcement)=>({
|
||||
id: announcement.id,
|
||||
createdAt: announcement.createdAt.toISOString(),
|
||||
updatedAt: announcement.updatedAt?.toISOString() ?? null,
|
||||
title: announcement.title,
|
||||
text: announcement.text,
|
||||
imageUrl: announcement.imageUrl,
|
||||
reads: reads.get(announcement),
|
||||
showPopup: announcement.showPopup,
|
||||
isGoodNews: announcement.isGoodNews
|
||||
}));
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import define from "../../../define.js";
|
||||
import { Announcements } from "../../../../../models/index.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
errors: {
|
||||
noSuchAnnouncement: {
|
||||
message: "No such announcement.",
|
||||
code: "NO_SUCH_ANNOUNCEMENT",
|
||||
id: "d3aae5a7-6372-4cb4-b61c-f511ffc2d7cc"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
title: {
|
||||
type: "string",
|
||||
minLength: 1
|
||||
},
|
||||
text: {
|
||||
type: "string",
|
||||
minLength: 1
|
||||
},
|
||||
imageUrl: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
minLength: 1
|
||||
},
|
||||
showPopup: {
|
||||
type: "boolean"
|
||||
},
|
||||
isGoodNews: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"id",
|
||||
"title",
|
||||
"text",
|
||||
"imageUrl"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const announcement = await Announcements.findOneBy({
|
||||
id: ps.id
|
||||
});
|
||||
if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement);
|
||||
await Announcements.update(announcement.id, {
|
||||
updatedAt: new Date(),
|
||||
title: ps.title,
|
||||
text: ps.text,
|
||||
imageUrl: ps.imageUrl,
|
||||
showPopup: ps.showPopup ?? false,
|
||||
isGoodNews: ps.isGoodNews ?? false
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,194 @@
|
||||
import * as fs from "node:fs";
|
||||
import { mkdir, rm, stat, writeFile } from "node:fs/promises";
|
||||
import { spawn } from "node:child_process";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import archiver from "archiver";
|
||||
import config from "../../../../config/index.js";
|
||||
import define from "../../define.js";
|
||||
import { ApiError } from "../../error.js";
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
import { createTempDir } from "../../../../misc/create-temp.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
path: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
fileName: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
size: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
includedMedia: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
}
|
||||
},
|
||||
errors: {
|
||||
backupFailed: {
|
||||
message: "Failed to create backup.",
|
||||
code: "BACKUP_FAILED",
|
||||
id: "7498ab9f-4e1d-40d0-96d8-d8d1d82bd621"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
const rootDir = resolve(dirname(fileURLToPath(import.meta.url)), "../../../../../../..");
|
||||
function timestamp() {
|
||||
const now = new Date();
|
||||
const pad = (value)=>value.toString().padStart(2, "0");
|
||||
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
||||
}
|
||||
function run(command, args, env) {
|
||||
return new Promise((resolvePromise, reject)=>{
|
||||
const child = spawn(command, args, {
|
||||
stdio: [
|
||||
"ignore",
|
||||
"ignore",
|
||||
"pipe"
|
||||
],
|
||||
env
|
||||
});
|
||||
let stderr = "";
|
||||
child.stderr.on("data", (chunk)=>{
|
||||
stderr += chunk.toString();
|
||||
});
|
||||
child.on("error", reject);
|
||||
child.on("exit", (code)=>{
|
||||
if (code === 0) {
|
||||
resolvePromise();
|
||||
} else {
|
||||
reject(new Error(`${command} exited with code ${code}: ${stderr.trim()}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function archiveDirectory(sourceDir, outFile) {
|
||||
return new Promise((resolvePromise, reject)=>{
|
||||
const output = fs.createWriteStream(outFile);
|
||||
const archive = archiver("tar", {
|
||||
gzip: true,
|
||||
gzipOptions: {
|
||||
level: 6
|
||||
}
|
||||
});
|
||||
output.on("close", ()=>resolvePromise());
|
||||
archive.on("error", reject);
|
||||
archive.pipe(output);
|
||||
archive.directory(sourceDir, false);
|
||||
archive.finalize();
|
||||
});
|
||||
}
|
||||
async function pathExists(path) {
|
||||
return stat(path).then(()=>true).catch(()=>false);
|
||||
}
|
||||
export default define(meta, paramDef, async (_ps, me)=>{
|
||||
const [workDir, cleanup] = await createTempDir();
|
||||
const fileName = `iceshrimp-full-${timestamp()}.tar.gz`;
|
||||
const outDir = resolve(process.env.ICESHRIMP_BACKUP_DIR ?? `${rootDir}/backups`);
|
||||
const outFile = resolve(outDir, fileName);
|
||||
const dbDumpPath = resolve(workDir, "database.dump");
|
||||
const mediaDir = resolve(config.mediaDir);
|
||||
let includedMedia = false;
|
||||
try {
|
||||
await mkdir(outDir, {
|
||||
recursive: true
|
||||
});
|
||||
const env = {
|
||||
...process.env,
|
||||
PGHOST: config.db.host,
|
||||
PGPORT: String(config.db.port),
|
||||
PGDATABASE: config.db.db,
|
||||
PGUSER: config.db.user,
|
||||
PGPASSWORD: config.db.pass
|
||||
};
|
||||
await run("pg_dump", [
|
||||
"--format=custom",
|
||||
"--blobs",
|
||||
"--no-owner",
|
||||
"--file",
|
||||
dbDumpPath,
|
||||
config.db.db
|
||||
], env);
|
||||
const configDir = resolve(workDir, "config");
|
||||
await mkdir(configDir, {
|
||||
recursive: true
|
||||
});
|
||||
const configFiles = [
|
||||
process.env.ICESHRIMP_CONFIG ? resolve(process.env.ICESHRIMP_CONFIG) : resolve(rootDir, ".config/default.yml"),
|
||||
...process.env.ICESHRIMP_SECRETS ? [
|
||||
resolve(process.env.ICESHRIMP_SECRETS)
|
||||
] : []
|
||||
];
|
||||
for (const configFile of configFiles){
|
||||
if (await pathExists(configFile)) {
|
||||
fs.copyFileSync(configFile, resolve(configDir, configFile.split("/").pop()));
|
||||
}
|
||||
}
|
||||
if (await pathExists(mediaDir) && !outDir.startsWith(mediaDir + "/") && outDir !== mediaDir) {
|
||||
fs.cpSync(mediaDir, resolve(workDir, "files"), {
|
||||
recursive: true,
|
||||
dereference: false,
|
||||
errorOnExist: false
|
||||
});
|
||||
includedMedia = true;
|
||||
}
|
||||
await writeFile(resolve(workDir, "manifest.json"), `${JSON.stringify({
|
||||
type: "iceshrimp-full-backup",
|
||||
version: config.version,
|
||||
createdAt: new Date().toISOString(),
|
||||
host: config.host,
|
||||
database: config.db.db,
|
||||
included: {
|
||||
database: true,
|
||||
config: true,
|
||||
media: includedMedia
|
||||
},
|
||||
restore: "yarn full:restore <this archive>"
|
||||
}, null, 2)}\n`, "utf8");
|
||||
await archiveDirectory(workDir, outFile);
|
||||
const outStat = await stat(outFile);
|
||||
await insertModerationLog(me, "createBackup", {
|
||||
path: outFile,
|
||||
size: outStat.size,
|
||||
includedMedia
|
||||
});
|
||||
return {
|
||||
path: outFile,
|
||||
fileName,
|
||||
size: outStat.size,
|
||||
includedMedia
|
||||
};
|
||||
} catch (e) {
|
||||
await rm(outFile, {
|
||||
force: true
|
||||
}).catch(()=>{});
|
||||
throw new ApiError(meta.errors.backupFailed, {
|
||||
message: e instanceof Error ? e.message : String(e)
|
||||
});
|
||||
} finally{
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Users } from "../../../../models/index.js";
|
||||
import { deleteAccount } from "../../../../services/delete-account.js";
|
||||
import define from "../../define.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
res: {}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const user = await Users.findOneByOrFail({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user.isDeleted) {
|
||||
return;
|
||||
}
|
||||
await deleteAccount(user);
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import define from "../../define.js";
|
||||
import { deleteFile } from "../../../../services/drive/delete-file.js";
|
||||
import { DriveFiles } from "../../../../models/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const files = await DriveFiles.findBy({
|
||||
userId: ps.userId
|
||||
});
|
||||
for (const file of files){
|
||||
deleteFile(file);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import define from "../../define.js";
|
||||
import { Users } from "../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
import { publishInternalEvent } from "../../../../services/stream.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
overrideMb: {
|
||||
type: "number",
|
||||
nullable: true
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId",
|
||||
"overrideMb"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
if (!Users.isLocalUser(user)) {
|
||||
throw new Error("user is not local user");
|
||||
}
|
||||
await Users.update(user.id, {
|
||||
driveCapacityOverrideMb: ps.overrideMb
|
||||
});
|
||||
publishInternalEvent("localUserUpdated", {
|
||||
id: user.id
|
||||
});
|
||||
insertModerationLog(me, "change-drive-capacity-override", {
|
||||
targetId: user.id
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import define from "../../../define.js";
|
||||
import { createCleanRemoteFilesJob } from "../../../../../queue/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
createCleanRemoteFilesJob();
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { IsNull } from "typeorm";
|
||||
import define from "../../../define.js";
|
||||
import { deleteFile } from "../../../../../services/drive/delete-file.js";
|
||||
import { DriveFiles } from "../../../../../models/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const files = await DriveFiles.findBy({
|
||||
userId: IsNull()
|
||||
});
|
||||
for (const file of files){
|
||||
deleteFile(file);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
import { DriveFiles } from "../../../../../models/index.js";
|
||||
import define from "../../../define.js";
|
||||
import { makePaginationQuery } from "../../../common/make-pagination-query.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: false,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: "DriveFile"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 10
|
||||
},
|
||||
sinceId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
untilId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id",
|
||||
nullable: true
|
||||
},
|
||||
type: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1)
|
||||
},
|
||||
origin: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"combined",
|
||||
"local",
|
||||
"remote"
|
||||
],
|
||||
default: "local"
|
||||
},
|
||||
hostname: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
default: null,
|
||||
description: "The local host is represented with `null`."
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const query = makePaginationQuery(DriveFiles.createQueryBuilder("file"), ps.sinceId, ps.untilId);
|
||||
if (ps.userId) {
|
||||
query.andWhere("file.userId = :userId", {
|
||||
userId: ps.userId
|
||||
});
|
||||
} else {
|
||||
if (ps.origin === "local") {
|
||||
query.andWhere("file.userHost IS NULL");
|
||||
} else if (ps.origin === "remote") {
|
||||
query.andWhere("file.userHost IS NOT NULL");
|
||||
}
|
||||
if (ps.hostname) {
|
||||
query.andWhere("file.userHost = :hostname", {
|
||||
hostname: ps.hostname
|
||||
});
|
||||
}
|
||||
}
|
||||
if (ps.type) {
|
||||
if (ps.type.endsWith("/*")) {
|
||||
query.andWhere("file.type like :type", {
|
||||
type: `${ps.type.replace("/*", "/")}%`
|
||||
});
|
||||
} else {
|
||||
query.andWhere("file.type = :type", {
|
||||
type: ps.type
|
||||
});
|
||||
}
|
||||
}
|
||||
const files = await query.take(ps.limit).getMany();
|
||||
return await DriveFiles.packMany(files, {
|
||||
detail: true,
|
||||
withUser: true,
|
||||
self: true
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,227 @@
|
||||
import { DriveFiles } from "../../../../../models/index.js";
|
||||
import define from "../../../define.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
errors: {
|
||||
noSuchFile: {
|
||||
message: "No such file.",
|
||||
code: "NO_SUCH_FILE",
|
||||
id: "caf3ca38-c6e5-472e-a30c-b05377dcc240"
|
||||
}
|
||||
},
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx"
|
||||
},
|
||||
createdAt: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "date-time"
|
||||
},
|
||||
userId: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx"
|
||||
},
|
||||
userHost: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
description: "The local host is represented with `null`."
|
||||
},
|
||||
md5: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "md5",
|
||||
example: "15eca7fba0480996e2245f5185bf39f2"
|
||||
},
|
||||
name: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
example: "lenna.jpg"
|
||||
},
|
||||
type: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
example: "image/jpeg"
|
||||
},
|
||||
size: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
example: 51469
|
||||
},
|
||||
comment: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
blurhash: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
properties: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
width: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
example: 1280
|
||||
},
|
||||
height: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
example: 720
|
||||
},
|
||||
avgColor: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
example: "rgb(40,65,87)"
|
||||
}
|
||||
}
|
||||
},
|
||||
storedInternal: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
example: true
|
||||
},
|
||||
url: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
format: "url"
|
||||
},
|
||||
thumbnailUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
format: "url"
|
||||
},
|
||||
webpublicUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
format: "url"
|
||||
},
|
||||
accessKey: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
thumbnailAccessKey: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
webpublicAccessKey: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
uri: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
src: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
folderId: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx"
|
||||
},
|
||||
isSensitive: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
isLink: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
fileId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"fileId"
|
||||
]
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
url: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"url"
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const file = ps.fileId ? await DriveFiles.findOneBy({
|
||||
id: ps.fileId
|
||||
}) : await DriveFiles.findOne({
|
||||
where: [
|
||||
{
|
||||
url: ps.url
|
||||
},
|
||||
{
|
||||
thumbnailUrl: ps.url
|
||||
},
|
||||
{
|
||||
webpublicUrl: ps.url
|
||||
}
|
||||
]
|
||||
});
|
||||
if (file == null) {
|
||||
throw new ApiError(meta.errors.noSuchFile);
|
||||
}
|
||||
if (!me.isAdmin) {
|
||||
file.requestIp = undefined;
|
||||
file.requestHeaders = undefined;
|
||||
}
|
||||
return file;
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { In } from "typeorm";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
ids: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
aliases: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"ids",
|
||||
"aliases"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const emojis = await Emojis.findBy({
|
||||
id: In(ps.ids)
|
||||
});
|
||||
for (const emoji of emojis){
|
||||
await Emojis.update(emoji.id, {
|
||||
updatedAt: new Date(),
|
||||
aliases: [
|
||||
...new Set(emoji.aliases.concat(ps.aliases))
|
||||
]
|
||||
});
|
||||
}
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis, DriveFiles } from "../../../../../models/index.js";
|
||||
import { genId } from "../../../../../misc/gen-id.js";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
import rndstr from "rndstr";
|
||||
import { publishBroadcastStream } from "../../../../../services/stream.js";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
import { getEmojiSize } from "../../../../../misc/emoji-meta.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
errors: {
|
||||
noSuchFile: {
|
||||
message: "No such file.",
|
||||
code: "MO_SUCH_FILE",
|
||||
id: "fc46b5a4-6b92-4c33-ac66-b806659bb5cf"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
fileId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"fileId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const file = await DriveFiles.findOneBy({
|
||||
id: ps.fileId
|
||||
});
|
||||
if (file == null) throw new ApiError(meta.errors.noSuchFile);
|
||||
const name = file.name.split(".")[0].match(/^[a-z0-9_]+$/) ? file.name.split(".")[0] : `_${rndstr("a-z0-9", 8)}_`;
|
||||
const size = await getEmojiSize(file.url);
|
||||
const emoji = await Emojis.insert({
|
||||
id: genId(),
|
||||
updatedAt: new Date(),
|
||||
name: name,
|
||||
category: null,
|
||||
host: null,
|
||||
aliases: [],
|
||||
originalUrl: file.url,
|
||||
publicUrl: file.webpublicUrl ?? file.url,
|
||||
type: file.webpublicType ?? file.type,
|
||||
license: null,
|
||||
glyph: file.type === "image/svg+xml",
|
||||
width: size.width || null,
|
||||
height: size.height || null
|
||||
}).then((x)=>Emojis.findOneByOrFail(x.identifiers[0]));
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
publishBroadcastStream("emojiAdded", {
|
||||
emoji: await Emojis.pack(emoji.id)
|
||||
});
|
||||
insertModerationLog(me, "addEmoji", {
|
||||
emojiId: emoji.id
|
||||
});
|
||||
return {
|
||||
id: emoji.id
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { genId } from "../../../../../misc/gen-id.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
import { uploadFromUrl } from "../../../../../services/drive/upload-from-url.js";
|
||||
import { publishBroadcastStream } from "../../../../../services/stream.js";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
import { getEmojiSize } from "../../../../../misc/emoji-meta.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
errors: {
|
||||
noSuchEmoji: {
|
||||
message: "No such emoji.",
|
||||
code: "NO_SUCH_EMOJI",
|
||||
id: "e2785b66-dca3-4087-9cac-b93c541cc425"
|
||||
}
|
||||
},
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
emojiId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"emojiId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const emoji = await Emojis.findOneBy({
|
||||
id: ps.emojiId
|
||||
});
|
||||
if (emoji == null) {
|
||||
throw new ApiError(meta.errors.noSuchEmoji);
|
||||
}
|
||||
let driveFile;
|
||||
try {
|
||||
// Create file
|
||||
driveFile = await uploadFromUrl({
|
||||
url: emoji.originalUrl,
|
||||
user: null,
|
||||
force: true
|
||||
});
|
||||
} catch (e) {
|
||||
throw new ApiError();
|
||||
}
|
||||
const size = await getEmojiSize(driveFile.url);
|
||||
const copied = await Emojis.insert({
|
||||
id: genId(),
|
||||
updatedAt: new Date(),
|
||||
name: emoji.name,
|
||||
host: null,
|
||||
aliases: [],
|
||||
originalUrl: driveFile.url,
|
||||
publicUrl: driveFile.webpublicUrl ?? driveFile.url,
|
||||
type: driveFile.webpublicType ?? driveFile.type,
|
||||
license: emoji.license,
|
||||
glyph: emoji.glyph,
|
||||
width: size.width || null,
|
||||
height: size.height || null
|
||||
}).then((x)=>Emojis.findOneByOrFail(x.identifiers[0]));
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
publishBroadcastStream("emojiAdded", {
|
||||
emoji: await Emojis.pack(copied.id)
|
||||
});
|
||||
return {
|
||||
id: copied.id
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { In } from "typeorm";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
ids: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"ids"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const emojis = await Emojis.findBy({
|
||||
id: In(ps.ids)
|
||||
});
|
||||
for (const emoji of emojis){
|
||||
await Emojis.delete(emoji.id);
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
insertModerationLog(me, "deleteEmoji", {
|
||||
emoji: emoji
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
errors: {
|
||||
noSuchEmoji: {
|
||||
message: "No such emoji.",
|
||||
code: "NO_SUCH_EMOJI",
|
||||
id: "be83669b-773a-44b7-b1f8-e5e5170ac3c2"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"id"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const emoji = await Emojis.findOneBy({
|
||||
id: ps.id
|
||||
});
|
||||
if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji);
|
||||
await Emojis.delete(emoji.id);
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
insertModerationLog(me, "deleteEmoji", {
|
||||
emoji: emoji
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import define from "../../../define.js";
|
||||
import { createImportCustomEmojisJob } from "../../../../../queue/index.js";
|
||||
export const meta = {
|
||||
secure: true,
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
fileId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"fileId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, user)=>{
|
||||
createImportCustomEmojisJob(user, ps.fileId);
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { toPuny } from "../../../../../misc/convert-host.js";
|
||||
import { makePaginationQuery } from "../../../common/make-pagination-query.js";
|
||||
import { sqlLikeEscape } from "../../../../../misc/sql-like-escape.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id"
|
||||
},
|
||||
aliases: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
name: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
category: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
host: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true,
|
||||
description: "The local host is represented with `null`."
|
||||
},
|
||||
url: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
license: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
glyph: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
glyphUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
width: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
height: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
default: null
|
||||
},
|
||||
host: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
default: null,
|
||||
description: "Use `null` to represent the local host."
|
||||
},
|
||||
limit: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 10
|
||||
},
|
||||
sinceId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
untilId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const q = makePaginationQuery(Emojis.createQueryBuilder("emoji"), ps.sinceId, ps.untilId);
|
||||
if (ps.host == null) {
|
||||
q.andWhere("emoji.host IS NOT NULL");
|
||||
} else {
|
||||
q.andWhere("emoji.host = :host", {
|
||||
host: toPuny(ps.host)
|
||||
});
|
||||
}
|
||||
if (ps.query) {
|
||||
q.andWhere("emoji.name like :query", {
|
||||
query: `%${sqlLikeEscape(ps.query)}%`
|
||||
});
|
||||
}
|
||||
const emojis = await q.orderBy("emoji.id", "DESC").take(ps.limit).getMany();
|
||||
return Emojis.packMany(emojis);
|
||||
});
|
||||
@@ -0,0 +1,123 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { makePaginationQuery } from "../../../common/make-pagination-query.js";
|
||||
//import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id"
|
||||
},
|
||||
aliases: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
name: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
category: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
host: {
|
||||
type: "null",
|
||||
optional: false,
|
||||
description: "The local host is represented with `null`. The field exists for compatibility with other API endpoints that return files."
|
||||
},
|
||||
url: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
license: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
glyph: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
glyphUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
width: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
height: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
default: null
|
||||
},
|
||||
limit: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 10
|
||||
},
|
||||
sinceId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
untilId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const q = makePaginationQuery(Emojis.createQueryBuilder("emoji"), ps.sinceId, ps.untilId).andWhere("emoji.host IS NULL");
|
||||
let emojis;
|
||||
if (ps.query) {
|
||||
//q.andWhere('emoji.name ILIKE :q', { q: `%${sqlLikeEscape(ps.query)}%` });
|
||||
//const emojis = await q.take(ps.limit).getMany();
|
||||
emojis = await q.getMany();
|
||||
emojis = emojis.filter((emoji)=>emoji.name.includes(ps.query) || emoji.aliases.some((a)=>a.includes(ps.query)) || emoji.category?.includes(ps.query));
|
||||
emojis.splice(ps.limit + 1);
|
||||
} else {
|
||||
emojis = await q.take(ps.limit).getMany();
|
||||
}
|
||||
return Emojis.packMany(emojis);
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { In } from "typeorm";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
ids: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
aliases: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"ids",
|
||||
"aliases"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const emojis = await Emojis.findBy({
|
||||
id: In(ps.ids)
|
||||
});
|
||||
for (const emoji of emojis){
|
||||
await Emojis.update(emoji.id, {
|
||||
updatedAt: new Date(),
|
||||
aliases: emoji.aliases.filter((x)=>!ps.aliases.includes(x))
|
||||
});
|
||||
}
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { In } from "typeorm";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
ids: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
aliases: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"ids",
|
||||
"aliases"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
await Emojis.update({
|
||||
id: In(ps.ids)
|
||||
}, {
|
||||
updatedAt: new Date(),
|
||||
aliases: ps.aliases
|
||||
});
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { In } from "typeorm";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
ids: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
category: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "Use `null` to reset the category."
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"ids"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
await Emojis.update({
|
||||
id: In(ps.ids)
|
||||
}, {
|
||||
updatedAt: new Date(),
|
||||
category: ps.category
|
||||
});
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { In } from "typeorm";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
ids: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
license: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "Use `null` to reset the license."
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"ids"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
await Emojis.update({
|
||||
id: In(ps.ids)
|
||||
}, {
|
||||
updatedAt: new Date(),
|
||||
license: ps.license
|
||||
});
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import define from "../../../define.js";
|
||||
import { Emojis } from "../../../../../models/index.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
import { db } from "../../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
errors: {
|
||||
noSuchEmoji: {
|
||||
message: "No such emoji.",
|
||||
code: "NO_SUCH_EMOJI",
|
||||
id: "684dec9d-a8c2-4364-9aa8-456c49cb1dc8"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
name: {
|
||||
type: "string"
|
||||
},
|
||||
category: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "Use `null` to reset the category."
|
||||
},
|
||||
aliases: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
license: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
glyph: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"id",
|
||||
"name",
|
||||
"aliases"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const emoji = await Emojis.findOneBy({
|
||||
id: ps.id
|
||||
});
|
||||
if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji);
|
||||
await Emojis.update(emoji.id, {
|
||||
updatedAt: new Date(),
|
||||
name: ps.name,
|
||||
category: ps.category,
|
||||
aliases: ps.aliases,
|
||||
license: ps.license,
|
||||
...typeof ps.glyph === "boolean" ? {
|
||||
glyph: ps.glyph
|
||||
} : {}
|
||||
});
|
||||
await db.queryResultCache.remove([
|
||||
"meta_emojis"
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import define from "../../../define.js";
|
||||
import { deleteFile } from "../../../../../services/drive/delete-file.js";
|
||||
import { DriveFiles } from "../../../../../models/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
host: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"host"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const files = await DriveFiles.findBy({
|
||||
userHost: ps.host
|
||||
});
|
||||
for (const file of files){
|
||||
deleteFile(file);
|
||||
}
|
||||
});
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import define from "../../../define.js";
|
||||
import { Instances } from "../../../../../models/index.js";
|
||||
import { toPuny } from "../../../../../misc/convert-host.js";
|
||||
import { fetchInstanceMetadata } from "../../../../../services/fetch-instance-metadata.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
host: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"host"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const instance = await Instances.findOneBy({
|
||||
host: toPuny(ps.host)
|
||||
});
|
||||
if (instance == null) {
|
||||
throw new Error("instance not found");
|
||||
}
|
||||
fetchInstanceMetadata(instance, true);
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import define from "../../../define.js";
|
||||
import deleteFollowing from "../../../../../services/following/delete.js";
|
||||
import { Followings, Users } from "../../../../../models/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
host: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"host"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const followings = await Followings.findBy({
|
||||
followerHost: ps.host
|
||||
});
|
||||
const pairs = await Promise.all(followings.map((f)=>Promise.all([
|
||||
Users.findOneByOrFail({
|
||||
id: f.followerId
|
||||
}),
|
||||
Users.findOneByOrFail({
|
||||
id: f.followeeId
|
||||
})
|
||||
])));
|
||||
for (const pair of pairs){
|
||||
deleteFollowing(pair[0], pair[1]);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import define from "../../../define.js";
|
||||
import { Instances } from "../../../../../models/index.js";
|
||||
import { toPuny } from "../../../../../misc/convert-host.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
host: {
|
||||
type: "string"
|
||||
},
|
||||
isSuspended: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"host",
|
||||
"isSuspended"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const instance = await Instances.findOneBy({
|
||||
host: toPuny(ps.host)
|
||||
});
|
||||
if (instance == null) {
|
||||
throw new Error("instance not found");
|
||||
}
|
||||
Instances.update({
|
||||
host: toPuny(ps.host)
|
||||
}, {
|
||||
isSuspended: ps.isSuspended
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import define from "../../define.js";
|
||||
import { db } from "../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
tags: [
|
||||
"admin"
|
||||
]
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async ()=>{
|
||||
const stats = await db.query("SELECT * FROM pg_indexes;").then((recs)=>{
|
||||
const res = [];
|
||||
for (const rec of recs){
|
||||
res.push(rec);
|
||||
}
|
||||
return res;
|
||||
});
|
||||
return stats;
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import { db } from "../../../../db/postgre.js";
|
||||
import define from "../../define.js";
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
example: {
|
||||
migrations: {
|
||||
count: 66,
|
||||
size: 32768
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async ()=>{
|
||||
const sizes = await db.query(`
|
||||
SELECT relname AS "table", reltuples as "count", pg_total_relation_size(C.oid) AS "size"
|
||||
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
||||
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
|
||||
AND C.relkind <> 'i'
|
||||
AND nspname !~ '^pg_toast';`).then((recs)=>{
|
||||
const res = {};
|
||||
for (const rec of recs){
|
||||
res[rec.table] = {
|
||||
count: parseInt(rec.count, 10),
|
||||
size: parseInt(rec.size, 10)
|
||||
};
|
||||
}
|
||||
return res;
|
||||
});
|
||||
return sizes;
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { UserIps } from "../../../../models/index.js";
|
||||
import define from "../../define.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const ips = await UserIps.find({
|
||||
where: {
|
||||
userId: ps.userId
|
||||
},
|
||||
order: {
|
||||
createdAt: "DESC"
|
||||
},
|
||||
take: 30
|
||||
});
|
||||
return ips.map((x)=>({
|
||||
ip: x.ip,
|
||||
createdAt: x.createdAt.toISOString()
|
||||
}));
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import rndstr from "rndstr";
|
||||
import define from "../../define.js";
|
||||
import { RegistrationTickets } from "../../../../models/index.js";
|
||||
import { genId } from "../../../../misc/gen-id.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
code: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
example: "2ERUA5VR",
|
||||
maxLength: 8,
|
||||
minLength: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async ()=>{
|
||||
const code = rndstr({
|
||||
length: 8,
|
||||
chars: "2-9A-HJ-NP-Z"
|
||||
});
|
||||
await RegistrationTickets.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
code
|
||||
});
|
||||
return {
|
||||
code
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,503 @@
|
||||
import config from "../../../../config/index.js";
|
||||
import { fetchMeta } from "../../../../misc/fetch-meta.js";
|
||||
import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "../../../../const.js";
|
||||
import define from "../../define.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"meta"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
driveCapacityPerLocalUserMb: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
driveCapacityPerRemoteUserMb: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
lua4frozenDatabaseCapacityMb: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
cacheRemoteFiles: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
emailRequiredForSignup: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
enableHcaptcha: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
hcaptchaSiteKey: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
enableRecaptcha: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
recaptchaSiteKey: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
swPublickey: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
mascotImageUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
default: "/twemoji/1f440.svg"
|
||||
},
|
||||
bannerUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
errorImageUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
default: "/twemoji/1f480.svg"
|
||||
},
|
||||
iconUrl: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
maxNoteTextLength: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
maxCaptionTextLength: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
emojis: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id"
|
||||
},
|
||||
aliases: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
category: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
host: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: true
|
||||
},
|
||||
url: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "url"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
enableEmail: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
enableGithubIntegration: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
enableDiscordIntegration: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
translatorAvailable: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
recommendedInstances: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
pinnedUsers: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
customMOTD: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
customSplashIcons: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
hiddenTags: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
blockedHosts: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
silencedHosts: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
allowedHosts: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
},
|
||||
privateMode: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
secureMode: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
hcaptchaSecretKey: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
recaptchaSecretKey: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
summaryProxy: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
email: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
smtpSecure: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
},
|
||||
smtpHost: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
smtpPort: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
smtpUser: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
smtpPass: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
swPrivateKey: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
useObjectStorage: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
},
|
||||
objectStorageBaseUrl: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
objectStorageBucket: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
objectStoragePrefix: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
objectStorageEndpoint: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
objectStorageRegion: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
objectStoragePort: {
|
||||
type: "number",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
objectStorageAccessKey: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
objectStorageSecretKey: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
objectStorageUseSSL: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
},
|
||||
objectStorageUseProxy: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
},
|
||||
objectStorageSetPublicRead: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
},
|
||||
enableIpLogging: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
},
|
||||
enableActiveEmailValidation: {
|
||||
type: "boolean",
|
||||
optional: true,
|
||||
nullable: false
|
||||
},
|
||||
defaultReaction: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
experimentalFeatures: {
|
||||
type: "object",
|
||||
optional: true,
|
||||
nullable: true,
|
||||
properties: {
|
||||
postImports: {
|
||||
type: "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
enableServerMachineStats: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
enableIdenticonGeneration: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
donationLink: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
},
|
||||
autofollowedAccount: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
nullable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const instance = await fetchMeta(true);
|
||||
return {
|
||||
maintainerName: instance.maintainerName,
|
||||
maintainerEmail: instance.maintainerEmail,
|
||||
version: config.version,
|
||||
name: instance.name,
|
||||
uri: config.url,
|
||||
description: instance.description,
|
||||
langs: instance.langs,
|
||||
tosUrl: instance.ToSUrl,
|
||||
repositoryUrl: instance.repositoryUrl,
|
||||
feedbackUrl: instance.feedbackUrl,
|
||||
disableRegistration: instance.disableRegistration,
|
||||
disableLocalTimeline: instance.disableLocalTimeline,
|
||||
disableRecommendedTimeline: instance.disableRecommendedTimeline,
|
||||
disableGlobalTimeline: instance.disableGlobalTimeline,
|
||||
driveCapacityPerLocalUserMb: instance.localDriveCapacityMb,
|
||||
driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb,
|
||||
lua4frozenDatabaseCapacityMb: instance.lua4frozenDatabaseCapacityMb,
|
||||
emailRequiredForSignup: instance.emailRequiredForSignup,
|
||||
enableHcaptcha: instance.enableHcaptcha,
|
||||
hcaptchaSiteKey: instance.hcaptchaSiteKey,
|
||||
enableRecaptcha: instance.enableRecaptcha,
|
||||
recaptchaSiteKey: instance.recaptchaSiteKey,
|
||||
swPublickey: instance.swPublicKey,
|
||||
themeColor: instance.themeColor,
|
||||
mascotImageUrl: instance.mascotImageUrl,
|
||||
bannerUrl: instance.bannerUrl,
|
||||
errorImageUrl: instance.errorImageUrl,
|
||||
iconUrl: instance.iconUrl,
|
||||
backgroundImageUrl: instance.backgroundImageUrl,
|
||||
logoImageUrl: instance.logoImageUrl,
|
||||
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
|
||||
maxCaptionTextLength: MAX_CAPTION_TEXT_LENGTH,
|
||||
defaultLightTheme: instance.defaultLightTheme,
|
||||
defaultDarkTheme: instance.defaultDarkTheme,
|
||||
enableEmail: instance.enableEmail,
|
||||
translatorAvailable: instance.deeplAuthKey != null || instance.libreTranslateApiUrl != null,
|
||||
pinnedPages: instance.pinnedPages,
|
||||
pinnedClipId: instance.pinnedClipId,
|
||||
cacheRemoteFiles: instance.cacheRemoteFiles,
|
||||
defaultReaction: instance.defaultReaction,
|
||||
recommendedInstances: instance.recommendedInstances,
|
||||
pinnedUsers: instance.pinnedUsers,
|
||||
customMOTD: instance.customMOTD,
|
||||
customSplashIcons: instance.customSplashIcons,
|
||||
hiddenTags: instance.hiddenTags,
|
||||
blockedHosts: instance.blockedHosts,
|
||||
silencedHosts: instance.silencedHosts,
|
||||
allowedHosts: instance.allowedHosts,
|
||||
privateMode: instance.privateMode,
|
||||
secureMode: instance.secureMode,
|
||||
hcaptchaSecretKey: instance.hcaptchaSecretKey,
|
||||
recaptchaSecretKey: instance.recaptchaSecretKey,
|
||||
summalyProxy: instance.summalyProxy,
|
||||
email: instance.email,
|
||||
smtpSecure: instance.smtpSecure,
|
||||
smtpHost: instance.smtpHost,
|
||||
smtpPort: instance.smtpPort,
|
||||
smtpUser: instance.smtpUser,
|
||||
smtpPass: instance.smtpPass,
|
||||
swPrivateKey: instance.swPrivateKey,
|
||||
useObjectStorage: instance.useObjectStorage,
|
||||
objectStorageBaseUrl: instance.objectStorageBaseUrl,
|
||||
objectStorageBucket: instance.objectStorageBucket,
|
||||
objectStoragePrefix: instance.objectStoragePrefix,
|
||||
objectStorageEndpoint: instance.objectStorageEndpoint,
|
||||
objectStorageRegion: instance.objectStorageRegion,
|
||||
objectStoragePort: instance.objectStoragePort,
|
||||
objectStorageAccessKey: instance.objectStorageAccessKey,
|
||||
objectStorageSecretKey: instance.objectStorageSecretKey,
|
||||
objectStorageUseSSL: instance.objectStorageUseSSL,
|
||||
objectStorageUseProxy: instance.objectStorageUseProxy,
|
||||
objectStorageSetPublicRead: instance.objectStorageSetPublicRead,
|
||||
objectStorageS3ForcePathStyle: instance.objectStorageS3ForcePathStyle,
|
||||
deeplAuthKey: instance.deeplAuthKey,
|
||||
deeplIsPro: instance.deeplIsPro,
|
||||
libreTranslateApiUrl: instance.libreTranslateApiUrl,
|
||||
libreTranslateApiKey: instance.libreTranslateApiKey,
|
||||
enableIpLogging: instance.enableIpLogging,
|
||||
enableActiveEmailValidation: instance.enableActiveEmailValidation,
|
||||
experimentalFeatures: instance.experimentalFeatures,
|
||||
enableServerMachineStats: instance.enableServerMachineStats,
|
||||
enableIdenticonGeneration: instance.enableIdenticonGeneration,
|
||||
donationLink: instance.donationLink,
|
||||
autofollowedAccount: instance.autofollowedAccount
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import define from "../../../define.js";
|
||||
import { Users } from "../../../../../models/index.js";
|
||||
import { publishInternalEvent } from "../../../../../services/stream.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
if (user.isAdmin) {
|
||||
throw new Error("cannot mark as moderator if admin user");
|
||||
}
|
||||
await Users.update(user.id, {
|
||||
isModerator: true
|
||||
});
|
||||
publishInternalEvent("userChangeModeratorState", {
|
||||
id: user.id,
|
||||
isModerator: true
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import define from "../../../define.js";
|
||||
import { Users } from "../../../../../models/index.js";
|
||||
import { publishInternalEvent } from "../../../../../services/stream.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
await Users.update(user.id, {
|
||||
isModerator: false
|
||||
});
|
||||
publishInternalEvent("userChangeModeratorState", {
|
||||
id: user.id,
|
||||
isModerator: false
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import define from "../../../define.js";
|
||||
import { Plans } from "../../../../../models/index.js";
|
||||
import { genId } from "../../../../../misc/gen-id.js";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
maxLength: 128
|
||||
},
|
||||
icon: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
maxLength: 64
|
||||
},
|
||||
description: {
|
||||
type: "string",
|
||||
maxLength: 512,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"name",
|
||||
"icon"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const name = ps.name.trim();
|
||||
const icon = ps.icon.trim();
|
||||
const description = ps.description.trim();
|
||||
if (name === "") throw new Error("name is empty");
|
||||
if (icon === "") throw new Error("icon is empty");
|
||||
const exists = await Plans.findOneBy({
|
||||
name
|
||||
});
|
||||
if (exists) throw new Error("plan name already exists");
|
||||
const plan = await Plans.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
updatedAt: null,
|
||||
name,
|
||||
icon,
|
||||
description
|
||||
}).then((x)=>Plans.findOneByOrFail(x.identifiers[0]));
|
||||
insertModerationLog(me, "createPlan", {
|
||||
planId: plan.id,
|
||||
name
|
||||
});
|
||||
return await Plans.pack(plan);
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import define from "../../../define.js";
|
||||
import { Plans } from "../../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
planId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"planId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const plan = await Plans.findOneByOrFail({
|
||||
id: ps.planId
|
||||
});
|
||||
await Plans.delete(plan.id);
|
||||
insertModerationLog(me, "deletePlan", {
|
||||
planId: plan.id,
|
||||
name: plan.name
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import define from "../../../define.js";
|
||||
import { Plans } from "../../../../../models/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async ()=>{
|
||||
const plans = await Plans.find({
|
||||
order: {
|
||||
createdAt: "ASC"
|
||||
}
|
||||
});
|
||||
return await Plans.packMany(plans);
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
import define from "../../../define.js";
|
||||
import { Plans } from "../../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
planId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
name: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
maxLength: 128
|
||||
},
|
||||
icon: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
maxLength: 64
|
||||
},
|
||||
description: {
|
||||
type: "string",
|
||||
maxLength: 512,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"planId",
|
||||
"name",
|
||||
"icon"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const plan = await Plans.findOneByOrFail({
|
||||
id: ps.planId
|
||||
});
|
||||
const name = ps.name.trim();
|
||||
const icon = ps.icon.trim();
|
||||
const description = ps.description.trim();
|
||||
if (name === "") throw new Error("name is empty");
|
||||
if (icon === "") throw new Error("icon is empty");
|
||||
const exists = await Plans.findOneBy({
|
||||
name
|
||||
});
|
||||
if (exists && exists.id !== plan.id) throw new Error("plan name already exists");
|
||||
await Plans.update(plan.id, {
|
||||
updatedAt: new Date(),
|
||||
name,
|
||||
icon,
|
||||
description
|
||||
});
|
||||
insertModerationLog(me, "updatePlan", {
|
||||
planId: plan.id,
|
||||
name
|
||||
});
|
||||
return await Plans.pack(plan.id);
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import define from "../../../define.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
import { Notes, PromoNotes } from "../../../../../models/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
errors: {
|
||||
noSuchNote: {
|
||||
message: "No such note.",
|
||||
code: "NO_SUCH_NOTE",
|
||||
id: "ee449fbe-af2a-453b-9cae-cf2fe7c895fc"
|
||||
},
|
||||
alreadyPromoted: {
|
||||
message: "The note has already promoted.",
|
||||
code: "ALREADY_PROMOTED",
|
||||
id: "ae427aa2-7a41-484f-a18c-2c1104051604"
|
||||
},
|
||||
notAdService: {
|
||||
message: "The note must have #AdService.",
|
||||
code: "NOT_AD_SERVICE",
|
||||
id: "970a4d57-7b67-4540-80ea-f210041724ef"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
noteId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
expiresAt: {
|
||||
type: "integer"
|
||||
},
|
||||
credits: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100000,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"noteId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, user)=>{
|
||||
const note = await Notes.findOneBy({
|
||||
id: ps.noteId
|
||||
});
|
||||
if (note == null) {
|
||||
throw new ApiError(meta.errors.noSuchNote);
|
||||
}
|
||||
if (!note.tags.includes("adservice")) {
|
||||
throw new ApiError(meta.errors.notAdService);
|
||||
}
|
||||
const expiresAt = ps.expiresAt ? new Date(ps.expiresAt) : new Date(Date.now() + 30 * 86400000);
|
||||
const credits = ps.credits ?? 1;
|
||||
const exist = await PromoNotes.findOneBy({
|
||||
noteId: note.id
|
||||
});
|
||||
if (exist) {
|
||||
await PromoNotes.update(note.id, {
|
||||
expiresAt: exist.expiresAt.getTime() > expiresAt.getTime() ? exist.expiresAt : expiresAt,
|
||||
totalCredits: exist.totalCredits + credits,
|
||||
remainingCredits: exist.remainingCredits + credits
|
||||
});
|
||||
return;
|
||||
}
|
||||
await PromoNotes.insert({
|
||||
noteId: note.id,
|
||||
expiresAt,
|
||||
totalCredits: credits,
|
||||
remainingCredits: credits,
|
||||
userId: note.userId
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
import rndstr from "rndstr";
|
||||
import { Notes, PromoNotes } from "../../../../../models/index.js";
|
||||
import define from "../../../define.js";
|
||||
import { makePaginationQuery } from "../../../common/make-pagination-query.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 10
|
||||
},
|
||||
sinceId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
untilId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, user)=>{
|
||||
const query = makePaginationQuery(Notes.createQueryBuilder("note"), ps.sinceId, ps.untilId).andWhere(`'adservice' = ANY(note.tags)`).innerJoinAndSelect("note.user", "user").leftJoinAndSelect("note.reply", "reply").leftJoinAndSelect("note.renote", "renote").leftJoinAndSelect("reply.user", "replyUser").leftJoinAndSelect("renote.user", "renoteUser");
|
||||
const notes = await query.take(ps.limit).getMany();
|
||||
if (notes.length === 0) return [];
|
||||
const promos = await PromoNotes.findBy(notes.map((note)=>({
|
||||
noteId: note.id
|
||||
})));
|
||||
return Promise.all(notes.map(async (note)=>{
|
||||
const promo = promos.find((item)=>item.noteId === note.id);
|
||||
const expiredCredits = promo && promo.expiresAt.getTime() <= Date.now() ? promo.remainingCredits : 0;
|
||||
note._prId_ = rndstr("a-z0-9", 8);
|
||||
return {
|
||||
id: note.id,
|
||||
note: await Notes.pack(note, user),
|
||||
expiresAt: promo?.expiresAt.toISOString() ?? null,
|
||||
totalCredits: promo?.totalCredits ?? 0,
|
||||
remainingCredits: promo?.remainingCredits ?? 0,
|
||||
expiredCredits
|
||||
};
|
||||
}));
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import define from "../../../define.js";
|
||||
import { destroy } from "../../../../../queue/index.js";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
destroy();
|
||||
insertModerationLog(me, "clearQueue");
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
import { deliverQueue } from "../../../../../queue/queues.js";
|
||||
import { URL } from "node:url";
|
||||
import define from "../../../define.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
type: "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
example: [
|
||||
[
|
||||
"example.com",
|
||||
12
|
||||
]
|
||||
]
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const jobs = await deliverQueue.getJobs([
|
||||
"delayed"
|
||||
]);
|
||||
const res = [];
|
||||
for (const job of jobs){
|
||||
const host = new URL(job.data.to).host;
|
||||
if (res.find((x)=>x[0] === host)) {
|
||||
res.find((x)=>x[0] === host)[1]++;
|
||||
} else {
|
||||
res.push([
|
||||
host,
|
||||
1
|
||||
]);
|
||||
}
|
||||
}
|
||||
res.sort((a, b)=>b[1] - a[1]);
|
||||
return res;
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
import { URL } from "node:url";
|
||||
import define from "../../../define.js";
|
||||
import { inboxQueue } from "../../../../../queue/queues.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
type: "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
example: [
|
||||
[
|
||||
"example.com",
|
||||
12
|
||||
]
|
||||
]
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const jobs = await inboxQueue.getJobs([
|
||||
"delayed"
|
||||
]);
|
||||
const res = [];
|
||||
for (const job of jobs){
|
||||
const host = new URL(job.data.signature.keyId).host;
|
||||
if (res.find((x)=>x[0] === host)) {
|
||||
res.find((x)=>x[0] === host)[1]++;
|
||||
} else {
|
||||
res.push([
|
||||
host,
|
||||
1
|
||||
]);
|
||||
}
|
||||
}
|
||||
res.sort((a, b)=>b[1] - a[1]);
|
||||
return res;
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import { deliverQueue, inboxQueue, dbQueue, objectStorageQueue } from "../../../../../queue/queues.js";
|
||||
import define from "../../../define.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
deliver: {
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: "QueueCount"
|
||||
},
|
||||
inbox: {
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: "QueueCount"
|
||||
},
|
||||
db: {
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: "QueueCount"
|
||||
},
|
||||
objectStorage: {
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: "QueueCount"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const deliverJobCounts = await deliverQueue.getJobCounts();
|
||||
const inboxJobCounts = await inboxQueue.getJobCounts();
|
||||
const dbJobCounts = await dbQueue.getJobCounts();
|
||||
const objectStorageJobCounts = await objectStorageQueue.getJobCounts();
|
||||
return {
|
||||
deliver: deliverJobCounts,
|
||||
inbox: inboxJobCounts,
|
||||
db: dbJobCounts,
|
||||
objectStorage: objectStorageJobCounts
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import { URL } from "node:url";
|
||||
import define from "../../../define.js";
|
||||
import { addRelay } from "../../../../../services/relay.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
errors: {
|
||||
invalidUrl: {
|
||||
message: "Invalid URL",
|
||||
code: "INVALID_URL",
|
||||
id: "fb8c92d3-d4e5-44e7-b3d4-800d5cef8b2c"
|
||||
}
|
||||
},
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id"
|
||||
},
|
||||
inbox: {
|
||||
description: "URL of the inbox, must be a https scheme URL",
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "url"
|
||||
},
|
||||
status: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
default: "requesting",
|
||||
enum: [
|
||||
"requesting",
|
||||
"accepted",
|
||||
"rejected"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
inbox: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"inbox"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, user)=>{
|
||||
try {
|
||||
if (new URL(ps.inbox).protocol !== "https:") throw new Error("https only");
|
||||
} catch {
|
||||
throw new ApiError(meta.errors.invalidUrl);
|
||||
}
|
||||
return await addRelay(ps.inbox);
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
import define from "../../../define.js";
|
||||
import { listRelay } from "../../../../../services/relay.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id"
|
||||
},
|
||||
inbox: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "url"
|
||||
},
|
||||
status: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
default: "requesting",
|
||||
enum: [
|
||||
"requesting",
|
||||
"accepted",
|
||||
"rejected"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, user)=>{
|
||||
return await listRelay();
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import define from "../../../define.js";
|
||||
import { removeRelay } from "../../../../../services/relay.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
inbox: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"inbox"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, user)=>{
|
||||
return await removeRelay(ps.inbox);
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
import define from "../../define.js";
|
||||
// import bcrypt from "bcryptjs";
|
||||
import rndstr from "rndstr";
|
||||
import { Users, UserProfiles } from "../../../../models/index.js";
|
||||
import { hashPassword } from "../../../../misc/password.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
password: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
minLength: 8,
|
||||
maxLength: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
if (user.isAdmin) {
|
||||
throw new Error("cannot reset password of admin");
|
||||
}
|
||||
const passwd = rndstr("a-zA-Z0-9", 8);
|
||||
// Generate hash of password
|
||||
// const hash = bcrypt.hashSync(passwd);
|
||||
const hash = await hashPassword(passwd);
|
||||
await UserProfiles.update({
|
||||
userId: user.id
|
||||
}, {
|
||||
password: hash
|
||||
});
|
||||
return {
|
||||
password: passwd
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import define from "../../define.js";
|
||||
import { AbuseUserReports, Users } from "../../../../models/index.js";
|
||||
import { getInstanceActor } from "../../../../services/instance-actor.js";
|
||||
import { deliver } from "../../../../queue/index.js";
|
||||
import { renderActivity } from "../../../../remote/activitypub/renderer/index.js";
|
||||
import { renderFlag } from "../../../../remote/activitypub/renderer/flag.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
reportId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
forward: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"reportId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const report = await AbuseUserReports.findOneByOrFail({
|
||||
id: ps.reportId
|
||||
});
|
||||
if (report == null) {
|
||||
throw new Error("report not found");
|
||||
}
|
||||
if (ps.forward && report.targetUserHost != null) {
|
||||
const actor = await getInstanceActor();
|
||||
const targetUser = await Users.findOneByOrFail({
|
||||
id: report.targetUserId
|
||||
});
|
||||
deliver(actor, renderActivity(renderFlag(actor, [
|
||||
targetUser.uri
|
||||
], report.comment)), targetUser.inbox);
|
||||
}
|
||||
await AbuseUserReports.update(report.id, {
|
||||
resolved: true,
|
||||
assigneeId: me.id,
|
||||
forwarded: ps.forward && report.targetUserHost != null
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import define from "../../define.js";
|
||||
import { Users, VerifiedBadgeRequests } from "../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
requestId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
approve: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"requestId",
|
||||
"approve"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const request = await VerifiedBadgeRequests.findOneByOrFail({
|
||||
id: ps.requestId
|
||||
});
|
||||
if (request.status !== "pending") {
|
||||
throw new Error("request already resolved");
|
||||
}
|
||||
if (ps.approve) {
|
||||
await Users.update(request.userId, {
|
||||
isVerified: true
|
||||
});
|
||||
}
|
||||
await VerifiedBadgeRequests.update(request.id, {
|
||||
status: ps.approve ? "approved" : "rejected",
|
||||
resolvedAt: new Date(),
|
||||
resolverId: me.id
|
||||
});
|
||||
insertModerationLog(me, ps.approve ? "approveVerifiedBadgeRequest" : "rejectVerifiedBadgeRequest", {
|
||||
targetId: request.userId,
|
||||
requestId: request.id
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import define from "../../define.js";
|
||||
import { sendEmail } from "../../../../services/send-email.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
to: {
|
||||
type: "string"
|
||||
},
|
||||
subject: {
|
||||
type: "string"
|
||||
},
|
||||
text: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"to",
|
||||
"subject",
|
||||
"text"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
await sendEmail(ps.to, ps.subject, ps.text, ps.text);
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
import * as sanitizeHtml from "sanitize-html";
|
||||
import define from "../../define.js";
|
||||
import { Users, UserProfiles } from "../../../../models/index.js";
|
||||
import { ApiError } from "../../error.js";
|
||||
import { sendEmail } from "../../../../services/send-email.js";
|
||||
import { createNotification } from "../../../../services/create-notification.js";
|
||||
import config from "../../../../config/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"users"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
description: "Send a moderation notice.",
|
||||
errors: {
|
||||
noSuchUser: {
|
||||
message: "No such user.",
|
||||
code: "NO_SUCH_USER",
|
||||
id: "1acefcb5-0959-43fd-9685-b48305736cb5"
|
||||
},
|
||||
noEmail: {
|
||||
message: "No email for user.",
|
||||
code: "NO_EMAIL",
|
||||
id: "ac9d2d22-ef73-11ed-a05b-0242ac120003"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
comment: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
maxLength: 2048
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId",
|
||||
"comment"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const [user, profile] = await Promise.all([
|
||||
Users.findOneBy({
|
||||
id: ps.userId
|
||||
}),
|
||||
UserProfiles.findOneBy({
|
||||
userId: ps.userId
|
||||
})
|
||||
]);
|
||||
if (user == null || profile == null) {
|
||||
throw new ApiError(meta.errors.noSuchUser);
|
||||
}
|
||||
createNotification(user.id, "app", {
|
||||
customBody: ps.comment,
|
||||
customHeader: "Moderation Notice",
|
||||
customIcon: config?.images?.info
|
||||
});
|
||||
setImmediate(async ()=>{
|
||||
const email = profile.email;
|
||||
if (email == null) {
|
||||
throw new ApiError(meta.errors.noEmail);
|
||||
}
|
||||
sendEmail(email, "Moderation notice", sanitizeHtml(ps.comment), sanitizeHtml(ps.comment));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,137 @@
|
||||
import * as os from "node:os";
|
||||
import si from "systeminformation";
|
||||
import define from "../../define.js";
|
||||
import { redisClient } from "../../../../db/redis.js";
|
||||
import { db } from "../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
tags: [
|
||||
"admin",
|
||||
"meta"
|
||||
],
|
||||
res: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
machine: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
os: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
example: "linux"
|
||||
},
|
||||
node: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
psql: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
cpu: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
model: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
cores: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false
|
||||
}
|
||||
}
|
||||
},
|
||||
mem: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
total: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "bytes"
|
||||
}
|
||||
}
|
||||
},
|
||||
fs: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
total: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "bytes"
|
||||
},
|
||||
used: {
|
||||
type: "number",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "bytes"
|
||||
}
|
||||
}
|
||||
},
|
||||
net: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
interface: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
example: "eth0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async ()=>{
|
||||
const memStats = await si.mem();
|
||||
const fsStats = await si.fsSize();
|
||||
const netInterface = await si.networkInterfaceDefault();
|
||||
const redisServerInfo = await redisClient.info("Server");
|
||||
const m = redisServerInfo.match(new RegExp("^redis_version:(.*)", "m"));
|
||||
const redis_version = m?.[1];
|
||||
return {
|
||||
machine: os.hostname(),
|
||||
os: os.platform(),
|
||||
node: process.version,
|
||||
psql: await db.query("SHOW server_version").then((x)=>x[0].server_version),
|
||||
redis: redis_version,
|
||||
cpu: {
|
||||
model: os.cpus()[0].model,
|
||||
cores: os.cpus().length
|
||||
},
|
||||
mem: {
|
||||
total: memStats.total
|
||||
},
|
||||
fs: {
|
||||
total: fsStats[0].size,
|
||||
used: fsStats[0].used
|
||||
},
|
||||
net: {
|
||||
interface: netInterface
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import define from "../../define.js";
|
||||
import { Users } from "../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
isVerified: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId",
|
||||
"isVerified"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
await Users.update(user.id, {
|
||||
isVerified: ps.isVerified
|
||||
});
|
||||
insertModerationLog(me, ps.isVerified ? "markAsVerified" : "unmarkAsVerified", {
|
||||
targetId: user.id
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
import define from "../../define.js";
|
||||
import { ModerationLogs } from "../../../../models/index.js";
|
||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id"
|
||||
},
|
||||
createdAt: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "date-time"
|
||||
},
|
||||
type: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
info: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false
|
||||
},
|
||||
userId: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id"
|
||||
},
|
||||
user: {
|
||||
type: "object",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
ref: "UserDetailed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 10
|
||||
},
|
||||
sinceId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
untilId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const query = makePaginationQuery(ModerationLogs.createQueryBuilder("report"), ps.sinceId, ps.untilId);
|
||||
const reports = await query.take(ps.limit).getMany();
|
||||
return await ModerationLogs.packMany(reports);
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Signins, UserProfiles, Users } from "../../../../models/index.js";
|
||||
import define from "../../define.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "object",
|
||||
nullable: false,
|
||||
optional: false
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const [user, profile] = await Promise.all([
|
||||
Users.findOneBy({
|
||||
id: ps.userId
|
||||
}),
|
||||
UserProfiles.findOneBy({
|
||||
userId: ps.userId
|
||||
})
|
||||
]);
|
||||
if (user == null || profile == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
const _me = await Users.findOneByOrFail({
|
||||
id: me.id
|
||||
});
|
||||
if (_me.isModerator && !_me.isAdmin && user.isAdmin) {
|
||||
throw new Error("cannot show info of admin");
|
||||
}
|
||||
if (!_me.isAdmin) {
|
||||
return {
|
||||
isModerator: user.isModerator,
|
||||
isSilenced: user.isSilenced,
|
||||
isSuspended: user.isSuspended,
|
||||
moderationNote: profile.moderationNote
|
||||
};
|
||||
}
|
||||
const maskedKeys = [
|
||||
"accessToken",
|
||||
"accessTokenSecret",
|
||||
"refreshToken"
|
||||
];
|
||||
Object.keys(profile.integrations).forEach((integration)=>{
|
||||
maskedKeys.forEach((key)=>profile.integrations[integration][key] = "<MASKED>");
|
||||
});
|
||||
const signins = await Signins.findBy({
|
||||
userId: user.id
|
||||
});
|
||||
return {
|
||||
email: profile.email,
|
||||
emailVerified: profile.emailVerified,
|
||||
autoAcceptFollowed: profile.autoAcceptFollowed,
|
||||
noCrawle: profile.noCrawle,
|
||||
preventAiLearning: profile.preventAiLearning,
|
||||
alwaysMarkNsfw: profile.alwaysMarkNsfw,
|
||||
carefulBot: profile.carefulBot,
|
||||
injectFeaturedNote: profile.injectFeaturedNote,
|
||||
receiveAnnouncementEmail: profile.receiveAnnouncementEmail,
|
||||
integrations: profile.integrations,
|
||||
mutedWords: profile.mutedWords,
|
||||
mutedInstances: profile.mutedInstances,
|
||||
mutingNotificationTypes: profile.mutingNotificationTypes,
|
||||
isModerator: user.isModerator,
|
||||
isVerified: user.isVerified,
|
||||
isSilenced: user.isSilenced,
|
||||
isSuspended: user.isSuspended,
|
||||
lastActiveDate: user.lastActiveDate,
|
||||
moderationNote: profile.moderationNote,
|
||||
signins
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,161 @@
|
||||
import { Users } from "../../../../models/index.js";
|
||||
import define from "../../define.js";
|
||||
import { sqlLikeEscape } from "../../../../misc/sql-like-escape.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
res: {
|
||||
type: "array",
|
||||
nullable: false,
|
||||
optional: false,
|
||||
items: {
|
||||
type: "object",
|
||||
nullable: false,
|
||||
optional: false,
|
||||
ref: "UserDetailed"
|
||||
}
|
||||
}
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 10
|
||||
},
|
||||
offset: {
|
||||
type: "integer",
|
||||
default: 0
|
||||
},
|
||||
sort: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"+follower",
|
||||
"-follower",
|
||||
"+createdAt",
|
||||
"-createdAt",
|
||||
"+updatedAt",
|
||||
"-updatedAt"
|
||||
]
|
||||
},
|
||||
state: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"all",
|
||||
"alive",
|
||||
"available",
|
||||
"admin",
|
||||
"moderator",
|
||||
"adminOrModerator",
|
||||
"silenced",
|
||||
"suspended",
|
||||
"verified"
|
||||
],
|
||||
default: "all"
|
||||
},
|
||||
origin: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"combined",
|
||||
"local",
|
||||
"remote"
|
||||
],
|
||||
default: "combined"
|
||||
},
|
||||
username: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
default: null
|
||||
},
|
||||
hostname: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
default: null,
|
||||
description: "The local host is represented with `null`."
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const query = Users.createQueryBuilder("user");
|
||||
switch(ps.state){
|
||||
case "available":
|
||||
query.where("user.isSuspended = FALSE");
|
||||
break;
|
||||
case "admin":
|
||||
query.where("user.isAdmin = TRUE");
|
||||
break;
|
||||
case "moderator":
|
||||
query.where("user.isModerator = TRUE");
|
||||
break;
|
||||
case "adminOrModerator":
|
||||
query.where("user.isAdmin = TRUE OR user.isModerator = TRUE");
|
||||
break;
|
||||
case "alive":
|
||||
query.where("user.updatedAt > :date", {
|
||||
date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5)
|
||||
});
|
||||
break;
|
||||
case "silenced":
|
||||
query.where("user.isSilenced = TRUE");
|
||||
break;
|
||||
case "suspended":
|
||||
query.where("user.isSuspended = TRUE");
|
||||
break;
|
||||
case "verified":
|
||||
query.where("user.isVerified = TRUE");
|
||||
break;
|
||||
}
|
||||
switch(ps.origin){
|
||||
case "local":
|
||||
query.andWhere("user.host IS NULL");
|
||||
break;
|
||||
case "remote":
|
||||
query.andWhere("user.host IS NOT NULL");
|
||||
break;
|
||||
}
|
||||
if (ps.username) {
|
||||
query.andWhere("user.usernameLower like :username", {
|
||||
username: `${sqlLikeEscape(ps.username.toLowerCase())}%`
|
||||
});
|
||||
}
|
||||
if (ps.hostname) {
|
||||
query.andWhere("user.host = :hostname", {
|
||||
hostname: ps.hostname.toLowerCase()
|
||||
});
|
||||
}
|
||||
switch(ps.sort){
|
||||
case "+follower":
|
||||
query.orderBy("user.followersCount", "DESC");
|
||||
break;
|
||||
case "-follower":
|
||||
query.orderBy("user.followersCount", "ASC");
|
||||
break;
|
||||
case "+createdAt":
|
||||
query.orderBy("user.createdAt", "DESC");
|
||||
break;
|
||||
case "-createdAt":
|
||||
query.orderBy("user.createdAt", "ASC");
|
||||
break;
|
||||
case "+updatedAt":
|
||||
query.orderBy("user.updatedAt", "DESC", "NULLS LAST");
|
||||
break;
|
||||
case "-updatedAt":
|
||||
query.orderBy("user.updatedAt", "ASC", "NULLS FIRST");
|
||||
break;
|
||||
default:
|
||||
query.orderBy("user.id", "ASC");
|
||||
break;
|
||||
}
|
||||
query.take(ps.limit);
|
||||
query.skip(ps.offset);
|
||||
const users = await query.getMany();
|
||||
return await Users.packMany(users, me, {
|
||||
detail: true
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import define from "../../define.js";
|
||||
import { Users } from "../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
import { publishInternalEvent } from "../../../../services/stream.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
if (user.isAdmin) {
|
||||
throw new Error("cannot silence admin");
|
||||
}
|
||||
await Users.update(user.id, {
|
||||
isSilenced: true
|
||||
});
|
||||
publishInternalEvent("userChangeSilencedState", {
|
||||
id: user.id,
|
||||
isSilenced: true
|
||||
});
|
||||
insertModerationLog(me, "silence", {
|
||||
targetId: user.id
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
import define from "../../define.js";
|
||||
import deleteFollowing from "../../../../services/following/delete.js";
|
||||
import { Users, Followings, Notifications } from "../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
import { doPostSuspend } from "../../../../services/suspend-user.js";
|
||||
import { publishUserEvent } from "../../../../services/stream.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
if (user.isAdmin) {
|
||||
throw new Error("cannot suspend admin");
|
||||
}
|
||||
if (user.isModerator) {
|
||||
throw new Error("cannot suspend moderator");
|
||||
}
|
||||
await Users.update(user.id, {
|
||||
isSuspended: true
|
||||
});
|
||||
insertModerationLog(me, "suspend", {
|
||||
targetId: user.id
|
||||
});
|
||||
// Terminate streaming
|
||||
if (Users.isLocalUser(user)) {
|
||||
publishUserEvent(user.id, "terminate", {});
|
||||
}
|
||||
(async ()=>{
|
||||
await doPostSuspend(user).catch((e)=>{});
|
||||
await unFollowAll(user).catch((e)=>{});
|
||||
await readAllNotify(user).catch((e)=>{});
|
||||
})();
|
||||
});
|
||||
async function unFollowAll(follower) {
|
||||
const followings = await Followings.findBy({
|
||||
followerId: follower.id
|
||||
});
|
||||
for (const following of followings){
|
||||
const followee = await Users.findOneBy({
|
||||
id: following.followeeId
|
||||
});
|
||||
if (followee == null) {
|
||||
throw new Error(`Cant find followee ${following.followeeId}`);
|
||||
}
|
||||
await deleteFollowing(follower, followee, true);
|
||||
}
|
||||
}
|
||||
async function readAllNotify(notifier) {
|
||||
await Notifications.update({
|
||||
notifierId: notifier.id,
|
||||
isRead: false
|
||||
}, {
|
||||
isRead: true
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import define from "../../define.js";
|
||||
import { Users } from "../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
import { publishInternalEvent } from "../../../../services/stream.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
await Users.update(user.id, {
|
||||
isSilenced: false
|
||||
});
|
||||
publishInternalEvent("userChangeSilencedState", {
|
||||
id: user.id,
|
||||
isSilenced: false
|
||||
});
|
||||
insertModerationLog(me, "unsilence", {
|
||||
targetId: user.id
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import define from "../../define.js";
|
||||
import { Users } from "../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
import { doPostUnsuspend } from "../../../../services/unsuspend-user.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
await Users.update(user.id, {
|
||||
isSuspended: false
|
||||
});
|
||||
insertModerationLog(me, "unsuspend", {
|
||||
targetId: user.id
|
||||
});
|
||||
doPostUnsuspend(user);
|
||||
});
|
||||
@@ -0,0 +1,671 @@
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
import define from "../../define.js";
|
||||
import { Metas } from "../../../../models/index.js";
|
||||
import { Users } from "../../../../models/index.js";
|
||||
import { IsNull } from "typeorm";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
disableRegistration: {
|
||||
type: "boolean",
|
||||
nullable: true
|
||||
},
|
||||
disableLocalTimeline: {
|
||||
type: "boolean",
|
||||
nullable: true
|
||||
},
|
||||
disableRecommendedTimeline: {
|
||||
type: "boolean",
|
||||
nullable: true
|
||||
},
|
||||
disableGlobalTimeline: {
|
||||
type: "boolean",
|
||||
nullable: true
|
||||
},
|
||||
defaultReaction: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
recommendedInstances: {
|
||||
type: "array",
|
||||
nullable: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
pinnedUsers: {
|
||||
type: "array",
|
||||
nullable: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
customMOTD: {
|
||||
type: "array",
|
||||
nullable: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
customSplashIcons: {
|
||||
type: "array",
|
||||
nullable: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
hiddenTags: {
|
||||
type: "array",
|
||||
nullable: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
blockedHosts: {
|
||||
type: "array",
|
||||
nullable: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
silencedHosts: {
|
||||
type: "array",
|
||||
nullable: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
allowedHosts: {
|
||||
type: "array",
|
||||
nullable: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
secureMode: {
|
||||
type: "boolean",
|
||||
nullable: true
|
||||
},
|
||||
privateMode: {
|
||||
type: "boolean",
|
||||
nullable: true
|
||||
},
|
||||
themeColor: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
pattern: "^#[0-9a-fA-F]{6}$"
|
||||
},
|
||||
mascotImageUrl: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
bannerUrl: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
logoImageUrl: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
errorImageUrl: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
iconUrl: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
backgroundImageUrl: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
name: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
description: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
defaultLightTheme: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
defaultDarkTheme: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
localDriveCapacityMb: {
|
||||
type: "integer"
|
||||
},
|
||||
remoteDriveCapacityMb: {
|
||||
type: "integer"
|
||||
},
|
||||
lua4frozenDatabaseCapacityMb: {
|
||||
type: "integer"
|
||||
},
|
||||
cacheRemoteFiles: {
|
||||
type: "boolean"
|
||||
},
|
||||
emailRequiredForSignup: {
|
||||
type: "boolean"
|
||||
},
|
||||
enableHcaptcha: {
|
||||
type: "boolean"
|
||||
},
|
||||
hcaptchaSiteKey: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
hcaptchaSecretKey: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
enableRecaptcha: {
|
||||
type: "boolean"
|
||||
},
|
||||
recaptchaSiteKey: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
recaptchaSecretKey: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
maintainerName: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
maintainerEmail: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
pinnedPages: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
pinnedClipId: {
|
||||
type: "string",
|
||||
format: "misskey:id",
|
||||
nullable: true
|
||||
},
|
||||
langs: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
summalyProxy: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
deeplAuthKey: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
deeplIsPro: {
|
||||
type: "boolean"
|
||||
},
|
||||
libreTranslateApiUrl: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
libreTranslateApiKey: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
enableGithubIntegration: {
|
||||
type: "boolean"
|
||||
},
|
||||
githubClientId: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
githubClientSecret: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
enableDiscordIntegration: {
|
||||
type: "boolean"
|
||||
},
|
||||
discordClientId: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
discordClientSecret: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
enableEmail: {
|
||||
type: "boolean"
|
||||
},
|
||||
email: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
smtpSecure: {
|
||||
type: "boolean"
|
||||
},
|
||||
smtpHost: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
smtpPort: {
|
||||
type: "integer",
|
||||
nullable: true
|
||||
},
|
||||
smtpUser: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
smtpPass: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
tosUrl: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
repositoryUrl: {
|
||||
type: "string"
|
||||
},
|
||||
feedbackUrl: {
|
||||
type: "string"
|
||||
},
|
||||
useObjectStorage: {
|
||||
type: "boolean"
|
||||
},
|
||||
objectStorageBaseUrl: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
objectStorageBucket: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
objectStoragePrefix: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
objectStorageEndpoint: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
objectStorageRegion: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
objectStoragePort: {
|
||||
type: "integer",
|
||||
nullable: true
|
||||
},
|
||||
objectStorageAccessKey: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
objectStorageSecretKey: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
objectStorageUseSSL: {
|
||||
type: "boolean"
|
||||
},
|
||||
objectStorageUseProxy: {
|
||||
type: "boolean"
|
||||
},
|
||||
objectStorageSetPublicRead: {
|
||||
type: "boolean"
|
||||
},
|
||||
objectStorageS3ForcePathStyle: {
|
||||
type: "boolean"
|
||||
},
|
||||
enableIpLogging: {
|
||||
type: "boolean"
|
||||
},
|
||||
enableActiveEmailValidation: {
|
||||
type: "boolean"
|
||||
},
|
||||
experimentalFeatures: {
|
||||
type: "object",
|
||||
nullable: true,
|
||||
properties: {
|
||||
postImports: {
|
||||
type: "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
enableServerMachineStats: {
|
||||
type: "boolean"
|
||||
},
|
||||
enableIdenticonGeneration: {
|
||||
type: "boolean"
|
||||
},
|
||||
donationLink: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
},
|
||||
autofollowedAccount: {
|
||||
type: "string",
|
||||
nullable: true
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const set = {};
|
||||
if (typeof ps.disableRegistration === "boolean") {
|
||||
set.disableRegistration = ps.disableRegistration;
|
||||
}
|
||||
if (typeof ps.disableLocalTimeline === "boolean") {
|
||||
set.disableLocalTimeline = ps.disableLocalTimeline;
|
||||
}
|
||||
if (typeof ps.disableRecommendedTimeline === "boolean") {
|
||||
set.disableRecommendedTimeline = ps.disableRecommendedTimeline;
|
||||
}
|
||||
if (typeof ps.disableGlobalTimeline === "boolean") {
|
||||
set.disableGlobalTimeline = ps.disableGlobalTimeline;
|
||||
}
|
||||
if (typeof ps.defaultReaction === "string") {
|
||||
set.defaultReaction = ps.defaultReaction;
|
||||
}
|
||||
if (Array.isArray(ps.pinnedUsers)) {
|
||||
set.pinnedUsers = ps.pinnedUsers.filter(Boolean);
|
||||
}
|
||||
if (Array.isArray(ps.customMOTD)) {
|
||||
set.customMOTD = ps.customMOTD.filter(Boolean);
|
||||
}
|
||||
if (Array.isArray(ps.customSplashIcons)) {
|
||||
set.customSplashIcons = ps.customSplashIcons.filter(Boolean);
|
||||
}
|
||||
if (Array.isArray(ps.recommendedInstances)) {
|
||||
set.recommendedInstances = ps.recommendedInstances.filter(Boolean);
|
||||
if (set.recommendedInstances?.length > 0) {
|
||||
set.recommendedInstances.forEach((instance, index)=>{
|
||||
if (/^https?:\/\//i.test(instance)) {
|
||||
set.recommendedInstances[index] = instance.replace(/^https?:\/\//i, "").replace(/\/$/, "");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (Array.isArray(ps.hiddenTags)) {
|
||||
set.hiddenTags = ps.hiddenTags.filter(Boolean);
|
||||
}
|
||||
if (Array.isArray(ps.blockedHosts)) {
|
||||
let lastValue = "";
|
||||
set.blockedHosts = ps.blockedHosts.sort().filter((h)=>{
|
||||
const lv = lastValue;
|
||||
lastValue = h;
|
||||
return h !== "" && h !== lv;
|
||||
});
|
||||
}
|
||||
if (Array.isArray(ps.silencedHosts)) {
|
||||
let lastValue = "";
|
||||
set.silencedHosts = ps.silencedHosts.sort().filter((h)=>{
|
||||
const lv = lastValue;
|
||||
lastValue = h;
|
||||
return h !== "" && h !== lv;
|
||||
});
|
||||
}
|
||||
if (ps.themeColor !== undefined) {
|
||||
set.themeColor = ps.themeColor;
|
||||
}
|
||||
if (Array.isArray(ps.allowedHosts)) {
|
||||
set.allowedHosts = ps.allowedHosts.filter(Boolean);
|
||||
}
|
||||
if (typeof ps.privateMode === "boolean") {
|
||||
set.privateMode = ps.privateMode;
|
||||
}
|
||||
if (typeof ps.secureMode === "boolean") {
|
||||
set.secureMode = ps.secureMode;
|
||||
}
|
||||
if (ps.mascotImageUrl !== undefined) {
|
||||
set.mascotImageUrl = ps.mascotImageUrl;
|
||||
}
|
||||
if (ps.bannerUrl !== undefined) {
|
||||
set.bannerUrl = ps.bannerUrl;
|
||||
}
|
||||
if (ps.logoImageUrl !== undefined) {
|
||||
set.logoImageUrl = ps.logoImageUrl;
|
||||
}
|
||||
if (ps.iconUrl !== undefined) {
|
||||
set.iconUrl = ps.iconUrl;
|
||||
}
|
||||
if (ps.backgroundImageUrl !== undefined) {
|
||||
set.backgroundImageUrl = ps.backgroundImageUrl;
|
||||
}
|
||||
if (ps.logoImageUrl !== undefined) {
|
||||
set.logoImageUrl = ps.logoImageUrl;
|
||||
}
|
||||
if (ps.name !== undefined) {
|
||||
set.name = ps.name;
|
||||
}
|
||||
if (ps.description !== undefined) {
|
||||
set.description = ps.description;
|
||||
}
|
||||
if (ps.defaultLightTheme !== undefined) {
|
||||
set.defaultLightTheme = ps.defaultLightTheme;
|
||||
}
|
||||
if (ps.defaultDarkTheme !== undefined) {
|
||||
set.defaultDarkTheme = ps.defaultDarkTheme;
|
||||
}
|
||||
if (ps.localDriveCapacityMb !== undefined) {
|
||||
set.localDriveCapacityMb = ps.localDriveCapacityMb;
|
||||
}
|
||||
if (ps.remoteDriveCapacityMb !== undefined) {
|
||||
set.remoteDriveCapacityMb = ps.remoteDriveCapacityMb;
|
||||
}
|
||||
if (ps.lua4frozenDatabaseCapacityMb !== undefined) {
|
||||
set.lua4frozenDatabaseCapacityMb = ps.lua4frozenDatabaseCapacityMb;
|
||||
}
|
||||
if (ps.cacheRemoteFiles !== undefined) {
|
||||
set.cacheRemoteFiles = ps.cacheRemoteFiles;
|
||||
}
|
||||
if (ps.emailRequiredForSignup !== undefined) {
|
||||
set.emailRequiredForSignup = ps.emailRequiredForSignup;
|
||||
}
|
||||
if (ps.enableHcaptcha !== undefined) {
|
||||
set.enableHcaptcha = ps.enableHcaptcha;
|
||||
}
|
||||
if (ps.hcaptchaSiteKey !== undefined) {
|
||||
set.hcaptchaSiteKey = ps.hcaptchaSiteKey;
|
||||
}
|
||||
if (ps.hcaptchaSecretKey !== undefined) {
|
||||
set.hcaptchaSecretKey = ps.hcaptchaSecretKey;
|
||||
}
|
||||
if (ps.enableRecaptcha !== undefined) {
|
||||
set.enableRecaptcha = ps.enableRecaptcha;
|
||||
}
|
||||
if (ps.recaptchaSiteKey !== undefined) {
|
||||
set.recaptchaSiteKey = ps.recaptchaSiteKey;
|
||||
}
|
||||
if (ps.recaptchaSecretKey !== undefined) {
|
||||
set.recaptchaSecretKey = ps.recaptchaSecretKey;
|
||||
}
|
||||
if (ps.maintainerName !== undefined) {
|
||||
set.maintainerName = ps.maintainerName;
|
||||
}
|
||||
if (ps.maintainerEmail !== undefined) {
|
||||
set.maintainerEmail = ps.maintainerEmail;
|
||||
}
|
||||
if (Array.isArray(ps.langs)) {
|
||||
set.langs = ps.langs.filter(Boolean);
|
||||
}
|
||||
if (Array.isArray(ps.pinnedPages)) {
|
||||
set.pinnedPages = ps.pinnedPages.filter(Boolean);
|
||||
}
|
||||
if (ps.pinnedClipId !== undefined) {
|
||||
set.pinnedClipId = ps.pinnedClipId;
|
||||
}
|
||||
if (ps.summalyProxy !== undefined) {
|
||||
set.summalyProxy = ps.summalyProxy;
|
||||
}
|
||||
if (ps.enableGithubIntegration !== undefined) {
|
||||
set.enableGithubIntegration = ps.enableGithubIntegration;
|
||||
}
|
||||
if (ps.githubClientId !== undefined) {
|
||||
set.githubClientId = ps.githubClientId;
|
||||
}
|
||||
if (ps.githubClientSecret !== undefined) {
|
||||
set.githubClientSecret = ps.githubClientSecret;
|
||||
}
|
||||
if (ps.enableDiscordIntegration !== undefined) {
|
||||
set.enableDiscordIntegration = ps.enableDiscordIntegration;
|
||||
}
|
||||
if (ps.discordClientId !== undefined) {
|
||||
set.discordClientId = ps.discordClientId;
|
||||
}
|
||||
if (ps.discordClientSecret !== undefined) {
|
||||
set.discordClientSecret = ps.discordClientSecret;
|
||||
}
|
||||
if (ps.enableEmail !== undefined) {
|
||||
set.enableEmail = ps.enableEmail;
|
||||
}
|
||||
if (ps.email !== undefined) {
|
||||
set.email = ps.email;
|
||||
}
|
||||
if (ps.smtpSecure !== undefined) {
|
||||
set.smtpSecure = ps.smtpSecure;
|
||||
}
|
||||
if (ps.smtpHost !== undefined) {
|
||||
set.smtpHost = ps.smtpHost;
|
||||
}
|
||||
if (ps.smtpPort !== undefined) {
|
||||
set.smtpPort = ps.smtpPort;
|
||||
}
|
||||
if (ps.smtpUser !== undefined) {
|
||||
set.smtpUser = ps.smtpUser;
|
||||
}
|
||||
if (ps.smtpPass !== undefined) {
|
||||
set.smtpPass = ps.smtpPass;
|
||||
}
|
||||
if (ps.errorImageUrl !== undefined) {
|
||||
set.errorImageUrl = ps.errorImageUrl;
|
||||
}
|
||||
if (ps.tosUrl !== undefined) {
|
||||
set.ToSUrl = ps.tosUrl;
|
||||
}
|
||||
if (ps.repositoryUrl !== undefined) {
|
||||
set.repositoryUrl = ps.repositoryUrl;
|
||||
}
|
||||
if (ps.feedbackUrl !== undefined) {
|
||||
set.feedbackUrl = ps.feedbackUrl;
|
||||
}
|
||||
if (ps.useObjectStorage !== undefined) {
|
||||
set.useObjectStorage = ps.useObjectStorage;
|
||||
}
|
||||
if (ps.objectStorageBaseUrl !== undefined) {
|
||||
set.objectStorageBaseUrl = ps.objectStorageBaseUrl;
|
||||
}
|
||||
if (ps.objectStorageBucket !== undefined) {
|
||||
set.objectStorageBucket = ps.objectStorageBucket;
|
||||
}
|
||||
if (ps.objectStoragePrefix !== undefined) {
|
||||
set.objectStoragePrefix = ps.objectStoragePrefix;
|
||||
}
|
||||
if (ps.objectStorageEndpoint !== undefined) {
|
||||
set.objectStorageEndpoint = ps.objectStorageEndpoint;
|
||||
}
|
||||
if (ps.objectStorageRegion !== undefined) {
|
||||
set.objectStorageRegion = ps.objectStorageRegion;
|
||||
}
|
||||
if (ps.objectStoragePort !== undefined) {
|
||||
set.objectStoragePort = ps.objectStoragePort;
|
||||
}
|
||||
if (ps.objectStorageAccessKey !== undefined) {
|
||||
set.objectStorageAccessKey = ps.objectStorageAccessKey;
|
||||
}
|
||||
if (ps.objectStorageSecretKey !== undefined) {
|
||||
set.objectStorageSecretKey = ps.objectStorageSecretKey;
|
||||
}
|
||||
if (ps.objectStorageUseSSL !== undefined) {
|
||||
set.objectStorageUseSSL = ps.objectStorageUseSSL;
|
||||
}
|
||||
if (ps.objectStorageUseProxy !== undefined) {
|
||||
set.objectStorageUseProxy = ps.objectStorageUseProxy;
|
||||
}
|
||||
if (ps.objectStorageSetPublicRead !== undefined) {
|
||||
set.objectStorageSetPublicRead = ps.objectStorageSetPublicRead;
|
||||
}
|
||||
if (ps.objectStorageS3ForcePathStyle !== undefined) {
|
||||
set.objectStorageS3ForcePathStyle = ps.objectStorageS3ForcePathStyle;
|
||||
}
|
||||
if (ps.deeplAuthKey !== undefined) {
|
||||
if (ps.deeplAuthKey === "") {
|
||||
set.deeplAuthKey = null;
|
||||
} else {
|
||||
set.deeplAuthKey = ps.deeplAuthKey;
|
||||
}
|
||||
}
|
||||
if (ps.deeplIsPro !== undefined) {
|
||||
set.deeplIsPro = ps.deeplIsPro;
|
||||
}
|
||||
if (ps.libreTranslateApiUrl !== undefined) {
|
||||
if (ps.libreTranslateApiUrl === "") {
|
||||
set.libreTranslateApiUrl = null;
|
||||
} else {
|
||||
set.libreTranslateApiUrl = ps.libreTranslateApiUrl;
|
||||
}
|
||||
}
|
||||
if (ps.libreTranslateApiKey !== undefined) {
|
||||
if (ps.libreTranslateApiKey === "") {
|
||||
set.libreTranslateApiKey = null;
|
||||
} else {
|
||||
set.libreTranslateApiKey = ps.libreTranslateApiKey;
|
||||
}
|
||||
}
|
||||
if (ps.enableIpLogging !== undefined) {
|
||||
set.enableIpLogging = ps.enableIpLogging;
|
||||
}
|
||||
if (ps.enableActiveEmailValidation !== undefined) {
|
||||
set.enableActiveEmailValidation = ps.enableActiveEmailValidation;
|
||||
}
|
||||
if (ps.experimentalFeatures !== undefined) {
|
||||
set.experimentalFeatures = ps.experimentalFeatures || undefined;
|
||||
}
|
||||
if (ps.enableServerMachineStats !== undefined) {
|
||||
set.enableServerMachineStats = ps.enableServerMachineStats;
|
||||
}
|
||||
if (ps.enableIdenticonGeneration !== undefined) {
|
||||
set.enableIdenticonGeneration = ps.enableIdenticonGeneration;
|
||||
}
|
||||
if (ps.donationLink !== undefined) {
|
||||
set.donationLink = ps.donationLink;
|
||||
if (set.donationLink && !/^https?:\/\//i.test(set.donationLink)) {
|
||||
set.donationLink = `https://${set.donationLink}`;
|
||||
}
|
||||
}
|
||||
if (ps.autofollowedAccount !== undefined) {
|
||||
if (ps.autofollowedAccount === null) {
|
||||
set.autofollowedAccount = null;
|
||||
} else {
|
||||
// Verify account exists and is a local account
|
||||
const user = await Users.findOneBy({
|
||||
username: ps.autofollowedAccount,
|
||||
host: IsNull()
|
||||
});
|
||||
if (user !== null) {
|
||||
set.autofollowedAccount = user.username;
|
||||
} else {
|
||||
set.autofollowedAccount = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
const meta = await Metas.findOne({
|
||||
where: {},
|
||||
order: {
|
||||
id: "DESC"
|
||||
}
|
||||
});
|
||||
if (meta) await Metas.update(meta.id, set);
|
||||
else await Metas.save(set);
|
||||
insertModerationLog(me, "updateMeta");
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import { UserProfiles, Users } from "../../../../models/index.js";
|
||||
import define from "../../define.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
text: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId",
|
||||
"text"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const user = await Users.findOneBy({
|
||||
id: ps.userId
|
||||
});
|
||||
if (user == null) {
|
||||
throw new Error("user not found");
|
||||
}
|
||||
await UserProfiles.update({
|
||||
userId: user.id
|
||||
}, {
|
||||
moderationNote: ps.text
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import define from "../../../define.js";
|
||||
import { Plans, UserPlans, Users } from "../../../../../models/index.js";
|
||||
import { genId } from "../../../../../misc/gen-id.js";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
planId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId",
|
||||
"planId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
await Users.findOneByOrFail({
|
||||
id: ps.userId
|
||||
});
|
||||
await Plans.findOneByOrFail({
|
||||
id: ps.planId
|
||||
});
|
||||
const exists = await UserPlans.findOneBy({
|
||||
userId: ps.userId,
|
||||
planId: ps.planId
|
||||
});
|
||||
if (exists) return;
|
||||
await UserPlans.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
userId: ps.userId,
|
||||
planId: ps.planId
|
||||
});
|
||||
insertModerationLog(me, "addUserPlan", {
|
||||
targetId: ps.userId,
|
||||
planId: ps.planId
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import define from "../../../define.js";
|
||||
import { Plans, UserPlans, Users } from "../../../../../models/index.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
await Users.findOneByOrFail({
|
||||
id: ps.userId
|
||||
});
|
||||
const joins = await UserPlans.find({
|
||||
where: {
|
||||
userId: ps.userId
|
||||
},
|
||||
relations: [
|
||||
"plan"
|
||||
],
|
||||
order: {
|
||||
createdAt: "ASC"
|
||||
}
|
||||
});
|
||||
return await Plans.packMany(joins.map((join)=>join.plan).filter((plan)=>plan != null));
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import define from "../../../define.js";
|
||||
import { UserPlans, Users } from "../../../../../models/index.js";
|
||||
import { insertModerationLog } from "../../../../../services/insert-moderation-log.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
planId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"userId",
|
||||
"planId"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
await Users.findOneByOrFail({
|
||||
id: ps.userId
|
||||
});
|
||||
await UserPlans.delete({
|
||||
userId: ps.userId,
|
||||
planId: ps.planId
|
||||
});
|
||||
insertModerationLog(me, "removeUserPlan", {
|
||||
targetId: ps.userId,
|
||||
planId: ps.planId
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import define from "../../define.js";
|
||||
import { insertModerationLog } from "../../../../services/insert-moderation-log.js";
|
||||
import { db } from "../../../../db/postgre.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireModerator: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
full: {
|
||||
type: "boolean"
|
||||
},
|
||||
analyze: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"full",
|
||||
"analyze"
|
||||
]
|
||||
};
|
||||
export default define(meta, paramDef, async (ps, me)=>{
|
||||
const params = [];
|
||||
if (ps.full) {
|
||||
params.push("FULL");
|
||||
}
|
||||
if (ps.analyze) {
|
||||
params.push("ANALYZE");
|
||||
}
|
||||
db.query(`VACUUM ${params.join(" ")}`);
|
||||
insertModerationLog(me, "vacuum", ps);
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import define from "../../define.js";
|
||||
import { VerifiedBadgeRequests } from "../../../../models/index.js";
|
||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||
export const meta = {
|
||||
tags: [
|
||||
"admin"
|
||||
],
|
||||
requireCredential: true,
|
||||
requireAdmin: true
|
||||
};
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 10
|
||||
},
|
||||
sinceId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
untilId: {
|
||||
type: "string",
|
||||
format: "misskey:id"
|
||||
},
|
||||
state: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"all",
|
||||
"pending",
|
||||
"approved",
|
||||
"rejected"
|
||||
],
|
||||
default: "pending"
|
||||
}
|
||||
},
|
||||
required: []
|
||||
};
|
||||
export default define(meta, paramDef, async (ps)=>{
|
||||
const query = makePaginationQuery(VerifiedBadgeRequests.createQueryBuilder("request"), ps.sinceId, ps.untilId);
|
||||
if (ps.state !== "all") {
|
||||
query.andWhere("request.status = :status", {
|
||||
status: ps.state
|
||||
});
|
||||
}
|
||||
const requests = await query.take(ps.limit).getMany();
|
||||
return await VerifiedBadgeRequests.packMany(requests);
|
||||
});
|
||||
Reference in New Issue
Block a user