Firefish v1.0.5-dev22

This commit is contained in:
naskya 2023-11-27 19:32:26 +09:00
parent 74f7ef6b98
commit 9fcd3e496f
Signed by: naskya
GPG key ID: 164DFF24E2D40139
71 changed files with 359 additions and 694 deletions

View file

@ -1,195 +0,0 @@
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Firefish configuration
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ┌─────┐
#───┘ URL └─────────────────────────────────────────────────────
# Final accessible URL seen by a user.
url: https://example.tld/
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# URL SETTINGS AFTER THAT!
# ┌───────────────────────┐
#───┘ Port and TLS settings └───────────────────────────────────
#
# Misskey requires a reverse proxy to support HTTPS connections.
#
# +----- https://example.tld/ ------------+
# +------+ |+-------------+ +----------------+|
# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
# +------+ |+-------------+ +----------------+|
# +---------------------------------------+
#
# You need to set up a reverse proxy. (e.g. nginx)
# An encrypted connection with HTTPS is highly recommended
# because tokens may be transferred in GET requests.
# The port that your Misskey server should listen on.
port: 3000
# ┌──────────────────────────┐
#───┘ PostgreSQL configuration └────────────────────────────────
db:
host: postgres
port: 5432
# Database name
db: postgres
# Auth
user: postgres
pass: test
# Whether disable Caching queries
#disableCache: true
# Extra Connection options
#extra:
# ssl: true
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
redis:
host: redis
port: 6379
#family: 0 # 0=Both, 4=IPv4, 6=IPv6
#pass: example-pass
#prefix: example-prefix
#db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
# You can select the ID generation method.
# You don't usually need to change this setting, but you can
# change it according to your preferences.
# Available methods:
# aid ... Short, Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: 'aid'
# ┌─────────────────────┐
#───┘ Other configuration └─────────────────────────────────────
# Max note length, should be < 8000.
#maxNoteLength: 3000
# Whether disable HSTS
#disableHsts: true
# Number of worker processes
#clusterLimit: 1
# Job concurrency per worker
# deliverJobConcurrency: 128
# inboxJobConcurrency: 16
# Job rate limiter
# deliverJobPerSec: 128
# inboxJobPerSec: 16
# Job attempts
# deliverJobMaxAttempts: 12
# inboxJobMaxAttempts: 8
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4
# Syslog option
#syslog:
# host: localhost
# port: 514
# Proxy for HTTP/HTTPS
#proxy: http://127.0.0.1:3128
#proxyBypassHosts: [
# 'example.com',
# '192.0.2.8'
#]
# Proxy for SMTP/SMTPS
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
# Media Proxy
#mediaProxy: https://example.com/proxy
# Proxy remote files (default: false)
#proxyRemoteFiles: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]
# Upload or download file size limits (bytes)
#maxFileSize: 262144000
# Managed hosting settings
# !!!!!!!!!!
# >>>>>> NORMAL SELF-HOSTERS, STAY AWAY! <<<<<<
# >>>>>> YOU DON'T NEED THIS! <<<<<<
# !!!!!!!!!!
# Each category is optional, but if each item in each category is mandatory!
# If you mess this up, that's on you, you've been warned...
#maxUserSignups: 100
#isManagedHosting: true
#deepl:
# managed: true
# authKey: ''
# isPro: false
#
#email:
# managed: true
# address: 'example@email.com'
# host: 'email.com'
# port: 587
# user: 'example@email.com'
# pass: ''
# useImplicitSslTls: false
#
#objectStorage:
# managed: true
# baseUrl: ''
# bucket: ''
# prefix: ''
# endpoint: ''
# region: ''
# accessKey: ''
# secretKey: ''
# useSsl: true
# connnectOverProxy: false
# setPublicReadOnUpload: true
# s3ForcePathStyle: true
# !!!!!!!!!!
# >>>>>> AGAIN, NORMAL SELF-HOSTERS, STAY AWAY! <<<<<<
# >>>>>> YOU DON'T NEED THIS, ABOVE SETTINGS ARE FOR MANAGED HOSTING ONLY! <<<<<<
# !!!!!!!!!!
# Seriously. Do NOT fill out the above settings if you're self-hosting.
# They're much better off being set from the control panel.

View file

@ -1,38 +0,0 @@
url: http://localhost:3000
port: 3000
db:
host: 127.0.0.1
port: 5432
db: firefish
user: firefish
pass: firefish
redis:
host: localhost
port: 6379
family: 4
#sonic:
# host: localhost
# port: 1491
# auth: SecretPassword
# collection: notes
# bucket: default
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
id: 'aid'
reservedUsernames:
- root
- admin
- administrator
- me
- system

View file

@ -1,4 +0,0 @@
# db settings
POSTGRES_PASSWORD=test
POSTGRES_USER=postgres
POSTGRES_DB=postgres

View file

@ -1,82 +0,0 @@
replicaCount: 1
resources:
requests:
cpu: 0.5
memory: 512Mi
limits:
cpu: 1
memory: 1Gi
firefish:
domain: example.tld
smtp:
from_address: noreply@example.tld
port: 587
server: smtp.gmail.com
useImplicitSslTls: false
login: me@example.tld
password: CHANGEME
objectStorage:
baseUrl: https://example-bucket.nyc3.cdn.digitaloceanspaces.com
access_key: CHANGEME
access_secret: CHANGEME
bucket: example-bucket
endpoint: nyc3.digitaloceanspaces.com:443
region: nyc3
allowedPrivateNetworks: []
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt
hosts:
- host: example.tld
paths:
- path: /
pathType: ImplementationSpecific
tls:
- secretName: example-tld-certificate
hosts:
- example.tld
elasticsearch:
enabled: false
postgresql:
auth:
password: CHANGEME
postgresPassword: CHANGEME
primary:
persistence:
enabled: true
storageClass: vultr-block-storage
size: 25Gi
resources:
requests:
cpu: 0.25
memory: 256Mi
limits:
cpu: 0.5
memory: 512Mi
metrics:
enabled: true
redis:
auth:
password: CHANGEME
master:
resources:
requests:
cpu: 0.25
memory: 256Mi
limits:
cpu: 0.5
memory: 256Mi
persistence:
storageclass: vultr-block-storage
size: 10Gi
replica:
replicaCount: 0
metrics:
enabled: true

View file

@ -52,7 +52,7 @@ gulp.task("build:backend:script", () => {
"./packages/backend/src/server/web/bios.js",
"./packages/backend/src/server/web/cli.js",
])
.pipe(replace("LANGS", JSON.stringify(Object.keys(locales))))
.pipe(replace("SUPPORTED_LANGS", JSON.stringify(Object.keys(locales))))
.pipe(
terser({
toplevel: true,

View file

@ -112,7 +112,7 @@ you: "Tu"
clickToShow: "Fes clic per a mostrar"
sensitive: "NSFW"
add: "Afegeix"
reaction: "Reaccions"
reaction: "Reacció"
reactionSetting: "Reaccions a mostrar al selector de reaccions"
reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem
\"+\" per afegir."
@ -792,11 +792,11 @@ customEmojis: Emojis personalitzats
cacheRemoteFilesDescription: Quan aquesta opció està desactivada, els fitxers remots
es carreguen directament del servidor remot. Desactivar-la farà que baixi l'ús d'emmagatzematge,
però incrementa el tràfic, perquè les miniatures no es generaran.
flagAsBot: Marca aquest compte com a bot
flagAsBot: Marca aquest compte com automatitzat
flagAsBotDescription: Activa aquesta opció si aquest compte és controlat per un programa.
Si s'activa, això actuarà com una bandera per a altres desenvolupadors i ajuda a
prevenir cadenes de interaccions infinites amb altres bots a més d'ajustar els sistemes
interns de Firefish per tractar aquest compte com un bot.
prevenir cadenes de interaccions infinites amb altres comptes automatitzats a més
d'ajustar els sistemes interns de Firefish per tractar aquest compte com automatitzat.
flagAsCat: Ets un gat? 🐱
flagShowTimelineReplies: Mostra respostes a la línia de temps
flagAsCatDescription: Guanyaràs unes orelles de gat i parlares com un gat!
@ -2127,7 +2127,7 @@ clipsDesc: Els clips són com marcadors categoritzats que es poden compartir. Po
selectChannel: Selecciona un canal
isLocked: Aquest compte té les següents aprovacions
isPatron: Mecenes de Firefish
isBot: Aquest compte és un bot
isBot: Aquest es un compte automatitzat
isModerator: Moderador
isAdmin: Administrador
_filters:
@ -2199,3 +2199,16 @@ openServerInfo: Mostra la informació del servidor fent clic al símbol del serv
en un missatge
vibrate: Activar vibracions
clickToShowPatterns: Fes clic per veure patrons de mòduls
iconSet: Conjunt d'Icones
_iconSets:
fill: Omplerts
regular: Normals
bold: Negreta
duotone: Bitó
light: Prims
showAttachedNotes: Mostrar publicacions que contenen aquest fitxer
reactions: Reaccions
attachedToNotes: Publicacions que contenen aquest fitxer
replies: Respostes
quotes: Cites
renotes: Impulsos

View file

@ -2218,3 +2218,4 @@ openServerInfo: Anzeigen von Serverinformationen durch Anklicken des Server-Tick
in einem Beitrag
vibrate: Vibrationen abspielen
clickToShowPatterns: Klicken um Modul-Muster anzuzeigen
replies: Antworten

View file

@ -312,7 +312,7 @@ agreeTo: "I agree to {0}"
tos: "Terms of Service"
start: "Begin"
home: "Home"
remoteUserCaution: "Information from remote users may be incomplete."
remoteUserCaution: "Information from remote users are incomplete."
activity: "Activity"
images: "Images"
birthday: "Birthday"
@ -2188,5 +2188,5 @@ _iconSets:
regular: "Regular"
fill: "Filled"
duotone: "Duotone"
moreUrls: "pinned pages"
moreUrls: "Pinned pages"
moreUrlsDescription: "Enter the pages you want to pin to the help menu in the lower left corner using this notation:\n\"Display name\": https://example.com/"

View file

@ -278,8 +278,7 @@ agreeTo: "Saya setuju kepada {0}"
tos: "Syarat dan ketentuan"
start: "Mulai"
home: "Beranda"
remoteUserCaution: "Informasi ini mungkin tidak mutakhir, karena pengguna ini berasal
dari instansi luar."
remoteUserCaution: "Informasi dari pengguna luar tidak lengkap."
activity: "Aktivitas"
images: "Gambar"
birthday: "Tanggal lahir"
@ -2192,3 +2191,5 @@ reactions: Reaksi
replies: Balasan
quotes: Kutipan
renotes: Postingan ulang
showAttachedNotes: Tampilkan postingan dengan berkas ini
attachedToNotes: Posting dengan berkas ini

View file

@ -268,7 +268,7 @@ agreeTo: "Sono d'accordo con {0}"
tos: "Termini d'uso"
start: "Inizia"
home: "Home"
remoteUserCaution: "Le informazioni degli utenti remoti possono essere incomplete."
remoteUserCaution: "Le informazioni degli utenti remoti sono incomplete."
activity: "Attività"
images: "Immagini"
birthday: "Compleanno"
@ -2180,3 +2180,5 @@ reactions: Reazioni
replies: Risposte
quotes: Citazioni
renotes: Boost
showAttachedNotes: Mostra i post con questo allegato
attachedToNotes: Post con questo allegato

View file

@ -1 +1 @@
465a585f47037ce26834587429f0bd341e35f76f
a6aa7d2b74020dfc9303995918e63727fb3cb2c0

View file

@ -139,7 +139,7 @@ importers:
specifier: 6.0.1
version: 6.0.1
argon2:
specifier: 0.31.2
specifier: ^0.31.2
version: 0.31.2
aws-sdk:
specifier: 2.1498.0
@ -624,7 +624,7 @@ importers:
specifier: 0.16.0
version: 0.16.0
'@types/autosize':
specifier: 4.0.3
specifier: ^4.0.3
version: 4.0.3
'@types/glob':
specifier: 8.1.0
@ -717,7 +717,7 @@ importers:
specifier: 2.30.0
version: 2.30.0
emojilib:
specifier: 3.0.11
specifier: ^3.0.11
version: 3.0.11
eslint-config-prettier:
specifier: 9.0.0
@ -810,7 +810,7 @@ importers:
specifier: 1.6.0
version: 1.6.0
tinyld:
specifier: 1.3.4
specifier: ^1.3.4
version: 1.3.4
typescript:
specifier: 5.2.2
@ -831,10 +831,10 @@ importers:
specifier: 3.3.8
version: 3.3.8(typescript@5.2.2)
vue-draggable-plus:
specifier: 0.2.7
specifier: ^0.2.7
version: 0.2.7(@types/sortablejs@1.15.5)
vue-plyr:
specifier: 7.0.0
specifier: ^7.0.0
version: 7.0.0
vue-prism-editor:
specifier: 2.0.0-alpha.2

View file

@ -1,6 +1,6 @@
{
"name": "firefish",
"version": "1.0.5-dev21",
"version": "1.0.5-dev22",
"codename": "aqua",
"repository": {
"type": "git",

View file

@ -40,7 +40,7 @@
"adm-zip": "0.5.10",
"ajv": "8.12.0",
"archiver": "6.0.1",
"argon2": "0.31.2",
"argon2": "^0.31.2",
"aws-sdk": "2.1498.0",
"axios": "1.6.2",
"bcryptjs": "2.4.3",

View file

@ -1,12 +1,12 @@
import * as Acct from "@/misc/acct.js";
import { Cache } from "@/misc/cache.js";
import { getWordHardMute } from "@/misc/check-word-mute.js";
import { getFullApAccount } from "@/misc/convert-host.js";
import type { Packed } from "@/misc/schema.js";
import type { Antenna } from "@/models/entities/antenna.js";
import type { Note } from "@/models/entities/note.js";
import type { User } from "@/models/entities/user.js";
import { Blockings, Followings, UserProfiles } from "@/models/index.js";
import { getFullApAccount } from "./convert-host.js";
const blockingCache = new Cache<User["id"][]>("blocking", 60 * 5);
const mutedWordsCache = new Cache<string[][] | undefined>("mutedWords", 60 * 5);

View file

@ -1,25 +0,0 @@
// AID
// 長さ8の[2000年1月1日からの経過ミリ秒をbase36でエンコードしたもの] + 長さ2の[ノイズ文字列]
import * as crypto from "node:crypto";
const TIME2000 = 946684800000;
let counter = crypto.randomBytes(2).readUInt16LE(0);
function getTime(time: number) {
time = time - TIME2000;
if (time < 0) time = 0;
return time.toString(36).padStart(8, "0");
}
function getNoise() {
return counter.toString(36).padStart(2, "0").slice(-2);
}
export function genAid(date: Date): string {
const t = date.getTime();
if (Number.isNaN(t)) throw "Failed to create AID: Invalid Date";
counter++;
return getTime(t) + getNoise();
}

View file

@ -1,26 +0,0 @@
const CHARS = "0123456789abcdef";
function getTime(time: number) {
if (time < 0) time = 0;
if (time === 0) {
return CHARS[0];
}
time += 0x800000000000;
return time.toString(16).padStart(12, CHARS[0]);
}
function getRandom() {
let str = "";
for (let i = 0; i < 12; i++) {
str += CHARS[Math.floor(Math.random() * CHARS.length)];
}
return str;
}
export function genMeid(date: Date): string {
return getTime(date.getTime()) + getRandom();
}

View file

@ -1,28 +0,0 @@
const CHARS = "0123456789abcdef";
// 4bit Fixed hex value 'g'
// 44bit UNIX Time ms in Hex
// 48bit Random value in Hex
function getTime(time: number) {
if (time < 0) time = 0;
if (time === 0) {
return CHARS[0];
}
return time.toString(16).padStart(11, CHARS[0]);
}
function getRandom() {
let str = "";
for (let i = 0; i < 12; i++) {
str += CHARS[Math.floor(Math.random() * CHARS.length)];
}
return str;
}
export function genMeidg(date: Date): string {
return `g${getTime(date.getTime())}${getRandom()}`;
}

View file

@ -1,26 +0,0 @@
const CHARS = "0123456789abcdef";
function getTime(time: number) {
if (time < 0) time = 0;
if (time === 0) {
return CHARS[0];
}
time = Math.floor(time / 1000);
return time.toString(16).padStart(8, CHARS[0]);
}
function getRandom() {
let str = "";
for (let i = 0; i < 16; i++) {
str += CHARS[Math.floor(Math.random() * CHARS.length)];
}
return str;
}
export function genObjectId(date: Date): string {
return getTime(date.getTime()) + getRandom();
}

View file

@ -6,6 +6,7 @@ import { shouldBlockInstance } from "@/misc/should-block-instance.js";
import type { UserPublickey } from "@/models/entities/user-publickey.js";
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import { Instances } from "@/models/index.js";
import { verifySignature } from "@/remote/activitypub/check-fetch.js";
import DbResolver from "@/remote/activitypub/db-resolver.js";
import { LdSignature } from "@/remote/activitypub/misc/ld-signature.js";
import { resolvePerson } from "@/remote/activitypub/models/person.js";
@ -109,6 +110,12 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
);
}
if (httpSignatureValidated) {
if (!verifySignature(signature, authUser.key)) {
return "skip: Invalid HTTP signature";
}
}
// また、signatureのsignerは、activity.actorと一致する必要がある
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る

View file

@ -1,7 +1,7 @@
import type Bull from "bull";
import { activeUsersChart } from "@/services/chart/index.js";
import { queueLogger } from "../../logger.js";
import { queueLogger } from "@/queue/logger.js";
const logger = queueLogger.createSubLogger("clean-charts");

View file

@ -3,6 +3,8 @@ import { checkExpiredMutings } from "./check-expired-mutings.js";
import { cleanCharts } from "./clean-charts.js";
import { clean } from "./clean.js";
import { setLocalEmojiSizes } from "./local-emoji-size.js";
import { resyncCharts } from "./resync-charts.js";
import { tickCharts } from "./tick-charts.js";
import { verifyLinks } from "./verify-links.js";
const jobs = {

View file

@ -1,4 +1,6 @@
import type { IncomingMessage } from "http";
import { verify } from "node:crypto";
import { createHash } from "node:crypto";
import { URL } from "url";
import config from "@/config/index.js";
import { toPuny } from "@/misc/convert-host.js";
@ -6,9 +8,10 @@ import { fetchMeta } from "@/misc/fetch-meta.js";
import { shouldBlockInstance } from "@/misc/should-block-instance.js";
import type { UserPublickey } from "@/models/entities/user-publickey.js";
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import { toSingle } from "@/prelude/array.js";
import DbResolver from "@/remote/activitypub/db-resolver.js";
import { getApId } from "@/remote/activitypub/type.js";
import httpSignature from "@peertube/http-signature";
import httpSignature, { IParsedSignature } from "@peertube/http-signature";
export async function hasSignature(req: IncomingMessage): Promise<string> {
const meta = await fetchMeta();
@ -28,10 +31,14 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
export async function checkFetch(req: IncomingMessage): Promise<number> {
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
if (req.headers.host !== config.host) return 400;
let signature;
try {
signature = httpSignature.parseRequest(req, { headers: [] });
signature = httpSignature.parseRequest(req, {
headers: ["(request-target)", "host", "date"],
});
} catch (e) {
return 401;
}
@ -108,6 +115,8 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
if (!httpSignatureValidated) {
return 403;
}
return verifySignature(signature, authUser.key) ? 200 : 401;
}
return 200;
}
@ -130,3 +139,39 @@ export async function getSignatureUser(req: IncomingMessage): Promise<{
keyId.hash = "";
return await dbResolver.getAuthUserFromApId(getApId(keyId.toString()));
}
export function verifySignature(
sig: IParsedSignature,
key: UserPublickey,
): boolean {
if (!["hs2019", "rsa-sha256"].includes(sig.algorithm.toLowerCase()))
return false;
try {
return verify(
"rsa-sha256",
Buffer.from(sig.signingString, "utf8"),
key.keyPem,
Buffer.from(sig.params.signature, "base64"),
);
} catch {
// Algo not supported
return false;
}
}
export function verifyDigest(
body: string,
digest: string | string[] | undefined,
): boolean {
digest = toSingle(digest);
if (
body == null ||
digest == null ||
!digest.toLowerCase().startsWith("sha-256=")
)
return false;
return (
createHash("sha256").update(body).digest("base64") === digest.substring(8)
);
}

View file

@ -1,4 +1,5 @@
import config from "@/config/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import type { NoteReaction } from "@/models/entities/note-reaction.js";
import type { Note } from "@/models/entities/note.js";
import { Emojis } from "@/models/index.js";

View file

@ -1,7 +1,8 @@
import Router from "@koa/router";
import httpSignature from "@peertube/http-signature";
import json from "koa-json-body";
import bodyParser from "koa-bodyparser";
import config from "@/config/index.js";
import { isSelfHost } from "@/misc/convert-host.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { getUserKeypair } from "@/misc/keypair-store.js";
@ -17,6 +18,7 @@ import { inbox as processInbox } from "@/queue/index.js";
import {
checkFetch,
getSignatureUser,
verifyDigest,
} from "@/remote/activitypub/check-fetch.js";
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
import renderFollow from "@/remote/activitypub/renderer/follow.js";
@ -26,6 +28,7 @@ import { renderLike } from "@/remote/activitypub/renderer/like.js";
import renderNote from "@/remote/activitypub/renderer/note.js";
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
import { getInstanceActor } from "@/services/instance-actor.js";
import Koa from "koa";
import { In, IsNull, Not } from "typeorm";
import Featured from "./activitypub/featured.js";
import Followers from "./activitypub/followers.js";
@ -39,15 +42,27 @@ const router = new Router();
//#region Routing
function inbox(ctx: Router.RouterContext) {
if (ctx.req.headers.host !== config.host) {
ctx.status = 400;
return;
}
let signature;
try {
signature = httpSignature.parseRequest(ctx.req, { headers: [] });
signature = httpSignature.parseRequest(ctx.req, {
headers: ["(request-target)", "digest", "host", "date"],
});
} catch (e) {
ctx.status = 401;
return;
}
if (!verifyDigest(ctx.request.rawBody, ctx.headers.digest)) {
ctx.status = 401;
return;
}
processInbox(ctx.request.body, signature);
ctx.status = 202;
@ -72,9 +87,23 @@ export function setResponseType(ctx: Router.RouterContext) {
}
}
async function parseJsonBodyOrFail(ctx: Router.RouterContext, next: Koa.Next) {
const koaBodyParser = bodyParser({
enableTypes: ["json"],
detectJSON: () => true,
});
try {
await koaBodyParser(ctx, next);
} catch {
ctx.status = 400;
return;
}
}
// inbox
router.post("/inbox", json(), inbox);
router.post("/users/:user/inbox", json(), inbox);
router.post("/inbox", parseJsonBodyOrFail, inbox);
router.post("/users/:user/inbox", parseJsonBodyOrFail, inbox);
// note
router.get("/notes/:note", async (ctx, next) => {

View file

@ -1,6 +1,6 @@
import { createImportCustomEmojisJob } from "@/queue/index.js";
import define from "@/server/api/define.js";
import { ApiError } from "../../../error.js";
import { ApiError } from "@/server/api/error.js";
export const meta = {
tags: ["admin", "emoji"],

View file

@ -3,7 +3,7 @@ import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { Emojis } from "@/models/index.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
import define from "@/server/api/define.js";
import { ApiError } from "../../../error.js";
import { ApiError } from "@/server/api/error.js";
export const meta = {
tags: ["admin", "emoji"],

View file

@ -1,4 +1,4 @@
import define from "../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts"],

View file

@ -1,4 +1,4 @@
import define from "../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "drive"],

View file

@ -1,4 +1,4 @@
import define from "../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts"],

View file

@ -1,4 +1,4 @@
import define from "../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "hashtags"],

View file

@ -1,4 +1,4 @@
import define from "../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts"],

View file

@ -1,4 +1,4 @@
import define from "../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "notes"],

View file

@ -1,4 +1,4 @@
import define from "../../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "drive", "users"],

View file

@ -1,4 +1,4 @@
import define from "../../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "users", "notes"],

View file

@ -1,4 +1,4 @@
import define from "../../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "users", "reactions"],

View file

@ -1,4 +1,4 @@
import define from "../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["charts", "users"],

View file

@ -1,15 +1,10 @@
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { Note } from "@/models/entities/note.js";
// import { FindManyOptions, In } from "typeorm";
import { Notes } from "@/models/index.js";
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
// import config from "@/config/index.js";
// import es from "@/db/elasticsearch.js";
// import sonic from "@/db/sonic.js";
// import meilisearch, { MeilisearchNote } from "@/db/meilisearch.js";
import define from "@/server/api/define.js";
export const meta = {
@ -255,7 +250,7 @@ export default define(meta, paramDef, async (ps, me) => {
while (found.length < ps.limit && start < noteIDs.length) {
const chunk = noteIDs.slice(start, start + chunkSize);
let query: FindManyOptions = {
const query: FindManyOptions = {
where: {
id: In(chunk),
},

View file

@ -25,8 +25,7 @@ const assets = `${_dirname}/../../server/file/assets/`;
const MAX_BYTE_RANGES = 10;
const commonReadableHandlerGenerator =
(ctx: Koa.Context) =>
(e: Error): void => {
(ctx: Koa.Context) => (e: Error): void => {
serverLogger.error(e);
ctx.status = 500;
ctx.set("Cache-Control", "max-age=300");

View file

@ -25,7 +25,12 @@
//#region Detect language & fetch translations
const v = localStorage.getItem("v") || VERSION;
const supportedLangs = LANGS;
let supportedLangs = [];
try {
supportedLangs = SUPPORTED_LANGS;
} catch {
console.warn("LANGS not found");
}
let lang = localStorage.getItem("lang");
if (lang == null || !supportedLangs.includes(lang)) {
if (supportedLangs.includes(navigator.language)) {

View file

@ -31,7 +31,6 @@ import type { ILocalUser, IRemoteUser, User } from "@/models/entities/user.js";
import {
ChannelFollowings,
Channels,
Followings,
Instances,
MutedNotes,
Mutings,

View file

@ -18,7 +18,7 @@
"@rollup/plugin-json": "6.0.1",
"@rollup/pluginutils": "5.0.5",
"@syuilo/aiscript": "0.16.0",
"@types/autosize": "4.0.3",
"@types/autosize": "^4.0.3",
"@types/glob": "8.1.0",
"@types/gulp": "4.0.17",
"@types/gulp-rename": "2.0.5",
@ -49,7 +49,7 @@
"compare-versions": "6.1.0",
"cropperjs": "2.0.0-beta.4",
"date-fns": "2.30.0",
"emojilib": "3.0.11",
"emojilib": "^3.0.11",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-file-progress": "1.3.0",
"eventemitter3": "5.0.1",
@ -80,15 +80,15 @@
"three": "0.158.0",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tinyld": "1.3.4",
"tinyld": "^1.3.4",
"typescript": "5.2.2",
"unicode-emoji-json": "0.4.0",
"uuid": "9.0.1",
"vite": "5.0.0",
"vite-plugin-compression": "0.5.1",
"vue": "3.3.8",
"vue-draggable-plus": "0.2.7",
"vue-plyr": "7.0.0",
"vue-draggable-plus": "^0.2.7",
"vue-plyr": "^7.0.0",
"vue-prism-editor": "2.0.0-alpha.2"
}
}

View file

@ -22,8 +22,8 @@
message.groupId
? message.user
: isMe(message)
? message.recipient
: message.user
? message.recipient
: message.user
"
:show-indicator="true"
disable-link

View file

@ -327,8 +327,8 @@ async function ok() {
const result = props.input
? inputValue.value
: props.select
? selectedValue.value
: true;
? selectedValue.value
: true;
done(false, result);
}

View file

@ -17,8 +17,8 @@
? i18n.ts.selectFiles
: i18n.ts.selectFolders
: type === "file"
? i18n.ts.selectFile
: i18n.ts.selectFolder
? i18n.ts.selectFile
: i18n.ts.selectFolder
}}
<span
v-if="selected.length > 0"

View file

@ -88,8 +88,8 @@ const preferedModalType =
deviceKind === "desktop" && props.src != null
? "popup"
: deviceKind === "smartphone"
? "drawer"
: "dialog";
? "drawer"
: "dialog";
const modal = ref<InstanceType<typeof MkModal>>();

View file

@ -113,9 +113,9 @@ const url =
props.raw || defaultStore.state.loadRawImages
? props.media.url
: defaultStore.state.disableShowingAnimatedImages &&
props.media.type.startsWith("image")
? getStaticImageUrl(props.media.thumbnailUrl)
: props.media.thumbnailUrl;
props.media.type.startsWith("image")
? getStaticImageUrl(props.media.thumbnailUrl)
: props.media.thumbnailUrl;
const mediaType = computed(() => {
return props.media.type === "video/quicktime"

View file

@ -169,22 +169,22 @@ const transitionName = computed(() =>
? useSendAnime.value
? "send"
: type.value === "drawer"
? "modal-drawer"
: type.value === "popup"
? "modal-popup"
: "modal"
? "modal-drawer"
: type.value === "popup"
? "modal-popup"
: "modal"
: "",
);
const transitionDuration = computed(() =>
transitionName.value === "send"
? 400
: transitionName.value === "modal-popup"
? 100
: transitionName.value === "modal"
? 200
: transitionName.value === "modal-drawer"
? 200
: 0,
? 100
: transitionName.value === "modal"
? 200
: transitionName.value === "modal-drawer"
? 200
: 0,
);
let contentClicking = false;

View file

@ -17,8 +17,8 @@
? `${props.height}px`
: null
: height
? `min(${props.height}px, 100%)`
: '100%',
? `min(${props.height}px, 100%)`
: '100%',
}"
tabindex="-1"
>

View file

@ -259,12 +259,12 @@ const fetchMore = async (): Promise<void> => {
offset: offset.value,
}
: props.pagination.reversed
? {
sinceId: items.value[0].id,
}
: {
untilId: items.value[items.value.length - 1].id,
}),
? {
sinceId: items.value[0].id,
}
: {
untilId: items.value[items.value.length - 1].id,
}),
})
.then(
(res) => {
@ -320,12 +320,12 @@ const fetchMoreAhead = async (): Promise<void> => {
offset: offset.value,
}
: props.pagination.reversed
? {
untilId: items.value[0].id,
}
: {
sinceId: items.value[items.value.length - 1].id,
}),
? {
untilId: items.value[0].id,
}
: {
sinceId: items.value[items.value.length - 1].id,
}),
})
.then(
(res) => {

View file

@ -81,10 +81,10 @@ const timer = computed(() =>
remaining.value >= 86400
? "_poll.remainingDays"
: remaining.value >= 3600
? "_poll.remainingHours"
: remaining.value >= 60
? "_poll.remainingMinutes"
: "_poll.remainingSeconds",
? "_poll.remainingHours"
: remaining.value >= 60
? "_poll.remainingMinutes"
: "_poll.remainingSeconds",
{
s: Math.floor(remaining.value % 60),
m: Math.floor(remaining.value / 60) % 60,

View file

@ -171,8 +171,8 @@ function get() {
...(expiration.value === "at"
? { expiresAt: calcAt() }
: expiration.value === "after"
? { expiredAfter: calcAfter() }
: {}),
? { expiredAfter: calcAfter() }
: {}),
};
}

View file

@ -76,8 +76,8 @@
reply
? 'ph-arrow-u-up-left'
: renote
? 'ph-quotes'
: 'ph-paper-plane-tilt',
? 'ph-quotes'
: 'ph-paper-plane-tilt',
)
"
></i>
@ -360,7 +360,6 @@ const visibleUsers = ref([]);
if (props.initialVisibleUsers) {
props.initialVisibleUsers.forEach(pushVisibleUser);
}
const autocomplete = ref(null);
const draghover = ref(false);
const quoteId = ref(null);
const hasNotSpecifiedMentions = ref(false);
@ -417,10 +416,10 @@ const submitText = computed((): string => {
return props.editId
? i18n.ts.edit
: props.renote
? i18n.ts.quote
: props.reply
? i18n.ts.reply
: i18n.ts.note;
? i18n.ts.quote
: props.reply
? i18n.ts.reply
: i18n.ts.note;
});
const textLength = computed((): number => {
@ -489,14 +488,14 @@ if (props.reply && props.reply.text != null) {
const mention = x.host
? `@${x.username}@${toASCII(x.host)}`
: otherHost == null || otherHost === host
? `@${x.username}`
: `@${x.username}@${toASCII(otherHost)}`;
? `@${x.username}`
: `@${x.username}@${toASCII(otherHost)}`;
//
// exclude me
if ($i.username === x.username && (x.host == null || x.host === host))
continue;
//
// remove duplicates
if (text.value.includes(`${mention} `)) continue;
text.value += `${mention} `;
@ -505,10 +504,10 @@ if (props.reply && props.reply.text != null) {
if (props.channel) {
visibility.value = "public";
localOnly.value = true; // TODO:
localOnly.value = true; // TODO: Delete this once channels get federated
}
//
// Inherit the original visibility
if (
props.reply &&
["home", "followers", "specified"].includes(props.reply.visibility)
@ -629,10 +628,6 @@ function togglePoll() {
}
}
function addTag(tag: string) {
insertTextAtCursor(textareaEl.value, ` #${tag} `);
}
function focus() {
if (textareaEl.value) {
textareaEl.value.focus();
@ -736,6 +731,8 @@ function clear() {
quoteId.value = null;
}
// FIXME: ev.which is deprecated
// https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/which
function onKeydown(ev: KeyboardEvent) {
if (
(ev.which === 10 || ev.which === 13) &&
@ -772,7 +769,7 @@ async function onPaste(ev: ClipboardEvent) {
}
}
const paste = ev.clipboardData.getData("text");
const paste = ev.clipboardData?.getData("text") ?? "";
if (!props.renote && !quoteId.value && paste.startsWith(url + "/notes/")) {
ev.preventDefault();
@ -787,7 +784,7 @@ async function onPaste(ev: ClipboardEvent) {
}
quoteId.value = paste
.substr(url.length)
.substring(url.length)
.match(/^\/notes\/(.+?)\/?$/)[1];
});
}
@ -886,8 +883,8 @@ async function post() {
renoteId: props.renote
? props.renote.id
: quoteId.value
? quoteId.value
: undefined,
? quoteId.value
: undefined,
channelId: props.channel ? props.channel.id : undefined,
poll: poll.value,
cw: useCw.value ? cw.value || "" : undefined,

View file

@ -371,10 +371,10 @@ function onChangeUsername(): void {
const err = !username.value.match(/^[a-zA-Z0-9_]+$/)
? "invalid-format"
: username.value.length < 1
? "min-range"
: username.value.length > 20
? "max-range"
: null;
? "min-range"
: username.value.length > 20
? "max-range"
: null;
if (err) {
usernameState.value = err;
@ -410,16 +410,16 @@ function onChangeEmail(): void {
emailState.value = result.available
? "ok"
: result.reason === "used"
? "unavailable:used"
: result.reason === "format"
? "unavailable:format"
: result.reason === "disposable"
? "unavailable:disposable"
: result.reason === "mx"
? "unavailable:mx"
: result.reason === "smtp"
? "unavailable:smtp"
: "unavailable";
? "unavailable:used"
: result.reason === "format"
? "unavailable:format"
: result.reason === "disposable"
? "unavailable:disposable"
: result.reason === "mx"
? "unavailable:mx"
: result.reason === "smtp"
? "unavailable:smtp"
: "unavailable";
})
.catch(() => {
emailState.value = "error";

View file

@ -29,11 +29,11 @@ const _time =
props.time == null
? NaN
: typeof props.time === "number"
? props.time
: (props.time instanceof Date
? props.time
: new Date(props.time)
).getTime();
? props.time
: (props.time instanceof Date
? props.time
: new Date(props.time)
).getTime();
const invalid = Number.isNaN(_time);
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
@ -46,30 +46,30 @@ const relative = computed<string>(() => {
return ago >= 31536000
? i18n.t("_ago.yearsAgo", { n: Math.floor(ago / 31536000).toString() })
: ago >= 2592000
? i18n.t("_ago.monthsAgo", {
n: Math.floor(ago / 2592000).toString(),
})
: ago >= 604800
? i18n.t("_ago.weeksAgo", {
n: Math.floor(ago / 604800).toString(),
})
: ago >= 86400
? i18n.t("_ago.daysAgo", {
n: Math.floor(ago / 86400).toString(),
})
: ago >= 3600
? i18n.t("_ago.hoursAgo", {
n: Math.floor(ago / 3600).toString(),
})
: ago >= 60
? i18n.t("_ago.minutesAgo", { n: (~~(ago / 60)).toString() })
: ago >= 10
? i18n.t("_ago.secondsAgo", {
n: (~~(ago % 60)).toString(),
})
: ago >= -1
? i18n.ts._ago.justNow
: i18n.ts._ago.future;
? i18n.t("_ago.monthsAgo", {
n: Math.floor(ago / 2592000).toString(),
})
: ago >= 604800
? i18n.t("_ago.weeksAgo", {
n: Math.floor(ago / 604800).toString(),
})
: ago >= 86400
? i18n.t("_ago.daysAgo", {
n: Math.floor(ago / 86400).toString(),
})
: ago >= 3600
? i18n.t("_ago.hoursAgo", {
n: Math.floor(ago / 3600).toString(),
})
: ago >= 60
? i18n.t("_ago.minutesAgo", { n: (~~(ago / 60)).toString() })
: ago >= 10
? i18n.t("_ago.secondsAgo", {
n: (~~(ago % 60)).toString(),
})
: ago >= -1
? i18n.ts._ago.justNow
: i18n.ts._ago.future;
});
let tickId: number;

View file

@ -127,18 +127,18 @@ const pagination = {
...(state.value === "federating"
? { federating: true }
: state.value === "subscribing"
? { subscribing: true }
: state.value === "publishing"
? { publishing: true }
: state.value === "suspended"
? { suspended: true }
: state.value === "blocked"
? { blocked: true }
: state.value === "silenced"
? { silenced: true }
: state.value === "notResponding"
? { notResponding: true }
: {}),
? { subscribing: true }
: state.value === "publishing"
? { publishing: true }
: state.value === "suspended"
? { suspended: true }
: state.value === "blocked"
? { blocked: true }
: state.value === "silenced"
? { silenced: true }
: state.value === "notResponding"
? { notResponding: true }
: {}),
})),
};

View file

@ -105,8 +105,8 @@ async function init() {
provider.value = meta.enableHcaptcha
? "hcaptcha"
: meta.enableRecaptcha
? "recaptcha"
: null;
? "recaptcha"
: null;
}
function save() {

View file

@ -51,23 +51,23 @@ const label =
props.type === "process"
? "Process"
: props.type === "active"
? "Active"
: props.type === "delayed"
? "Delayed"
: props.type === "waiting"
? "Waiting"
: ("?" as never);
? "Active"
: props.type === "delayed"
? "Delayed"
: props.type === "waiting"
? "Waiting"
: ("?" as never);
const color =
props.type === "process"
? "#c4a7e7"
: props.type === "active"
? "#31748f"
: props.type === "delayed"
? "#eb6f92"
: props.type === "waiting"
? "#f6c177"
: ("?" as never);
? "#31748f"
: props.type === "delayed"
? "#eb6f92"
: props.type === "waiting"
? "#f6c177"
: ("?" as never);
onMounted(() => {
const vLineColor = defaultStore.state.darkMode

View file

@ -96,23 +96,23 @@ const label =
props.type === "process"
? "Process"
: props.type === "active"
? "Active"
: props.type === "delayed"
? "Delayed"
: props.type === "waiting"
? "Waiting"
: ("?" as never);
? "Active"
: props.type === "delayed"
? "Delayed"
: props.type === "waiting"
? "Waiting"
: ("?" as never);
const color =
props.type === "process"
? "#9ccfd8"
: props.type === "active"
? "#31748f"
: props.type === "delayed"
? "#eb6f92"
: props.type === "waiting"
? "#f6c177"
: ("?" as never);
? "#31748f"
: props.type === "delayed"
? "#eb6f92"
: props.type === "waiting"
? "#f6c177"
: ("?" as never);
onMounted(() => {
chartInstance = new Chart(chartEl.value, {

View file

@ -117,8 +117,8 @@ onMounted(() => {
props.domain === "inbox"
? "admin/queue/inbox-delayed"
: props.domain === "deliver"
? "admin/queue/deliver-delayed"
: null,
? "admin/queue/deliver-delayed"
: null,
{},
).then((result) => {
jobs.value = result;

View file

@ -290,14 +290,14 @@ async function init() {
meta.sensitiveMediaDetectionSensitivity === "veryLow"
? 0
: meta.sensitiveMediaDetectionSensitivity === "low"
? 1
: meta.sensitiveMediaDetectionSensitivity === "medium"
? 2
: meta.sensitiveMediaDetectionSensitivity === "high"
? 3
: meta.sensitiveMediaDetectionSensitivity === "veryHigh"
? 4
: 0;
? 1
: meta.sensitiveMediaDetectionSensitivity === "medium"
? 2
: meta.sensitiveMediaDetectionSensitivity === "high"
? 3
: meta.sensitiveMediaDetectionSensitivity === "veryHigh"
? 4
: 0;
setSensitiveFlagAutomatically.value = meta.setSensitiveFlagAutomatically;
enableSensitiveMediaDetectionForVideos.value =
meta.enableSensitiveMediaDetectionForVideos;
@ -317,14 +317,14 @@ function save() {
sensitiveMediaDetectionSensitivity.value === 0
? "veryLow"
: sensitiveMediaDetectionSensitivity.value === 1
? "low"
: sensitiveMediaDetectionSensitivity.value === 2
? "medium"
: sensitiveMediaDetectionSensitivity.value === 3
? "high"
: sensitiveMediaDetectionSensitivity.value === 4
? "veryHigh"
: 0,
? "low"
: sensitiveMediaDetectionSensitivity.value === 2
? "medium"
: sensitiveMediaDetectionSensitivity.value === 3
? "high"
: sensitiveMediaDetectionSensitivity.value === 4
? "veryHigh"
: 0,
setSensitiveFlagAutomatically: setSensitiveFlagAutomatically.value,
enableSensitiveMediaDetectionForVideos:
enableSensitiveMediaDetectionForVideos.value,

View file

@ -98,14 +98,14 @@ function onEndpointChange() {
p.type === "String"
? ""
: p.type === "Number"
? 0
: p.type === "Boolean"
? false
: p.type === "Array"
? []
: p.type === "Object"
? {}
: null;
? 0
: p.type === "Boolean"
? false
: p.type === "Array"
? []
: p.type === "Object"
? {}
: null;
}
body.value = JSON5.stringify(endpointBody, null, 2);
});

View file

@ -229,14 +229,7 @@ function edit() {
watch(() => props.postId, fetchPost, { immediate: true });
const headerActions = computed(() => [
{
icon: `${icon("ph-pencil")}`,
text: i18n.ts.edit,
handler: edit,
},
]);
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(

View file

@ -108,11 +108,11 @@
>{{ i18n.ts._theme.manage
}}<template #suffix>{{ themesCount }}</template></FormLink
>
<FormLink to="https://assets.firefish.io/theme/list" external
<!-- <FormLink to="https://assets.misskey.io/theme/list" external
><template #icon
><i :class="icon('ph-planet')"></i></template
>{{ i18n.ts._theme.explore }}</FormLink
>
> -->
<FormLink to="/settings/theme/install"
><template #icon
><i :class="icon('ph-download-simple')"></i></template

View file

@ -90,8 +90,8 @@
color: color.forPreview
? color.forPreview
: theme.base === 'light'
? '#5f5f5f'
: '#dadada',
? '#5f5f5f'
: '#dadada',
}"
>
A

View file

@ -270,12 +270,12 @@ definePageMetadata(
src.value === "local"
? "ph-users ph-lg"
: src.value === "social"
? "ph-handshake ph-lg"
: src.value === "recommended"
? "ph-thumbs-up ph-lg"
: src.value === "global"
? "ph-planet ph-lg"
: "ph-house ph-lg",
? "ph-handshake ph-lg"
: src.value === "recommended"
? "ph-thumbs-up ph-lg"
: src.value === "global"
? "ph-planet ph-lg"
: "ph-house ph-lg",
})),
);

View file

@ -1,11 +1,11 @@
import XCheatSheet from "@/components/MkCheatSheetDialog.vue";
import XTutorial from "@/components/MkTutorialDialog.vue";
import { defaultStore } from "@/store";
import { host } from "@/config";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import * as os from "@/os";
import icon from "@/scripts/icon";
import { defaultStore } from "@/store";
import type { MenuItem } from "@/types/menu";
const instanceSpecificItems: MenuItem[] = [];

View file

@ -35,8 +35,8 @@
widgetProps.src === "list"
? widgetProps.list.name
: widgetProps.src === "antenna"
? widgetProps.antenna.name
: i18n.t("_timelines." + widgetProps.src)
? widgetProps.antenna.name
: i18n.t("_timelines." + widgetProps.src)
}}</span>
<i
:class="
@ -57,8 +57,8 @@
widgetProps.src === 'list'
? `list:${widgetProps.list.id}`
: widgetProps.src === 'antenna'
? `antenna:${widgetProps.antenna.id}`
: widgetProps.src
? `antenna:${widgetProps.antenna.id}`
: widgetProps.src
"
:src="widgetProps.src"
:list="widgetProps.list ? widgetProps.list.id : null"

View file

@ -1,17 +1,17 @@
import Response from "./response";
import OAuth from "./oauth";
import { isCancel, RequestCanceledError } from "./cancel";
import { ProxyConfig } from "./proxy_config";
import { RequestCanceledError, isCancel } from "./cancel";
import Converter from "./converter";
import Entity from "./entity";
import FilterContext from "./filter_context";
import generator, {
detector,
MegalodonInterface,
WebSocketInterface,
detector,
} from "./megalodon";
import Misskey from "./misskey";
import Entity from "./entity";
import NotificationType from "./notification";
import FilterContext from "./filter_context";
import Converter from "./converter";
import OAuth from "./oauth";
import { ProxyConfig } from "./proxy_config";
import Response from "./response";
export {
Response,

View file

@ -1,10 +1,10 @@
import Response from "./response";
import axios, { AxiosRequestConfig } from "axios";
import { DEFAULT_UA } from "./default";
import Entity from "./entity";
import Misskey from "./misskey";
import OAuth from "./oauth";
import proxyAgent, { ProxyConfig } from "./proxy_config";
import Entity from "./entity";
import axios, { AxiosRequestConfig } from "axios";
import Misskey from "./misskey";
import { DEFAULT_UA } from "./default";
import Response from "./response";
export interface WebSocketInterface {
start(): void;

View file

@ -1,22 +1,22 @@
import FormData from "form-data";
import AsyncLock from "async-lock";
import FormData from "form-data";
import MisskeyAPI from "./misskey/api_client";
import fs from "node:fs";
import MegalodonEntity from "@/entity";
import { DEFAULT_UA } from "./default";
import { ProxyConfig } from "./proxy_config";
import OAuth from "./oauth";
import Response from "./response";
import Entity from "./entity";
import {
MegalodonInterface,
WebSocketInterface,
NoImplementedError,
ArgumentError,
MegalodonInterface,
NoImplementedError,
UnexpectedError,
WebSocketInterface,
} from "./megalodon";
import MegalodonEntity from "@/entity";
import fs from "node:fs";
import MisskeyAPI from "./misskey/api_client";
import MisskeyNotificationType from "./misskey/notification";
import OAuth from "./oauth";
import { ProxyConfig } from "./proxy_config";
import Response from "./response";
type AccountCache = {
locks: AsyncLock;