Co-authored-by: MeiMei <30769358+mei23@users.noreply.github.com>
Co-authored-by: Satsuki Yanagi <17376330+u1-liquid@users.noreply.github.com>
This commit is contained in:
syuilo 2020-01-30 04:37:25 +09:00 committed by GitHub
parent a5955c1123
commit f6154dc0af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
871 changed files with 26140 additions and 71950 deletions

View file

@ -0,0 +1,33 @@
import define from '../../../define';
import { Users } from '../../../../../models';
import { signup } from '../../../common/signup';
export const meta = {
tags: ['admin'],
params: {
username: {
validator: Users.validateLocalUsername,
},
password: {
validator: Users.validatePassword,
}
}
};
export default define(meta, async (ps, me) => {
const noUsers = (await Users.count({})) === 0;
if (!noUsers && me == null) throw new Error('access denied');
const { account, secret } = await signup(ps.username, ps.password);
const res = await Users.pack(account, account, {
detail: true,
includeSecrets: true
});
(res as any).token = secret;
return res;
});

View file

@ -0,0 +1,36 @@
import $ from 'cafy';
import define from '../../../define';
import { Announcements } from '../../../../../models';
import { genId } from '../../../../../misc/gen-id';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
title: {
validator: $.str.min(1)
},
text: {
validator: $.str.min(1)
},
imageUrl: {
validator: $.nullable.str.min(1)
}
}
};
export default define(meta, async (ps) => {
const announcement = await Announcements.save({
id: genId(),
createdAt: new Date(),
updatedAt: null,
title: ps.title,
text: ps.text,
imageUrl: ps.imageUrl,
});
return announcement;
});

View file

@ -0,0 +1,34 @@
import $ from 'cafy';
import define from '../../../define';
import { ID } from '../../../../../misc/cafy-id';
import { Announcements } from '../../../../../models';
import { ApiError } from '../../../error';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
id: {
validator: $.type(ID)
}
},
errors: {
noSuchAnnouncement: {
message: 'No such announcement.',
code: 'NO_SUCH_ANNOUNCEMENT',
id: 'ecad8040-a276-4e85-bda9-015a708d291e'
}
}
};
export default define(meta, async (ps, me) => {
const announcement = await Announcements.findOne(ps.id);
if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement);
await Announcements.delete(announcement.id);
});

View file

@ -0,0 +1,41 @@
import $ from 'cafy';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { Announcements, AnnouncementReads } from '../../../../../models';
import { makePaginationQuery } from '../../../common/make-pagination-query';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
limit: {
validator: $.optional.num.range(1, 100),
default: 10
},
sinceId: {
validator: $.optional.type(ID),
},
untilId: {
validator: $.optional.type(ID),
},
}
};
export default define(meta, async (ps) => {
const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId);
const announcements = await query.take(ps.limit!).getMany();
for (const announcement of announcements) {
(announcement as any).reads = await AnnouncementReads.count({
announcementId: announcement.id
});
}
return announcements;
});

View file

@ -0,0 +1,48 @@
import $ from 'cafy';
import define from '../../../define';
import { ID } from '../../../../../misc/cafy-id';
import { Announcements } from '../../../../../models';
import { ApiError } from '../../../error';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
id: {
validator: $.type(ID)
},
title: {
validator: $.str.min(1)
},
text: {
validator: $.str.min(1)
},
imageUrl: {
validator: $.nullable.str.min(1)
}
},
errors: {
noSuchAnnouncement: {
message: 'No such announcement.',
code: 'NO_SUCH_ANNOUNCEMENT',
id: 'd3aae5a7-6372-4cb4-b61c-f511ffc2d7cc'
}
}
};
export default define(meta, async (ps, me) => {
const announcement = await Announcements.findOne(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,
});
});

View file

@ -0,0 +1,62 @@
import $ from 'cafy';
import define from '../../../define';
import { Emojis } from '../../../../../models';
import { toPuny } from '../../../../../misc/convert-host';
import { makePaginationQuery } from '../../../common/make-pagination-query';
import { ID } from '../../../../../misc/cafy-id';
export const meta = {
desc: {
'ja-JP': 'カスタム絵文字を取得します。'
},
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
host: {
validator: $.optional.nullable.str,
default: null as any
},
limit: {
validator: $.optional.num.range(1, 100),
default: 10
},
sinceId: {
validator: $.optional.type(ID),
},
untilId: {
validator: $.optional.type(ID),
}
}
};
export default define(meta, 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) });
}
const emojis = await q
.orderBy('emoji.category', 'ASC')
.orderBy('emoji.name', 'ASC')
.take(ps.limit!)
.getMany();
return emojis.map(e => ({
id: e.id,
name: e.name,
category: e.category,
aliases: e.aliases,
host: e.host,
url: e.url
}));
});

View file

@ -1,7 +1,8 @@
import $ from 'cafy';
import define from '../../../define';
import { Emojis } from '../../../../../models';
import { toPunyNullable } from '../../../../../misc/convert-host';
import { makePaginationQuery } from '../../../common/make-pagination-query';
import { ID } from '../../../../../misc/cafy-id';
export const meta = {
desc: {
@ -14,23 +15,28 @@ export const meta = {
requireModerator: true,
params: {
host: {
validator: $.optional.nullable.str,
default: null as any
limit: {
validator: $.optional.num.range(1, 100),
default: 10
},
sinceId: {
validator: $.optional.type(ID),
},
untilId: {
validator: $.optional.type(ID),
}
}
};
export default define(meta, async (ps) => {
const emojis = await Emojis.find({
where: {
host: toPunyNullable(ps.host)
},
order: {
category: 'ASC',
name: 'ASC'
}
});
const emojis = await makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId)
.andWhere(`emoji.host IS NULL`)
.orderBy('emoji.category', 'ASC')
.orderBy('emoji.name', 'ASC')
.take(ps.limit!)
.getMany();
return emojis.map(e => ({
id: e.id,

View file

@ -0,0 +1,31 @@
import define from '../../../define';
import { deliverQueue } from '../../../../../queue';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
}
};
export default define(meta, async (ps) => {
const jobs = await deliverQueue.getJobs(['delayed']);
const res = [] as [string, number][];
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;
});

View file

@ -0,0 +1,31 @@
import define from '../../../define';
import { inboxQueue } from '../../../../../queue';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
}
};
export default define(meta, async (ps) => {
const jobs = await inboxQueue.getJobs(['delayed']);
const res = [] as [string, number][];
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;
});

View file

@ -0,0 +1,45 @@
import * as os from 'os';
import * as si from 'systeminformation';
import { getConnection } from 'typeorm';
import define from '../../define';
import redis from '../../../../db/redis';
export const meta = {
requireCredential: false,
desc: {
},
tags: ['meta'],
params: {
},
};
export default define(meta, async () => {
const memStats = await si.mem();
const fsStats = await si.fsSize();
const netInterface = await si.networkInterfaceDefault();
return {
machine: os.hostname(),
os: os.platform(),
node: process.version,
psql: await getConnection().query('SHOW server_version').then(x => x[0].server_version),
redis: redis.server_info.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
}
};
});

View file

@ -13,16 +13,9 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
requireAdmin: true,
params: {
announcements: {
validator: $.optional.nullable.arr($.obj()),
desc: {
'ja-JP': 'お知らせ'
}
},
disableRegistration: {
validator: $.optional.nullable.bool,
desc: {
@ -44,13 +37,6 @@ export const meta = {
}
},
enableEmojiReaction: {
validator: $.optional.nullable.bool,
desc: {
'ja-JP': '絵文字リアクションを有効にするか否か'
}
},
useStarForReactionFallback: {
validator: $.optional.nullable.bool,
desc: {
@ -347,7 +333,7 @@ export const meta = {
}
},
ToSUrl: {
tosUrl: {
validator: $.optional.nullable.str,
desc: {
'ja-JP': '利用規約のURL'
@ -413,10 +399,6 @@ export const meta = {
export default define(meta, async (ps, me) => {
const set = {} as Partial<Meta>;
if (ps.announcements) {
set.announcements = ps.announcements;
}
if (typeof ps.disableRegistration === 'boolean') {
set.disableRegistration = ps.disableRegistration;
}
@ -429,10 +411,6 @@ export default define(meta, async (ps, me) => {
set.disableGlobalTimeline = ps.disableGlobalTimeline;
}
if (typeof ps.enableEmojiReaction === 'boolean') {
set.enableEmojiReaction = ps.enableEmojiReaction;
}
if (typeof ps.useStarForReactionFallback === 'boolean') {
set.useStarForReactionFallback = ps.useStarForReactionFallback;
}
@ -601,8 +579,8 @@ export default define(meta, async (ps, me) => {
set.swPrivateKey = ps.swPrivateKey;
}
if (ps.ToSUrl !== undefined) {
set.ToSUrl = ps.ToSUrl;
if (ps.tosUrl !== undefined) {
set.ToSUrl = ps.tosUrl;
}
if (ps.repositoryUrl !== undefined) {