Merge branch 'misskey-dev:develop' into sonic
This commit is contained in:
commit
79b9e6d4ba
66 changed files with 428 additions and 206 deletions
10
.github/workflows/lint.yml
vendored
10
.github/workflows/lint.yml
vendored
|
@ -14,12 +14,12 @@ jobs:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version: 16.x
|
||||||
- uses: actions/cache@v2
|
cache: 'yarn'
|
||||||
with:
|
cache-dependency-path: |
|
||||||
path: '**/node_modules'
|
packages/backend/yarn.lock
|
||||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
packages/client/yarn.lock
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn lint
|
- run: yarn lint
|
||||||
|
|
16
.github/workflows/test.yml
vendored
16
.github/workflows/test.yml
vendored
|
@ -33,9 +33,13 @@ jobs:
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'yarn'
|
||||||
|
cache-dependency-path: |
|
||||||
|
packages/backend/yarn.lock
|
||||||
|
packages/client/yarn.lock
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
- name: Check yarn.lock
|
- name: Check yarn.lock
|
||||||
|
@ -80,13 +84,13 @@ jobs:
|
||||||
#- uses: browser-actions/setup-firefox@latest
|
#- uses: browser-actions/setup-firefox@latest
|
||||||
# if: ${{ matrix.browser == 'firefox' }}
|
# if: ${{ matrix.browser == 'firefox' }}
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- uses: actions/cache@v2
|
cache: 'yarn'
|
||||||
with:
|
cache-dependency-path: |
|
||||||
path: '**/node_modules'
|
packages/backend/yarn.lock
|
||||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
packages/client/yarn.lock
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
- name: Check yarn.lock
|
- name: Check yarn.lock
|
||||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -10,10 +10,22 @@
|
||||||
You should also include the user name that made the change.
|
You should also include the user name that made the change.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## 12.109.1 (2022/04/02)
|
## 12.x.x (unreleased)
|
||||||
|
|
||||||
### Known issues
|
### Improvements
|
||||||
- two-factor authentication is not working
|
- Improve webhook @syuilo
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
-
|
||||||
|
|
||||||
|
|
||||||
|
## 12.109.2 (2022/04/03)
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
- API: admin/update-meta was not working @syuilo
|
||||||
|
- Client: テーマを切り替えたり読み込んだりするとmeta[name="theme-color"]のcontentがundefinedになる問題を修正 @tamaina
|
||||||
|
|
||||||
|
## 12.109.1 (2022/04/02)
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
- API: Renoteが行えない問題を修正
|
- API: Renoteが行えない問題を修正
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "12.109.1",
|
"version": "12.109.2",
|
||||||
"codename": "indigo",
|
"codename": "indigo",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -209,7 +209,11 @@ export const db = new DataSource({
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function initDb() {
|
export async function initDb() {
|
||||||
|
if (db.isInitialized) {
|
||||||
|
// nop
|
||||||
|
} else {
|
||||||
await db.connect();
|
await db.connect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resetDb() {
|
export async function resetDb() {
|
||||||
|
|
|
@ -42,7 +42,8 @@ async function getCaptchaResponse(url: string, secret: string, response: string)
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': config.userAgent,
|
'User-Agent': config.userAgent,
|
||||||
},
|
},
|
||||||
timeout: 10 * 1000,
|
// TODO
|
||||||
|
//timeout: 10 * 1000,
|
||||||
agent: getAgentByUrl,
|
agent: getAgentByUrl,
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
throw `${e.message || e}`;
|
throw `${e.message || e}`;
|
||||||
|
|
|
@ -120,9 +120,9 @@ export const httpsAgent = config.proxy
|
||||||
*/
|
*/
|
||||||
export function getAgentByUrl(url: URL, bypassProxy = false) {
|
export function getAgentByUrl(url: URL, bypassProxy = false) {
|
||||||
if (bypassProxy || (config.proxyBypassHosts || []).includes(url.hostname)) {
|
if (bypassProxy || (config.proxyBypassHosts || []).includes(url.hostname)) {
|
||||||
return url.protocol == 'http:' ? _http : _https;
|
return url.protocol === 'http:' ? _http : _https;
|
||||||
} else {
|
} else {
|
||||||
return url.protocol == 'http:' ? httpAgent : httpsAgent;
|
return url.protocol === 'http:' ? httpAgent : httpsAgent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const getNoteSummary = (note: Packed<'Note'>): string => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ファイルが添付されているとき
|
// ファイルが添付されているとき
|
||||||
if ((note.files || []).length != 0) {
|
if ((note.files || []).length !== 0) {
|
||||||
summary += ` (📎${note.files!.length})`;
|
summary += ` (📎${note.files!.length})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ export const packedEmojiSchema = {
|
||||||
host: {
|
host: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
|
|
@ -21,6 +21,7 @@ export const packedUserLiteSchema = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
nullable: true, optional: false,
|
nullable: true, optional: false,
|
||||||
example: 'misskey.example.com',
|
example: 'misskey.example.com',
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
avatarUrl: {
|
avatarUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import httpSignature from 'http-signature';
|
import httpSignature from 'http-signature';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import config from '@/config/index.js';
|
import config from '@/config/index.js';
|
||||||
import { envOption } from '../env.js';
|
import { envOption } from '../env.js';
|
||||||
|
@ -16,7 +17,7 @@ import { getJobInfo } from './get-job-info.js';
|
||||||
import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue, endedPollNotificationQueue, webhookDeliverQueue } from './queues.js';
|
import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue, endedPollNotificationQueue, webhookDeliverQueue } from './queues.js';
|
||||||
import { ThinUser } from './types.js';
|
import { ThinUser } from './types.js';
|
||||||
import { IActivity } from '@/remote/activitypub/type.js';
|
import { IActivity } from '@/remote/activitypub/type.js';
|
||||||
import { Webhook } from '@/models/entities/webhook.js';
|
import { Webhook, webhookEventTypes } from '@/models/entities/webhook.js';
|
||||||
|
|
||||||
function renderError(e: Error): any {
|
function renderError(e: Error): any {
|
||||||
return {
|
return {
|
||||||
|
@ -262,12 +263,16 @@ export function createCleanRemoteFilesJob() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function webhookDeliver(webhook: Webhook, content: unknown) {
|
export function webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) {
|
||||||
const data = {
|
const data = {
|
||||||
|
type,
|
||||||
content,
|
content,
|
||||||
webhookId: webhook.id,
|
webhookId: webhook.id,
|
||||||
|
userId: webhook.userId,
|
||||||
to: webhook.url,
|
to: webhook.url,
|
||||||
secret: webhook.secret,
|
secret: webhook.secret,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
eventId: uuid(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return webhookDeliverQueue.add(data, {
|
return webhookDeliverQueue.add(data, {
|
||||||
|
|
|
@ -8,13 +8,9 @@ import config from '@/config/index.js';
|
||||||
|
|
||||||
const logger = new Logger('webhook');
|
const logger = new Logger('webhook');
|
||||||
|
|
||||||
let latest: string | null = null;
|
|
||||||
|
|
||||||
export default async (job: Bull.Job<WebhookDeliverJobData>) => {
|
export default async (job: Bull.Job<WebhookDeliverJobData>) => {
|
||||||
try {
|
try {
|
||||||
if (latest !== (latest = JSON.stringify(job.data.content, null, 2))) {
|
logger.debug(`delivering ${job.data.webhookId}`);
|
||||||
logger.debug(`delivering ${latest}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await getResponse({
|
const res = await getResponse({
|
||||||
url: job.data.to,
|
url: job.data.to,
|
||||||
|
@ -25,7 +21,14 @@ export default async (job: Bull.Job<WebhookDeliverJobData>) => {
|
||||||
'X-Misskey-Hook-Id': job.data.webhookId,
|
'X-Misskey-Hook-Id': job.data.webhookId,
|
||||||
'X-Misskey-Hook-Secret': job.data.secret,
|
'X-Misskey-Hook-Secret': job.data.secret,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(job.data.content),
|
body: JSON.stringify({
|
||||||
|
hookId: job.data.webhookId,
|
||||||
|
userId: job.data.userId,
|
||||||
|
eventId: job.data.eventId,
|
||||||
|
createdAt: job.data.createdAt,
|
||||||
|
type: job.data.type,
|
||||||
|
body: job.data.content,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
Webhooks.update({ id: job.data.webhookId }, {
|
Webhooks.update({ id: job.data.webhookId }, {
|
||||||
|
|
|
@ -48,10 +48,14 @@ export type EndedPollNotificationJobData = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WebhookDeliverJobData = {
|
export type WebhookDeliverJobData = {
|
||||||
|
type: string;
|
||||||
content: unknown;
|
content: unknown;
|
||||||
webhookId: Webhook['id'];
|
webhookId: Webhook['id'];
|
||||||
|
userId: User['id'];
|
||||||
to: string;
|
to: string;
|
||||||
secret: string;
|
secret: string;
|
||||||
|
createdAt: number;
|
||||||
|
eventId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ThinUser = {
|
export type ThinUser = {
|
||||||
|
|
|
@ -95,7 +95,7 @@ function genSigningString(request: Request, includeHeaders: string[]) {
|
||||||
|
|
||||||
function lcObjectKey(src: Record<string, string>) {
|
function lcObjectKey(src: Record<string, string>) {
|
||||||
const dst: Record<string, string> = {};
|
const dst: Record<string, string> = {};
|
||||||
for (const key of Object.keys(src).filter(x => x != '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key];
|
for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key];
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,15 +109,15 @@ export default class DeliverManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.recipes.filter((recipe): recipe is IDirectRecipe => {
|
this.recipes.filter((recipe): recipe is IDirectRecipe =>
|
||||||
// followers recipes have already been processed
|
// followers recipes have already been processed
|
||||||
isDirect(recipe)
|
isDirect(recipe)
|
||||||
// check that shared inbox has not been added yet
|
// check that shared inbox has not been added yet
|
||||||
&& !(recipe.to.sharedInbox && inboxes.has(recipe.to.sharedInbox))
|
&& !(recipe.to.sharedInbox && inboxes.has(recipe.to.sharedInbox))
|
||||||
// check that they actually have an inbox
|
// check that they actually have an inbox
|
||||||
&& recipe.to.inbox
|
&& recipe.to.inbox != null,
|
||||||
})
|
)
|
||||||
.forEach(recipe => inboxes.add(recipe.to.inbox));
|
.forEach(recipe => inboxes.add(recipe.to.inbox!));
|
||||||
|
|
||||||
// deliver
|
// deliver
|
||||||
for (const inbox of inboxes) {
|
for (const inbox of inboxes) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ export const performReadActivity = async (actor: CacheableRemoteUser, activity:
|
||||||
return `skip: message not found`;
|
return `skip: message not found`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actor.id != message.recipientId) {
|
if (actor.id !== message.recipientId) {
|
||||||
return `skip: actor is not a message recipient`;
|
return `skip: actor is not a message recipient`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import unfollow from '@/services/following/delete.js';
|
import unfollow from '@/services/following/delete.js';
|
||||||
import cancelRequest from '@/services/following/requests/cancel.js';
|
import cancelRequest from '@/services/following/requests/cancel.js';
|
||||||
import {IAccept} from '../../type.js';
|
import { IAccept } from '../../type.js';
|
||||||
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
||||||
import { Followings } from '@/models/index.js';
|
import { Followings } from '@/models/index.js';
|
||||||
import DbResolver from '../../db-resolver.js';
|
import DbResolver from '../../db-resolver.js';
|
||||||
|
|
|
@ -113,7 +113,8 @@ export class LdSignature {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/ld+json, application/json',
|
Accept: 'application/ld+json, application/json',
|
||||||
},
|
},
|
||||||
timeout: this.loderTimeout,
|
// TODO
|
||||||
|
//timeout: this.loderTimeout,
|
||||||
agent: u => u.protocol === 'http:' ? httpAgent : httpsAgent,
|
agent: u => u.protocol === 'http:' ? httpAgent : httpsAgent,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|
|
@ -69,7 +69,7 @@ export async function updateQuestion(value: any) {
|
||||||
const oldCount = poll.votes[poll.choices.indexOf(choice)];
|
const oldCount = poll.votes[poll.choices.indexOf(choice)];
|
||||||
const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems;
|
const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems;
|
||||||
|
|
||||||
if (oldCount != newCount) {
|
if (oldCount !== newCount) {
|
||||||
changed = true;
|
changed = true;
|
||||||
poll.votes[poll.choices.indexOf(choice)] = newCount;
|
poll.votes[poll.choices.indexOf(choice)] = newCount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ type IWebFinger = {
|
||||||
export default async function(query: string): Promise<IWebFinger> {
|
export default async function(query: string): Promise<IWebFinger> {
|
||||||
const url = genUrl(query);
|
const url = genUrl(query);
|
||||||
|
|
||||||
return await getJson(url, 'application/jrd+json, application/json');
|
return await getJson(url, 'application/jrd+json, application/json') as IWebFinger;
|
||||||
}
|
}
|
||||||
|
|
||||||
function genUrl(query: string) {
|
function genUrl(query: string) {
|
||||||
|
|
|
@ -121,14 +121,14 @@ export function verifyLogin({
|
||||||
signature: Buffer,
|
signature: Buffer,
|
||||||
challenge: string
|
challenge: string
|
||||||
}) {
|
}) {
|
||||||
if (clientData.type != 'webauthn.get') {
|
if (clientData.type !== 'webauthn.get') {
|
||||||
throw new Error('type is not webauthn.get');
|
throw new Error('type is not webauthn.get');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hash(clientData.challenge).toString('hex') != challenge) {
|
if (hash(clientData.challenge).toString('hex') !== challenge) {
|
||||||
throw new Error('challenge mismatch');
|
throw new Error('challenge mismatch');
|
||||||
}
|
}
|
||||||
if (clientData.origin != config.scheme + '://' + config.host) {
|
if (clientData.origin !== config.scheme + '://' + config.host) {
|
||||||
throw new Error('origin mismatch');
|
throw new Error('origin mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,11 +148,11 @@ export const procedures = {
|
||||||
verify({ publicKey }: {publicKey: Map<number, Buffer>}) {
|
verify({ publicKey }: {publicKey: Map<number, Buffer>}) {
|
||||||
const negTwo = publicKey.get(-2);
|
const negTwo = publicKey.get(-2);
|
||||||
|
|
||||||
if (!negTwo || negTwo.length != 32) {
|
if (!negTwo || negTwo.length !== 32) {
|
||||||
throw new Error('invalid or no -2 key given');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree = publicKey.get(-3);
|
const negThree = publicKey.get(-3);
|
||||||
if (!negThree || negThree.length != 32) {
|
if (!negThree || negThree.length !== 32) {
|
||||||
throw new Error('invalid or no -3 key given');
|
throw new Error('invalid or no -3 key given');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ export const procedures = {
|
||||||
rpIdHash: Buffer,
|
rpIdHash: Buffer,
|
||||||
credentialId: Buffer,
|
credentialId: Buffer,
|
||||||
}) {
|
}) {
|
||||||
if (attStmt.alg != -7) {
|
if (attStmt.alg !== -7) {
|
||||||
throw new Error('alg mismatch');
|
throw new Error('alg mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,11 +196,11 @@ export const procedures = {
|
||||||
|
|
||||||
const negTwo = publicKey.get(-2);
|
const negTwo = publicKey.get(-2);
|
||||||
|
|
||||||
if (!negTwo || negTwo.length != 32) {
|
if (!negTwo || negTwo.length !== 32) {
|
||||||
throw new Error('invalid or no -2 key given');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree = publicKey.get(-3);
|
const negThree = publicKey.get(-3);
|
||||||
if (!negThree || negThree.length != 32) {
|
if (!negThree || negThree.length !== 32) {
|
||||||
throw new Error('invalid or no -3 key given');
|
throw new Error('invalid or no -3 key given');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ export const procedures = {
|
||||||
.map((key: any) => PEMString(key))
|
.map((key: any) => PEMString(key))
|
||||||
.concat([GSR2]);
|
.concat([GSR2]);
|
||||||
|
|
||||||
if (getCertSubject(certificateChain[0]).CN != 'attest.android.com') {
|
if (getCertSubject(certificateChain[0]).CN !== 'attest.android.com') {
|
||||||
throw new Error('invalid common name');
|
throw new Error('invalid common name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,11 +283,11 @@ export const procedures = {
|
||||||
|
|
||||||
const negTwo = publicKey.get(-2);
|
const negTwo = publicKey.get(-2);
|
||||||
|
|
||||||
if (!negTwo || negTwo.length != 32) {
|
if (!negTwo || negTwo.length !== 32) {
|
||||||
throw new Error('invalid or no -2 key given');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree = publicKey.get(-3);
|
const negThree = publicKey.get(-3);
|
||||||
if (!negThree || negThree.length != 32) {
|
if (!negThree || negThree.length !== 32) {
|
||||||
throw new Error('invalid or no -3 key given');
|
throw new Error('invalid or no -3 key given');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,11 +332,11 @@ export const procedures = {
|
||||||
|
|
||||||
const negTwo = publicKey.get(-2);
|
const negTwo = publicKey.get(-2);
|
||||||
|
|
||||||
if (!negTwo || negTwo.length != 32) {
|
if (!negTwo || negTwo.length !== 32) {
|
||||||
throw new Error('invalid or no -2 key given');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree = publicKey.get(-3);
|
const negThree = publicKey.get(-3);
|
||||||
if (!negThree || negThree.length != 32) {
|
if (!negThree || negThree.length !== 32) {
|
||||||
throw new Error('invalid or no -3 key given');
|
throw new Error('invalid or no -3 key given');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +353,7 @@ export const procedures = {
|
||||||
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation
|
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation
|
||||||
throw new Error('ECDAA-Verify is not supported');
|
throw new Error('ECDAA-Verify is not supported');
|
||||||
} else {
|
} else {
|
||||||
if (attStmt.alg != -7) throw new Error('alg mismatch');
|
if (attStmt.alg !== -7) throw new Error('alg mismatch');
|
||||||
|
|
||||||
throw new Error('self attestation is not supported');
|
throw new Error('self attestation is not supported');
|
||||||
}
|
}
|
||||||
|
@ -377,7 +377,7 @@ export const procedures = {
|
||||||
credentialId: Buffer
|
credentialId: Buffer
|
||||||
}) {
|
}) {
|
||||||
const x5c: Buffer[] = attStmt.x5c;
|
const x5c: Buffer[] = attStmt.x5c;
|
||||||
if (x5c.length != 1) {
|
if (x5c.length !== 1) {
|
||||||
throw new Error('x5c length does not match expectation');
|
throw new Error('x5c length does not match expectation');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,11 +387,11 @@ export const procedures = {
|
||||||
|
|
||||||
const negTwo: Buffer = publicKey.get(-2);
|
const negTwo: Buffer = publicKey.get(-2);
|
||||||
|
|
||||||
if (!negTwo || negTwo.length != 32) {
|
if (!negTwo || negTwo.length !== 32) {
|
||||||
throw new Error('invalid or no -2 key given');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree: Buffer = publicKey.get(-3);
|
const negThree: Buffer = publicKey.get(-3);
|
||||||
if (!negThree || negThree.length != 32) {
|
if (!negThree || negThree.length !== 32) {
|
||||||
throw new Error('invalid or no -3 key given');
|
throw new Error('invalid or no -3 key given');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,12 @@ export const paramDef = {
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) },
|
type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) },
|
||||||
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
|
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
|
||||||
hostname: { type: 'string', nullable: true, default: null },
|
hostname: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -40,6 +40,7 @@ export const meta = {
|
||||||
userHost: {
|
userHost: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
md5: {
|
md5: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -151,11 +152,20 @@ export const meta = {
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
properties: {
|
properties: {
|
||||||
fileId: { type: 'string', format: 'misskey:id' },
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['fileId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
url: { type: 'string' },
|
url: { type: 'string' },
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['url'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
|
|
@ -40,6 +40,7 @@ export const meta = {
|
||||||
host: {
|
host: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -54,7 +55,12 @@ export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
query: { type: 'string', nullable: true, default: null },
|
query: { type: 'string', nullable: true, default: null },
|
||||||
host: { 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 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
|
|
|
@ -38,8 +38,9 @@ export const meta = {
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
host: {
|
host: {
|
||||||
type: 'string',
|
type: 'null',
|
||||||
optional: false, nullable: true,
|
optional: false,
|
||||||
|
description: 'The local host is represented with `null`. The field exists for compatibility with other API endpoints that return files.',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
|
|
@ -17,7 +17,11 @@ export const paramDef = {
|
||||||
ids: { type: 'array', items: {
|
ids: { type: 'array', items: {
|
||||||
type: 'string', format: 'misskey:id',
|
type: 'string', format: 'misskey:id',
|
||||||
} },
|
} },
|
||||||
category: { type: 'string', nullable: true },
|
category: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'Use `null` to reset the category.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ['ids'],
|
required: ['ids'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -23,7 +23,11 @@ export const paramDef = {
|
||||||
properties: {
|
properties: {
|
||||||
id: { type: 'string', format: 'misskey:id' },
|
id: { type: 'string', format: 'misskey:id' },
|
||||||
name: { type: 'string' },
|
name: { type: 'string' },
|
||||||
category: { type: 'string', nullable: true },
|
category: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'Use `null` to reset the category.',
|
||||||
|
},
|
||||||
aliases: { type: 'array', items: {
|
aliases: { type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
} },
|
} },
|
||||||
|
|
|
@ -26,8 +26,13 @@ export const paramDef = {
|
||||||
sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] },
|
sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] },
|
||||||
state: { type: 'string', enum: ['all', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: "all" },
|
state: { type: 'string', enum: ['all', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: "all" },
|
||||||
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
|
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
|
||||||
username: { type: 'string', default: null },
|
username: { type: 'string', nullable: true, default: null },
|
||||||
hostname: { type: 'string', default: null },
|
hostname: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -397,12 +397,14 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.transaction(async transactionalEntityManager => {
|
await db.transaction(async transactionalEntityManager => {
|
||||||
const meta = await transactionalEntityManager.findOne(Meta, {
|
const metas = await transactionalEntityManager.find(Meta, {
|
||||||
order: {
|
order: {
|
||||||
id: 'DESC',
|
id: 'DESC',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const meta = metas[0];
|
||||||
|
|
||||||
if (meta) {
|
if (meta) {
|
||||||
await transactionalEntityManager.update(Meta, meta.id, set);
|
await transactionalEntityManager.update(Meta, meta.id, set);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: 'string', minLength: 1, maxLength: 100 },
|
name: { type: 'string', minLength: 1, maxLength: 100 },
|
||||||
isPublic: { type: 'boolean' },
|
isPublic: { type: 'boolean', default: false },
|
||||||
description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 },
|
description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 },
|
||||||
},
|
},
|
||||||
required: ['name'],
|
required: ['name'],
|
||||||
|
|
|
@ -48,7 +48,6 @@ export const paramDef = {
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
// @ts-ignore
|
|
||||||
export default define(meta, paramDef, async (ps, user, _, file, cleanup) => {
|
export default define(meta, paramDef, async (ps, user, _, file, cleanup) => {
|
||||||
// Get 'name' parameter
|
// Get 'name' parameter
|
||||||
let name = ps.name || file.originalname;
|
let name = ps.name || file.originalname;
|
||||||
|
|
|
@ -28,22 +28,25 @@ export const meta = {
|
||||||
code: 'ACCESS_DENIED',
|
code: 'ACCESS_DENIED',
|
||||||
id: '25b73c73-68b1-41d0-bad1-381cfdf6579f',
|
id: '25b73c73-68b1-41d0-bad1-381cfdf6579f',
|
||||||
},
|
},
|
||||||
|
|
||||||
fileIdOrUrlRequired: {
|
|
||||||
message: 'fileId or url required.',
|
|
||||||
code: 'INVALID_PARAM',
|
|
||||||
id: '89674805-722c-440c-8d88-5641830dc3e4',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
properties: {
|
properties: {
|
||||||
fileId: { type: 'string', format: 'misskey:id' },
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['fileId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
url: { type: 'string' },
|
url: { type: 'string' },
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['url'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -62,8 +65,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
thumbnailUrl: ps.url,
|
thumbnailUrl: ps.url,
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
throw new ApiError(meta.errors.fileIdOrUrlRequired);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
host: { type: 'string', nullable: true },
|
host: { type: 'string', nullable: true, description: 'Omit or use `null` to not filter by host.' },
|
||||||
blocked: { type: 'boolean', nullable: true },
|
blocked: { type: 'boolean', nullable: true },
|
||||||
notResponding: { type: 'boolean', nullable: true },
|
notResponding: { type: 'boolean', nullable: true },
|
||||||
suspended: { type: 'boolean', nullable: true },
|
suspended: { type: 'boolean', nullable: true },
|
||||||
|
|
|
@ -50,10 +50,10 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
|
||||||
const clientData = JSON.parse(ps.clientDataJSON);
|
const clientData = JSON.parse(ps.clientDataJSON);
|
||||||
|
|
||||||
if (clientData.type != 'webauthn.create') {
|
if (clientData.type !== 'webauthn.create') {
|
||||||
throw new Error('not a creation attestation');
|
throw new Error('not a creation attestation');
|
||||||
}
|
}
|
||||||
if (clientData.origin != config.scheme + '://' + config.host) {
|
if (clientData.origin !== config.scheme + '://' + config.host) {
|
||||||
throw new Error('origin mismatch');
|
throw new Error('origin mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const credentialId = authData.slice(55, 55 + credentialIdLength);
|
const credentialId = authData.slice(55, 55 + credentialIdLength);
|
||||||
const publicKeyData = authData.slice(55 + credentialIdLength);
|
const publicKeyData = authData.slice(55 + credentialIdLength);
|
||||||
const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData);
|
const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData);
|
||||||
if (publicKey.get(3) != -7) {
|
if (publicKey.get(3) !== -7) {
|
||||||
throw new Error('alg mismatch');
|
throw new Error('alg mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
take: ps.limit,
|
take: ps.limit,
|
||||||
skip: ps.offset,
|
skip: ps.offset,
|
||||||
order: {
|
order: {
|
||||||
id: ps.sort == 'asc' ? 1 : -1,
|
id: ps.sort === 'asc' ? 1 : -1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -47,14 +47,25 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
|
||||||
groupId: { type: 'string', format: 'misskey:id' },
|
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
markAsRead: { type: 'boolean', default: true },
|
markAsRead: { type: 'boolean', default: true },
|
||||||
},
|
},
|
||||||
required: [],
|
anyOf: [
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
groupId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['groupId'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -126,7 +137,5 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, {
|
return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, {
|
||||||
populateGroup: false,
|
populateGroup: false,
|
||||||
})));
|
})));
|
||||||
} else {
|
|
||||||
throw new Error();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -67,12 +67,23 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
|
||||||
groupId: { type: 'string', format: 'misskey:id' },
|
|
||||||
text: { type: 'string', nullable: true, maxLength: 3000 },
|
text: { type: 'string', nullable: true, maxLength: 3000 },
|
||||||
fileId: { type: 'string', format: 'misskey:id' },
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
},
|
},
|
||||||
required: [],
|
anyOf: [
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
groupId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['groupId'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
|
|
@ -169,6 +169,7 @@ export const meta = {
|
||||||
host: {
|
host: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
|
|
@ -38,7 +38,11 @@ export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
expiresAt: { type: 'integer', nullable: true },
|
expiresAt: {
|
||||||
|
type: 'integer',
|
||||||
|
nullable: true,
|
||||||
|
description: 'A Unix Epoch timestamp that must lie in the future. `null` means an indefinite mute.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ['userId'],
|
required: ['userId'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
local: { type: 'boolean' },
|
local: { type: 'boolean', default: false },
|
||||||
reply: { type: 'boolean' },
|
reply: { type: 'boolean' },
|
||||||
renote: { type: 'boolean' },
|
renote: { type: 'boolean' },
|
||||||
withFiles: { type: 'boolean' },
|
withFiles: { type: 'boolean' },
|
||||||
|
@ -52,19 +52,19 @@ export default define(meta, paramDef, async (ps) => {
|
||||||
query.andWhere('note.userHost IS NULL');
|
query.andWhere('note.userHost IS NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.reply != undefined) {
|
if (ps.reply !== undefined) {
|
||||||
query.andWhere(ps.reply ? 'note.replyId IS NOT NULL' : 'note.replyId IS NULL');
|
query.andWhere(ps.reply ? 'note.replyId IS NOT NULL' : 'note.replyId IS NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.renote != undefined) {
|
if (ps.renote !== undefined) {
|
||||||
query.andWhere(ps.renote ? 'note.renoteId IS NOT NULL' : 'note.renoteId IS NULL');
|
query.andWhere(ps.renote ? 'note.renoteId IS NOT NULL' : 'note.renoteId IS NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.withFiles != undefined) {
|
if (ps.withFiles !== undefined) {
|
||||||
query.andWhere(ps.withFiles ? `note.fileIds != '{}'` : `note.fileIds = '{}'`);
|
query.andWhere(ps.withFiles ? `note.fileIds != '{}'` : `note.fileIds = '{}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.poll != undefined) {
|
if (ps.poll !== undefined) {
|
||||||
query.andWhere(ps.poll ? 'note.hasPoll = TRUE' : 'note.hasPoll = FALSE');
|
query.andWhere(ps.poll ? 'note.hasPoll = TRUE' : 'note.hasPoll = FALSE');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
conversation.push(p);
|
conversation.push(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversation.length == ps.limit) {
|
if (conversation.length === ps.limit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,12 +59,6 @@ export const meta = {
|
||||||
id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15',
|
id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15',
|
||||||
},
|
},
|
||||||
|
|
||||||
contentRequired: {
|
|
||||||
message: 'Content required. You need to set text, fileIds, renoteId or poll.',
|
|
||||||
code: 'CONTENT_REQUIRED',
|
|
||||||
id: '6f57e42b-c348-439b-bc45-993995cc515a',
|
|
||||||
},
|
|
||||||
|
|
||||||
cannotCreateAlreadyExpiredPoll: {
|
cannotCreateAlreadyExpiredPoll: {
|
||||||
message: 'Poll is already expired.',
|
message: 'Poll is already expired.',
|
||||||
code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL',
|
code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL',
|
||||||
|
@ -92,29 +86,41 @@ export const paramDef = {
|
||||||
visibleUserIds: { type: 'array', uniqueItems: true, items: {
|
visibleUserIds: { type: 'array', uniqueItems: true, items: {
|
||||||
type: 'string', format: 'misskey:id',
|
type: 'string', format: 'misskey:id',
|
||||||
} },
|
} },
|
||||||
text: { type: 'string', nullable: true, maxLength: MAX_NOTE_TEXT_LENGTH, default: null },
|
text: { type: 'string', maxLength: MAX_NOTE_TEXT_LENGTH, nullable: true },
|
||||||
cw: { type: 'string', nullable: true, maxLength: 100 },
|
cw: { type: 'string', nullable: true, maxLength: 100 },
|
||||||
localOnly: { type: 'boolean', default: false },
|
localOnly: { type: 'boolean', default: false },
|
||||||
noExtractMentions: { type: 'boolean', default: false },
|
noExtractMentions: { type: 'boolean', default: false },
|
||||||
noExtractHashtags: { type: 'boolean', default: false },
|
noExtractHashtags: { type: 'boolean', default: false },
|
||||||
noExtractEmojis: { type: 'boolean', default: false },
|
noExtractEmojis: { type: 'boolean', default: false },
|
||||||
fileIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 16, items: {
|
fileIds: {
|
||||||
type: 'string', format: 'misskey:id',
|
type: 'array',
|
||||||
} },
|
uniqueItems: true,
|
||||||
mediaIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 16, items: {
|
minItems: 1,
|
||||||
type: 'string', format: 'misskey:id',
|
maxItems: 16,
|
||||||
} },
|
items: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
mediaIds: {
|
||||||
|
deprecated: true,
|
||||||
|
description: 'Use `fileIds` instead. If both are specified, this property is discarded.',
|
||||||
|
type: 'array',
|
||||||
|
uniqueItems: true,
|
||||||
|
minItems: 1,
|
||||||
|
maxItems: 16,
|
||||||
|
items: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
replyId: { type: 'string', format: 'misskey:id', nullable: true },
|
replyId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
renoteId: { type: 'string', format: 'misskey:id', nullable: true },
|
renoteId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
channelId: { type: 'string', format: 'misskey:id', nullable: true },
|
channelId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
poll: {
|
poll: {
|
||||||
type: 'object', nullable: true,
|
type: 'object',
|
||||||
|
nullable: true,
|
||||||
properties: {
|
properties: {
|
||||||
choices: {
|
choices: {
|
||||||
type: 'array', uniqueItems: true, minItems: 2, maxItems: 10,
|
type: 'array',
|
||||||
items: {
|
uniqueItems: true,
|
||||||
type: 'string', minLength: 1, maxLength: 50,
|
minItems: 2,
|
||||||
},
|
maxItems: 10,
|
||||||
|
items: { type: 'string', minLength: 1, maxLength: 50 },
|
||||||
},
|
},
|
||||||
multiple: { type: 'boolean', default: false },
|
multiple: { type: 'boolean', default: false },
|
||||||
expiresAt: { type: 'integer', nullable: true },
|
expiresAt: { type: 'integer', nullable: true },
|
||||||
|
@ -123,7 +129,34 @@ export const paramDef = {
|
||||||
required: ['choices'],
|
required: ['choices'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
anyOf: [
|
||||||
|
{
|
||||||
|
// (re)note with text, files and poll are optional
|
||||||
|
properties: {
|
||||||
|
text: { type: 'string', maxLength: MAX_NOTE_TEXT_LENGTH, nullable: false },
|
||||||
|
},
|
||||||
|
required: ['text'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// (re)note with files, text and poll are optional
|
||||||
|
required: ['fileIds'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// (re)note with files, text and poll are optional
|
||||||
|
required: ['mediaIds'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// (re)note with poll, text and files are optional
|
||||||
|
properties: {
|
||||||
|
poll: { type: 'object', nullable: false, },
|
||||||
|
},
|
||||||
|
required: ['poll'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// pure renote
|
||||||
|
required: ['renoteId'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -152,7 +185,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
|
||||||
if (renote == null) {
|
if (renote == null) {
|
||||||
throw new ApiError(meta.errors.noSuchRenoteTarget);
|
throw new ApiError(meta.errors.noSuchRenoteTarget);
|
||||||
} else if (renote.renoteId && !renote.text && !renote.fileIds) {
|
} else if (renote.renoteId && !renote.text && !renote.fileIds && !renote.poll) {
|
||||||
throw new ApiError(meta.errors.cannotReRenote);
|
throw new ApiError(meta.errors.cannotReRenote);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,10 +208,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
|
||||||
if (reply == null) {
|
if (reply == null) {
|
||||||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||||
}
|
} else if (reply.renoteId && !reply.text && !reply.fileIds && !renote.poll) {
|
||||||
|
|
||||||
// 返信対象が引用でないRenoteだったらエラー
|
|
||||||
if (reply.renoteId && !reply.text && !reply.fileIds) {
|
|
||||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,11 +234,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー
|
|
||||||
if (!(ps.text || files.length || renote || ps.poll)) {
|
|
||||||
throw new ApiError(meta.errors.contentRequired);
|
|
||||||
}
|
|
||||||
|
|
||||||
let channel: Channel | undefined;
|
let channel: Channel | undefined;
|
||||||
if (ps.channelId != null) {
|
if (ps.channelId != null) {
|
||||||
channel = await Channels.findOneBy({ id: ps.channelId });
|
channel = await Channels.findOneBy({ id: ps.channelId });
|
||||||
|
|
|
@ -35,7 +35,11 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
withFiles: { type: 'boolean' },
|
withFiles: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Only show notes that have attached files.',
|
||||||
|
},
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
|
|
|
@ -48,7 +48,11 @@ export const paramDef = {
|
||||||
includeMyRenotes: { type: 'boolean', default: true },
|
includeMyRenotes: { type: 'boolean', default: true },
|
||||||
includeRenotedMyNotes: { type: 'boolean', default: true },
|
includeRenotedMyNotes: { type: 'boolean', default: true },
|
||||||
includeLocalRenotes: { type: 'boolean', default: true },
|
includeLocalRenotes: { type: 'boolean', default: true },
|
||||||
withFiles: { type: 'boolean' },
|
withFiles: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Only show notes that have attached files.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -37,7 +37,11 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
withFiles: { type: 'boolean' },
|
withFiles: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Only show notes that have attached files.',
|
||||||
|
},
|
||||||
fileType: { type: 'array', items: {
|
fileType: { type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
} },
|
} },
|
||||||
|
|
|
@ -110,7 +110,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
|
||||||
if (exist.length) {
|
if (exist.length) {
|
||||||
if (poll.multiple) {
|
if (poll.multiple) {
|
||||||
if (exist.some(x => x.choice == ps.choice)) {
|
if (exist.some(x => x.choice === ps.choice)) {
|
||||||
throw new ApiError(meta.errors.alreadyVoted);
|
throw new ApiError(meta.errors.alreadyVoted);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -25,21 +25,44 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
tag: { type: 'string' },
|
|
||||||
query: { type: 'array', items: {
|
|
||||||
type: 'array', items: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
} },
|
|
||||||
reply: { type: 'boolean', nullable: true, default: null },
|
reply: { type: 'boolean', nullable: true, default: null },
|
||||||
renote: { type: 'boolean', nullable: true, default: null },
|
renote: { type: 'boolean', nullable: true, default: null },
|
||||||
withFiles: { type: 'boolean' },
|
withFiles: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Only show notes that have attached files.',
|
||||||
|
},
|
||||||
poll: { type: 'boolean', nullable: true, default: null },
|
poll: { type: 'boolean', nullable: true, default: null },
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
},
|
},
|
||||||
required: [],
|
anyOf: [
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
tag: { type: 'string', minLength: 1 },
|
||||||
|
},
|
||||||
|
required: ['tag'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
query: {
|
||||||
|
type: 'array',
|
||||||
|
description: 'The outer arrays are chained with OR, the inner arrays are chained with AND.',
|
||||||
|
items: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
minLength: 1,
|
||||||
|
},
|
||||||
|
minItems: 1,
|
||||||
|
},
|
||||||
|
minItems: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['query'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
|
|
@ -42,7 +42,11 @@ export const paramDef = {
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
offset: { type: 'integer', default: 0 },
|
offset: { type: 'integer', default: 0 },
|
||||||
host: { type: 'string', nullable: true },
|
host: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
|
},
|
||||||
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
||||||
channelId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
channelId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
||||||
},
|
},
|
||||||
|
|
|
@ -38,7 +38,11 @@ export const paramDef = {
|
||||||
includeMyRenotes: { type: 'boolean', default: true },
|
includeMyRenotes: { type: 'boolean', default: true },
|
||||||
includeRenotedMyNotes: { type: 'boolean', default: true },
|
includeRenotedMyNotes: { type: 'boolean', default: true },
|
||||||
includeLocalRenotes: { type: 'boolean', default: true },
|
includeLocalRenotes: { type: 'boolean', default: true },
|
||||||
withFiles: { type: 'boolean' },
|
withFiles: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Only show notes that have attached files.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -75,7 +75,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
Accept: 'application/json, */*',
|
Accept: 'application/json, */*',
|
||||||
},
|
},
|
||||||
body: params,
|
body: params,
|
||||||
timeout: 10000,
|
// TODO
|
||||||
|
//timeout: 10000,
|
||||||
agent: getAgentByUrl,
|
agent: getAgentByUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,11 @@ export const paramDef = {
|
||||||
includeMyRenotes: { type: 'boolean', default: true },
|
includeMyRenotes: { type: 'boolean', default: true },
|
||||||
includeRenotedMyNotes: { type: 'boolean', default: true },
|
includeRenotedMyNotes: { type: 'boolean', default: true },
|
||||||
includeLocalRenotes: { type: 'boolean', default: true },
|
includeLocalRenotes: { type: 'boolean', default: true },
|
||||||
withFiles: { type: 'boolean' },
|
withFiles: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Only show notes that have attached files.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ['listId'],
|
required: ['listId'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -26,12 +26,21 @@ export const meta = {
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
properties: {
|
properties: {
|
||||||
pageId: { type: 'string', format: 'misskey:id' },
|
pageId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['pageId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
name: { type: 'string' },
|
name: { type: 'string' },
|
||||||
username: { type: 'string' },
|
username: { type: 'string' },
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['name', 'username'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
|
|
@ -38,14 +38,29 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
|
||||||
username: { type: 'string' },
|
|
||||||
host: { type: 'string', nullable: true },
|
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
},
|
},
|
||||||
required: [],
|
anyOf: [
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
username: { type: 'string' },
|
||||||
|
host: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['username', 'host'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
|
|
@ -38,14 +38,29 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
|
||||||
username: { type: 'string' },
|
|
||||||
host: { type: 'string', nullable: true },
|
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
},
|
},
|
||||||
required: [],
|
anyOf: [
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
username: { type: 'string' },
|
||||||
|
host: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['username', 'host'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
|
|
@ -28,7 +28,10 @@ export const paramDef = {
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
detail: { type: 'boolean', default: true },
|
detail: { type: 'boolean', default: true },
|
||||||
},
|
},
|
||||||
required: [],
|
anyOf: [
|
||||||
|
{ required: ['username'] },
|
||||||
|
{ required: ['host'] },
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// TODO: avatar,bannerをJOINしたいけどエラーになる
|
// TODO: avatar,bannerをJOINしたいけどエラーになる
|
||||||
|
|
|
@ -46,15 +46,33 @@ export const meta = {
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
userIds: { type: 'array', uniqueItems: true, items: {
|
userIds: { type: 'array', uniqueItems: true, items: {
|
||||||
type: 'string', format: 'misskey:id',
|
type: 'string', format: 'misskey:id',
|
||||||
} },
|
} },
|
||||||
username: { type: 'string' },
|
|
||||||
host: { type: 'string', nullable: true },
|
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['userIds'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
username: { type: 'string' },
|
||||||
|
host: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['username', 'host'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
|
|
@ -24,17 +24,17 @@ export default async (ctx: Koa.Context) => {
|
||||||
ctx.body = { error };
|
ctx.body = { error };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof username != 'string') {
|
if (typeof username !== 'string') {
|
||||||
ctx.status = 400;
|
ctx.status = 400;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof password != 'string') {
|
if (typeof password !== 'string') {
|
||||||
ctx.status = 400;
|
ctx.status = 400;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token != null && typeof token != 'string') {
|
if (token != null && typeof token !== 'string') {
|
||||||
ctx.status = 400;
|
ctx.status = 400;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,7 @@ async function cancelRequest(follower: User, followee: User) {
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'unfollow', {
|
||||||
type: 'unfollow',
|
|
||||||
user: packed,
|
user: packed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -118,8 +117,7 @@ async function unFollow(follower: User, followee: User) {
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'unfollow', {
|
||||||
type: 'unfollow',
|
|
||||||
user: packed,
|
user: packed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ export async function uploadFromUrl({
|
||||||
sensitive = false,
|
sensitive = false,
|
||||||
force = false,
|
force = false,
|
||||||
isLink = false,
|
isLink = false,
|
||||||
comment = null
|
comment = null,
|
||||||
}: Args): Promise<DriveFile> {
|
}: Args): Promise<DriveFile> {
|
||||||
let name = new URL(url).pathname.split('/').pop() || null;
|
let name = new URL(url).pathname.split('/').pop() || null;
|
||||||
if (name == null || !DriveFiles.validateFileName(name)) {
|
if (name == null || !DriveFiles.validateFileName(name)) {
|
||||||
|
@ -38,7 +38,7 @@ export async function uploadFromUrl({
|
||||||
|
|
||||||
// If the comment is same as the name, skip comment
|
// If the comment is same as the name, skip comment
|
||||||
// (image.name is passed in when receiving attachment)
|
// (image.name is passed in when receiving attachment)
|
||||||
if (comment !== null && name == comment) {
|
if (comment !== null && name === comment) {
|
||||||
comment = null;
|
comment = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ async function fetchNodeinfo(instance: Instance): Promise<NodeInfo> {
|
||||||
} else {
|
} else {
|
||||||
throw e.statusCode || e.message;
|
throw e.statusCode || e.message;
|
||||||
}
|
}
|
||||||
});
|
}) as Record<string, unknown>;
|
||||||
|
|
||||||
if (wellknown.links == null || !Array.isArray(wellknown.links)) {
|
if (wellknown.links == null || !Array.isArray(wellknown.links)) {
|
||||||
throw 'No wellknown links';
|
throw 'No wellknown links';
|
||||||
|
@ -121,7 +121,7 @@ async function fetchNodeinfo(instance: Instance): Promise<NodeInfo> {
|
||||||
|
|
||||||
logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`);
|
logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`);
|
||||||
|
|
||||||
return info;
|
return info as NodeInfo;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e}`);
|
logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e}`);
|
||||||
|
|
||||||
|
@ -142,12 +142,12 @@ async function fetchDom(instance: Instance): Promise<DOMWindow['document']> {
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchManifest(instance: Instance): Promise<Record<string, any> | null> {
|
async function fetchManifest(instance: Instance): Promise<Record<string, unknown> | null> {
|
||||||
const url = 'https://' + instance.host;
|
const url = 'https://' + instance.host;
|
||||||
|
|
||||||
const manifestUrl = url + '/manifest.json';
|
const manifestUrl = url + '/manifest.json';
|
||||||
|
|
||||||
const manifest = await getJson(manifestUrl);
|
const manifest = await getJson(manifestUrl) as Record<string, unknown>;
|
||||||
|
|
||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,8 @@ async function fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] |
|
||||||
const faviconUrl = url + '/favicon.ico';
|
const faviconUrl = url + '/favicon.ico';
|
||||||
|
|
||||||
const favicon = await fetch(faviconUrl, {
|
const favicon = await fetch(faviconUrl, {
|
||||||
timeout: 10000,
|
// TODO
|
||||||
|
//timeout: 10000,
|
||||||
agent: getAgentByUrl,
|
agent: getAgentByUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -97,8 +97,7 @@ export async function insertFollowingDoc(followee: { id: User['id']; host: User[
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'follow', {
|
||||||
type: 'follow',
|
|
||||||
user: packed,
|
user: packed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -108,12 +107,11 @@ export async function insertFollowingDoc(followee: { id: User['id']; host: User[
|
||||||
// Publish followed event
|
// Publish followed event
|
||||||
if (Users.isLocalUser(followee)) {
|
if (Users.isLocalUser(followee)) {
|
||||||
Users.pack(follower.id, followee).then(async packed => {
|
Users.pack(follower.id, followee).then(async packed => {
|
||||||
publishMainStream(followee.id, 'followed', packed)
|
publishMainStream(followee.id, 'followed', packed);
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === followee.id && x.on.includes('followed'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === followee.id && x.on.includes('followed'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'followed', {
|
||||||
type: 'followed',
|
|
||||||
user: packed,
|
user: packed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,7 @@ export default async function(follower: { id: User['id']; host: User['host']; ur
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'unfollow', {
|
||||||
type: 'unfollow',
|
|
||||||
user: packed,
|
user: packed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,8 +115,7 @@ async function publishUnfollow(followee: Both, follower: Local) {
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'unfollow', {
|
||||||
type: 'unfollow',
|
|
||||||
user: packedFollowee,
|
user: packedFollowee,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -370,8 +370,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
getActiveWebhooks().then(webhooks => {
|
getActiveWebhooks().then(webhooks => {
|
||||||
webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note'));
|
webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'note', {
|
||||||
type: 'note',
|
|
||||||
note: noteObj,
|
note: noteObj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -400,8 +399,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'reply', {
|
||||||
type: 'reply',
|
|
||||||
note: noteObj,
|
note: noteObj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -427,8 +425,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'renote', {
|
||||||
type: 'renote',
|
|
||||||
note: noteObj,
|
note: noteObj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -662,8 +659,7 @@ async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note,
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, {
|
webhookDeliver(webhook, 'mention', {
|
||||||
type: 'mention',
|
|
||||||
note: detailPackedNote,
|
note: detailPackedNote,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,14 +51,14 @@ export function applyTheme(theme: Theme, persist = true) {
|
||||||
|
|
||||||
if (_theme.base) {
|
if (_theme.base) {
|
||||||
const base = [lightTheme, darkTheme].find(x => x.id === _theme.base);
|
const base = [lightTheme, darkTheme].find(x => x.id === _theme.base);
|
||||||
_theme.props = Object.assign({}, base.props, _theme.props);
|
if (base) _theme.props = Object.assign({}, base.props, _theme.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = compile(_theme);
|
const props = compile(_theme);
|
||||||
|
|
||||||
for (const tag of document.head.children) {
|
for (const tag of document.head.children) {
|
||||||
if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
|
if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
|
||||||
tag.setAttribute('content', props['html']);
|
tag.setAttribute('content', props['htmlThemeColor']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue