forked from naskya/firefish
Firefish v1.0.5-dev22
This commit is contained in:
parent
74f7ef6b98
commit
9fcd3e496f
71 changed files with 359 additions and 694 deletions
195
.config/ci.yml
195
.config/ci.yml
|
@ -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.
|
|
|
@ -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
|
|
|
@ -1,4 +0,0 @@
|
||||||
# db settings
|
|
||||||
POSTGRES_PASSWORD=test
|
|
||||||
POSTGRES_USER=postgres
|
|
||||||
POSTGRES_DB=postgres
|
|
|
@ -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
|
|
|
@ -52,7 +52,7 @@ gulp.task("build:backend:script", () => {
|
||||||
"./packages/backend/src/server/web/bios.js",
|
"./packages/backend/src/server/web/bios.js",
|
||||||
"./packages/backend/src/server/web/cli.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(
|
.pipe(
|
||||||
terser({
|
terser({
|
||||||
toplevel: true,
|
toplevel: true,
|
||||||
|
|
|
@ -112,7 +112,7 @@ you: "Tu"
|
||||||
clickToShow: "Fes clic per a mostrar"
|
clickToShow: "Fes clic per a mostrar"
|
||||||
sensitive: "NSFW"
|
sensitive: "NSFW"
|
||||||
add: "Afegeix"
|
add: "Afegeix"
|
||||||
reaction: "Reaccions"
|
reaction: "Reacció"
|
||||||
reactionSetting: "Reaccions a mostrar al selector de reaccions"
|
reactionSetting: "Reaccions a mostrar al selector de reaccions"
|
||||||
reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem
|
reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem
|
||||||
\"+\" per afegir."
|
\"+\" per afegir."
|
||||||
|
@ -792,11 +792,11 @@ customEmojis: Emojis personalitzats
|
||||||
cacheRemoteFilesDescription: Quan aquesta opció està desactivada, els fitxers remots
|
cacheRemoteFilesDescription: Quan aquesta opció està desactivada, els fitxers remots
|
||||||
es carreguen directament del servidor remot. Desactivar-la farà que baixi l'ús d'emmagatzematge,
|
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.
|
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.
|
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
|
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
|
prevenir cadenes de interaccions infinites amb altres comptes automatitzats a més
|
||||||
interns de Firefish per tractar aquest compte com un bot.
|
d'ajustar els sistemes interns de Firefish per tractar aquest compte com automatitzat.
|
||||||
flagAsCat: Ets un gat? 🐱
|
flagAsCat: Ets un gat? 🐱
|
||||||
flagShowTimelineReplies: Mostra respostes a la línia de temps
|
flagShowTimelineReplies: Mostra respostes a la línia de temps
|
||||||
flagAsCatDescription: Guanyaràs unes orelles de gat i parlares com un gat!
|
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
|
selectChannel: Selecciona un canal
|
||||||
isLocked: Aquest compte té les següents aprovacions
|
isLocked: Aquest compte té les següents aprovacions
|
||||||
isPatron: Mecenes de Firefish
|
isPatron: Mecenes de Firefish
|
||||||
isBot: Aquest compte és un bot
|
isBot: Aquest es un compte automatitzat
|
||||||
isModerator: Moderador
|
isModerator: Moderador
|
||||||
isAdmin: Administrador
|
isAdmin: Administrador
|
||||||
_filters:
|
_filters:
|
||||||
|
@ -2199,3 +2199,16 @@ openServerInfo: Mostra la informació del servidor fent clic al símbol del serv
|
||||||
en un missatge
|
en un missatge
|
||||||
vibrate: Activar vibracions
|
vibrate: Activar vibracions
|
||||||
clickToShowPatterns: Fes clic per veure patrons de mòduls
|
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
|
||||||
|
|
|
@ -2218,3 +2218,4 @@ openServerInfo: Anzeigen von Serverinformationen durch Anklicken des Server-Tick
|
||||||
in einem Beitrag
|
in einem Beitrag
|
||||||
vibrate: Vibrationen abspielen
|
vibrate: Vibrationen abspielen
|
||||||
clickToShowPatterns: Klicken um Modul-Muster anzuzeigen
|
clickToShowPatterns: Klicken um Modul-Muster anzuzeigen
|
||||||
|
replies: Antworten
|
||||||
|
|
|
@ -312,7 +312,7 @@ agreeTo: "I agree to {0}"
|
||||||
tos: "Terms of Service"
|
tos: "Terms of Service"
|
||||||
start: "Begin"
|
start: "Begin"
|
||||||
home: "Home"
|
home: "Home"
|
||||||
remoteUserCaution: "Information from remote users may be incomplete."
|
remoteUserCaution: "Information from remote users are incomplete."
|
||||||
activity: "Activity"
|
activity: "Activity"
|
||||||
images: "Images"
|
images: "Images"
|
||||||
birthday: "Birthday"
|
birthday: "Birthday"
|
||||||
|
@ -2188,5 +2188,5 @@ _iconSets:
|
||||||
regular: "Regular"
|
regular: "Regular"
|
||||||
fill: "Filled"
|
fill: "Filled"
|
||||||
duotone: "Duotone"
|
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/"
|
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/"
|
||||||
|
|
|
@ -278,8 +278,7 @@ agreeTo: "Saya setuju kepada {0}"
|
||||||
tos: "Syarat dan ketentuan"
|
tos: "Syarat dan ketentuan"
|
||||||
start: "Mulai"
|
start: "Mulai"
|
||||||
home: "Beranda"
|
home: "Beranda"
|
||||||
remoteUserCaution: "Informasi ini mungkin tidak mutakhir, karena pengguna ini berasal
|
remoteUserCaution: "Informasi dari pengguna luar tidak lengkap."
|
||||||
dari instansi luar."
|
|
||||||
activity: "Aktivitas"
|
activity: "Aktivitas"
|
||||||
images: "Gambar"
|
images: "Gambar"
|
||||||
birthday: "Tanggal lahir"
|
birthday: "Tanggal lahir"
|
||||||
|
@ -2192,3 +2191,5 @@ reactions: Reaksi
|
||||||
replies: Balasan
|
replies: Balasan
|
||||||
quotes: Kutipan
|
quotes: Kutipan
|
||||||
renotes: Postingan ulang
|
renotes: Postingan ulang
|
||||||
|
showAttachedNotes: Tampilkan postingan dengan berkas ini
|
||||||
|
attachedToNotes: Posting dengan berkas ini
|
||||||
|
|
|
@ -268,7 +268,7 @@ agreeTo: "Sono d'accordo con {0}"
|
||||||
tos: "Termini d'uso"
|
tos: "Termini d'uso"
|
||||||
start: "Inizia"
|
start: "Inizia"
|
||||||
home: "Home"
|
home: "Home"
|
||||||
remoteUserCaution: "Le informazioni degli utenti remoti possono essere incomplete."
|
remoteUserCaution: "Le informazioni degli utenti remoti sono incomplete."
|
||||||
activity: "Attività"
|
activity: "Attività"
|
||||||
images: "Immagini"
|
images: "Immagini"
|
||||||
birthday: "Compleanno"
|
birthday: "Compleanno"
|
||||||
|
@ -2180,3 +2180,5 @@ reactions: Reazioni
|
||||||
replies: Risposte
|
replies: Risposte
|
||||||
quotes: Citazioni
|
quotes: Citazioni
|
||||||
renotes: Boost
|
renotes: Boost
|
||||||
|
showAttachedNotes: Mostra i post con questo allegato
|
||||||
|
attachedToNotes: Post con questo allegato
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
465a585f47037ce26834587429f0bd341e35f76f
|
a6aa7d2b74020dfc9303995918e63727fb3cb2c0
|
||||||
|
|
|
@ -139,7 +139,7 @@ importers:
|
||||||
specifier: 6.0.1
|
specifier: 6.0.1
|
||||||
version: 6.0.1
|
version: 6.0.1
|
||||||
argon2:
|
argon2:
|
||||||
specifier: 0.31.2
|
specifier: ^0.31.2
|
||||||
version: 0.31.2
|
version: 0.31.2
|
||||||
aws-sdk:
|
aws-sdk:
|
||||||
specifier: 2.1498.0
|
specifier: 2.1498.0
|
||||||
|
@ -624,7 +624,7 @@ importers:
|
||||||
specifier: 0.16.0
|
specifier: 0.16.0
|
||||||
version: 0.16.0
|
version: 0.16.0
|
||||||
'@types/autosize':
|
'@types/autosize':
|
||||||
specifier: 4.0.3
|
specifier: ^4.0.3
|
||||||
version: 4.0.3
|
version: 4.0.3
|
||||||
'@types/glob':
|
'@types/glob':
|
||||||
specifier: 8.1.0
|
specifier: 8.1.0
|
||||||
|
@ -717,7 +717,7 @@ importers:
|
||||||
specifier: 2.30.0
|
specifier: 2.30.0
|
||||||
version: 2.30.0
|
version: 2.30.0
|
||||||
emojilib:
|
emojilib:
|
||||||
specifier: 3.0.11
|
specifier: ^3.0.11
|
||||||
version: 3.0.11
|
version: 3.0.11
|
||||||
eslint-config-prettier:
|
eslint-config-prettier:
|
||||||
specifier: 9.0.0
|
specifier: 9.0.0
|
||||||
|
@ -810,7 +810,7 @@ importers:
|
||||||
specifier: 1.6.0
|
specifier: 1.6.0
|
||||||
version: 1.6.0
|
version: 1.6.0
|
||||||
tinyld:
|
tinyld:
|
||||||
specifier: 1.3.4
|
specifier: ^1.3.4
|
||||||
version: 1.3.4
|
version: 1.3.4
|
||||||
typescript:
|
typescript:
|
||||||
specifier: 5.2.2
|
specifier: 5.2.2
|
||||||
|
@ -831,10 +831,10 @@ importers:
|
||||||
specifier: 3.3.8
|
specifier: 3.3.8
|
||||||
version: 3.3.8(typescript@5.2.2)
|
version: 3.3.8(typescript@5.2.2)
|
||||||
vue-draggable-plus:
|
vue-draggable-plus:
|
||||||
specifier: 0.2.7
|
specifier: ^0.2.7
|
||||||
version: 0.2.7(@types/sortablejs@1.15.5)
|
version: 0.2.7(@types/sortablejs@1.15.5)
|
||||||
vue-plyr:
|
vue-plyr:
|
||||||
specifier: 7.0.0
|
specifier: ^7.0.0
|
||||||
version: 7.0.0
|
version: 7.0.0
|
||||||
vue-prism-editor:
|
vue-prism-editor:
|
||||||
specifier: 2.0.0-alpha.2
|
specifier: 2.0.0-alpha.2
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "firefish",
|
"name": "firefish",
|
||||||
"version": "1.0.5-dev21",
|
"version": "1.0.5-dev22",
|
||||||
"codename": "aqua",
|
"codename": "aqua",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"adm-zip": "0.5.10",
|
"adm-zip": "0.5.10",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"archiver": "6.0.1",
|
"archiver": "6.0.1",
|
||||||
"argon2": "0.31.2",
|
"argon2": "^0.31.2",
|
||||||
"aws-sdk": "2.1498.0",
|
"aws-sdk": "2.1498.0",
|
||||||
"axios": "1.6.2",
|
"axios": "1.6.2",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import * as Acct from "@/misc/acct.js";
|
import * as Acct from "@/misc/acct.js";
|
||||||
import { Cache } from "@/misc/cache.js";
|
import { Cache } from "@/misc/cache.js";
|
||||||
import { getWordHardMute } from "@/misc/check-word-mute.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 { Packed } from "@/misc/schema.js";
|
||||||
import type { Antenna } from "@/models/entities/antenna.js";
|
import type { Antenna } from "@/models/entities/antenna.js";
|
||||||
import type { Note } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import type { User } from "@/models/entities/user.js";
|
import type { User } from "@/models/entities/user.js";
|
||||||
import { Blockings, Followings, UserProfiles } from "@/models/index.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 blockingCache = new Cache<User["id"][]>("blocking", 60 * 5);
|
||||||
const mutedWordsCache = new Cache<string[][] | undefined>("mutedWords", 60 * 5);
|
const mutedWordsCache = new Cache<string[][] | undefined>("mutedWords", 60 * 5);
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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()}`;
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -6,6 +6,7 @@ import { shouldBlockInstance } from "@/misc/should-block-instance.js";
|
||||||
import type { UserPublickey } from "@/models/entities/user-publickey.js";
|
import type { UserPublickey } from "@/models/entities/user-publickey.js";
|
||||||
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
||||||
import { Instances } from "@/models/index.js";
|
import { Instances } from "@/models/index.js";
|
||||||
|
import { verifySignature } from "@/remote/activitypub/check-fetch.js";
|
||||||
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
||||||
import { LdSignature } from "@/remote/activitypub/misc/ld-signature.js";
|
import { LdSignature } from "@/remote/activitypub/misc/ld-signature.js";
|
||||||
import { resolvePerson } from "@/remote/activitypub/models/person.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と一致する必要がある
|
// また、signatureのsignerは、activity.actorと一致する必要がある
|
||||||
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
|
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
|
||||||
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
|
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type Bull from "bull";
|
import type Bull from "bull";
|
||||||
|
|
||||||
import { activeUsersChart } from "@/services/chart/index.js";
|
import { activeUsersChart } from "@/services/chart/index.js";
|
||||||
import { queueLogger } from "../../logger.js";
|
import { queueLogger } from "@/queue/logger.js";
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger("clean-charts");
|
const logger = queueLogger.createSubLogger("clean-charts");
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { checkExpiredMutings } from "./check-expired-mutings.js";
|
||||||
import { cleanCharts } from "./clean-charts.js";
|
import { cleanCharts } from "./clean-charts.js";
|
||||||
import { clean } from "./clean.js";
|
import { clean } from "./clean.js";
|
||||||
import { setLocalEmojiSizes } from "./local-emoji-size.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";
|
import { verifyLinks } from "./verify-links.js";
|
||||||
|
|
||||||
const jobs = {
|
const jobs = {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import type { IncomingMessage } from "http";
|
import type { IncomingMessage } from "http";
|
||||||
|
import { verify } from "node:crypto";
|
||||||
|
import { createHash } from "node:crypto";
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import { toPuny } from "@/misc/convert-host.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 { shouldBlockInstance } from "@/misc/should-block-instance.js";
|
||||||
import type { UserPublickey } from "@/models/entities/user-publickey.js";
|
import type { UserPublickey } from "@/models/entities/user-publickey.js";
|
||||||
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
import type { CacheableRemoteUser } from "@/models/entities/user.js";
|
||||||
|
import { toSingle } from "@/prelude/array.js";
|
||||||
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
import DbResolver from "@/remote/activitypub/db-resolver.js";
|
||||||
import { getApId } from "@/remote/activitypub/type.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> {
|
export async function hasSignature(req: IncomingMessage): Promise<string> {
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta();
|
||||||
|
@ -28,10 +31,14 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
|
||||||
export async function checkFetch(req: IncomingMessage): Promise<number> {
|
export async function checkFetch(req: IncomingMessage): Promise<number> {
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta();
|
||||||
if (meta.secureMode || meta.privateMode) {
|
if (meta.secureMode || meta.privateMode) {
|
||||||
|
if (req.headers.host !== config.host) return 400;
|
||||||
|
|
||||||
let signature;
|
let signature;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
signature = httpSignature.parseRequest(req, { headers: [] });
|
signature = httpSignature.parseRequest(req, {
|
||||||
|
headers: ["(request-target)", "host", "date"],
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return 401;
|
return 401;
|
||||||
}
|
}
|
||||||
|
@ -108,6 +115,8 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
|
||||||
if (!httpSignatureValidated) {
|
if (!httpSignatureValidated) {
|
||||||
return 403;
|
return 403;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return verifySignature(signature, authUser.key) ? 200 : 401;
|
||||||
}
|
}
|
||||||
return 200;
|
return 200;
|
||||||
}
|
}
|
||||||
|
@ -130,3 +139,39 @@ export async function getSignatureUser(req: IncomingMessage): Promise<{
|
||||||
keyId.hash = "";
|
keyId.hash = "";
|
||||||
return await dbResolver.getAuthUserFromApId(getApId(keyId.toString()));
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
||||||
import type { Note } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import httpSignature from "@peertube/http-signature";
|
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 { isSelfHost } from "@/misc/convert-host.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
import { getUserKeypair } from "@/misc/keypair-store.js";
|
import { getUserKeypair } from "@/misc/keypair-store.js";
|
||||||
|
@ -17,6 +18,7 @@ import { inbox as processInbox } from "@/queue/index.js";
|
||||||
import {
|
import {
|
||||||
checkFetch,
|
checkFetch,
|
||||||
getSignatureUser,
|
getSignatureUser,
|
||||||
|
verifyDigest,
|
||||||
} from "@/remote/activitypub/check-fetch.js";
|
} from "@/remote/activitypub/check-fetch.js";
|
||||||
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
|
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
|
||||||
import renderFollow from "@/remote/activitypub/renderer/follow.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 renderNote from "@/remote/activitypub/renderer/note.js";
|
||||||
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
|
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
|
||||||
import { getInstanceActor } from "@/services/instance-actor.js";
|
import { getInstanceActor } from "@/services/instance-actor.js";
|
||||||
|
import Koa from "koa";
|
||||||
import { In, IsNull, Not } from "typeorm";
|
import { In, IsNull, Not } from "typeorm";
|
||||||
import Featured from "./activitypub/featured.js";
|
import Featured from "./activitypub/featured.js";
|
||||||
import Followers from "./activitypub/followers.js";
|
import Followers from "./activitypub/followers.js";
|
||||||
|
@ -39,15 +42,27 @@ const router = new Router();
|
||||||
//#region Routing
|
//#region Routing
|
||||||
|
|
||||||
function inbox(ctx: Router.RouterContext) {
|
function inbox(ctx: Router.RouterContext) {
|
||||||
|
if (ctx.req.headers.host !== config.host) {
|
||||||
|
ctx.status = 400;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let signature;
|
let signature;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
signature = httpSignature.parseRequest(ctx.req, { headers: [] });
|
signature = httpSignature.parseRequest(ctx.req, {
|
||||||
|
headers: ["(request-target)", "digest", "host", "date"],
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!verifyDigest(ctx.request.rawBody, ctx.headers.digest)) {
|
||||||
|
ctx.status = 401;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
processInbox(ctx.request.body, signature);
|
processInbox(ctx.request.body, signature);
|
||||||
|
|
||||||
ctx.status = 202;
|
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
|
// inbox
|
||||||
router.post("/inbox", json(), inbox);
|
router.post("/inbox", parseJsonBodyOrFail, inbox);
|
||||||
router.post("/users/:user/inbox", json(), inbox);
|
router.post("/users/:user/inbox", parseJsonBodyOrFail, inbox);
|
||||||
|
|
||||||
// note
|
// note
|
||||||
router.get("/notes/:note", async (ctx, next) => {
|
router.get("/notes/:note", async (ctx, next) => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createImportCustomEmojisJob } from "@/queue/index.js";
|
import { createImportCustomEmojisJob } from "@/queue/index.js";
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
import { ApiError } from "../../../error.js";
|
import { ApiError } from "@/server/api/error.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["admin", "emoji"],
|
tags: ["admin", "emoji"],
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
import { ApiError } from "../../../error.js";
|
import { ApiError } from "@/server/api/error.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["admin", "emoji"],
|
tags: ["admin", "emoji"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts"],
|
tags: ["charts"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts", "drive"],
|
tags: ["charts", "drive"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts"],
|
tags: ["charts"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts", "hashtags"],
|
tags: ["charts", "hashtags"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts"],
|
tags: ["charts"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts", "notes"],
|
tags: ["charts", "notes"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts", "drive", "users"],
|
tags: ["charts", "drive", "users"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts", "users", "notes"],
|
tags: ["charts", "users", "notes"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts", "users", "reactions"],
|
tags: ["charts", "users", "reactions"],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import define from "../../define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["charts", "users"],
|
tags: ["charts", "users"],
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
|
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
// import { FindManyOptions, In } from "typeorm";
|
|
||||||
import { Notes } from "@/models/index.js";
|
import { Notes } from "@/models/index.js";
|
||||||
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
||||||
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
|
||||||
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
|
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
|
||||||
import { makePaginationQuery } from "@/server/api/common/make-pagination-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";
|
import define from "@/server/api/define.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -255,7 +250,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
while (found.length < ps.limit && start < noteIDs.length) {
|
while (found.length < ps.limit && start < noteIDs.length) {
|
||||||
const chunk = noteIDs.slice(start, start + chunkSize);
|
const chunk = noteIDs.slice(start, start + chunkSize);
|
||||||
|
|
||||||
let query: FindManyOptions = {
|
const query: FindManyOptions = {
|
||||||
where: {
|
where: {
|
||||||
id: In(chunk),
|
id: In(chunk),
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,8 +25,7 @@ const assets = `${_dirname}/../../server/file/assets/`;
|
||||||
const MAX_BYTE_RANGES = 10;
|
const MAX_BYTE_RANGES = 10;
|
||||||
|
|
||||||
const commonReadableHandlerGenerator =
|
const commonReadableHandlerGenerator =
|
||||||
(ctx: Koa.Context) =>
|
(ctx: Koa.Context) => (e: Error): void => {
|
||||||
(e: Error): void => {
|
|
||||||
serverLogger.error(e);
|
serverLogger.error(e);
|
||||||
ctx.status = 500;
|
ctx.status = 500;
|
||||||
ctx.set("Cache-Control", "max-age=300");
|
ctx.set("Cache-Control", "max-age=300");
|
||||||
|
|
|
@ -25,7 +25,12 @@
|
||||||
//#region Detect language & fetch translations
|
//#region Detect language & fetch translations
|
||||||
const v = localStorage.getItem("v") || VERSION;
|
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");
|
let lang = localStorage.getItem("lang");
|
||||||
if (lang == null || !supportedLangs.includes(lang)) {
|
if (lang == null || !supportedLangs.includes(lang)) {
|
||||||
if (supportedLangs.includes(navigator.language)) {
|
if (supportedLangs.includes(navigator.language)) {
|
||||||
|
|
|
@ -31,7 +31,6 @@ import type { ILocalUser, IRemoteUser, User } from "@/models/entities/user.js";
|
||||||
import {
|
import {
|
||||||
ChannelFollowings,
|
ChannelFollowings,
|
||||||
Channels,
|
Channels,
|
||||||
Followings,
|
|
||||||
Instances,
|
Instances,
|
||||||
MutedNotes,
|
MutedNotes,
|
||||||
Mutings,
|
Mutings,
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
"@rollup/plugin-json": "6.0.1",
|
"@rollup/plugin-json": "6.0.1",
|
||||||
"@rollup/pluginutils": "5.0.5",
|
"@rollup/pluginutils": "5.0.5",
|
||||||
"@syuilo/aiscript": "0.16.0",
|
"@syuilo/aiscript": "0.16.0",
|
||||||
"@types/autosize": "4.0.3",
|
"@types/autosize": "^4.0.3",
|
||||||
"@types/glob": "8.1.0",
|
"@types/glob": "8.1.0",
|
||||||
"@types/gulp": "4.0.17",
|
"@types/gulp": "4.0.17",
|
||||||
"@types/gulp-rename": "2.0.5",
|
"@types/gulp-rename": "2.0.5",
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
"compare-versions": "6.1.0",
|
"compare-versions": "6.1.0",
|
||||||
"cropperjs": "2.0.0-beta.4",
|
"cropperjs": "2.0.0-beta.4",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"emojilib": "3.0.11",
|
"emojilib": "^3.0.11",
|
||||||
"eslint-config-prettier": "9.0.0",
|
"eslint-config-prettier": "9.0.0",
|
||||||
"eslint-plugin-file-progress": "1.3.0",
|
"eslint-plugin-file-progress": "1.3.0",
|
||||||
"eventemitter3": "5.0.1",
|
"eventemitter3": "5.0.1",
|
||||||
|
@ -80,15 +80,15 @@
|
||||||
"three": "0.158.0",
|
"three": "0.158.0",
|
||||||
"throttle-debounce": "5.0.0",
|
"throttle-debounce": "5.0.0",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tinyld": "1.3.4",
|
"tinyld": "^1.3.4",
|
||||||
"typescript": "5.2.2",
|
"typescript": "5.2.2",
|
||||||
"unicode-emoji-json": "0.4.0",
|
"unicode-emoji-json": "0.4.0",
|
||||||
"uuid": "9.0.1",
|
"uuid": "9.0.1",
|
||||||
"vite": "5.0.0",
|
"vite": "5.0.0",
|
||||||
"vite-plugin-compression": "0.5.1",
|
"vite-plugin-compression": "0.5.1",
|
||||||
"vue": "3.3.8",
|
"vue": "3.3.8",
|
||||||
"vue-draggable-plus": "0.2.7",
|
"vue-draggable-plus": "^0.2.7",
|
||||||
"vue-plyr": "7.0.0",
|
"vue-plyr": "^7.0.0",
|
||||||
"vue-prism-editor": "2.0.0-alpha.2"
|
"vue-prism-editor": "2.0.0-alpha.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
message.groupId
|
message.groupId
|
||||||
? message.user
|
? message.user
|
||||||
: isMe(message)
|
: isMe(message)
|
||||||
? message.recipient
|
? message.recipient
|
||||||
: message.user
|
: message.user
|
||||||
"
|
"
|
||||||
:show-indicator="true"
|
:show-indicator="true"
|
||||||
disable-link
|
disable-link
|
||||||
|
|
|
@ -327,8 +327,8 @@ async function ok() {
|
||||||
const result = props.input
|
const result = props.input
|
||||||
? inputValue.value
|
? inputValue.value
|
||||||
: props.select
|
: props.select
|
||||||
? selectedValue.value
|
? selectedValue.value
|
||||||
: true;
|
: true;
|
||||||
done(false, result);
|
done(false, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
? i18n.ts.selectFiles
|
? i18n.ts.selectFiles
|
||||||
: i18n.ts.selectFolders
|
: i18n.ts.selectFolders
|
||||||
: type === "file"
|
: type === "file"
|
||||||
? i18n.ts.selectFile
|
? i18n.ts.selectFile
|
||||||
: i18n.ts.selectFolder
|
: i18n.ts.selectFolder
|
||||||
}}
|
}}
|
||||||
<span
|
<span
|
||||||
v-if="selected.length > 0"
|
v-if="selected.length > 0"
|
||||||
|
|
|
@ -88,8 +88,8 @@ const preferedModalType =
|
||||||
deviceKind === "desktop" && props.src != null
|
deviceKind === "desktop" && props.src != null
|
||||||
? "popup"
|
? "popup"
|
||||||
: deviceKind === "smartphone"
|
: deviceKind === "smartphone"
|
||||||
? "drawer"
|
? "drawer"
|
||||||
: "dialog";
|
: "dialog";
|
||||||
|
|
||||||
const modal = ref<InstanceType<typeof MkModal>>();
|
const modal = ref<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
|
|
|
@ -113,9 +113,9 @@ const url =
|
||||||
props.raw || defaultStore.state.loadRawImages
|
props.raw || defaultStore.state.loadRawImages
|
||||||
? props.media.url
|
? props.media.url
|
||||||
: defaultStore.state.disableShowingAnimatedImages &&
|
: defaultStore.state.disableShowingAnimatedImages &&
|
||||||
props.media.type.startsWith("image")
|
props.media.type.startsWith("image")
|
||||||
? getStaticImageUrl(props.media.thumbnailUrl)
|
? getStaticImageUrl(props.media.thumbnailUrl)
|
||||||
: props.media.thumbnailUrl;
|
: props.media.thumbnailUrl;
|
||||||
|
|
||||||
const mediaType = computed(() => {
|
const mediaType = computed(() => {
|
||||||
return props.media.type === "video/quicktime"
|
return props.media.type === "video/quicktime"
|
||||||
|
|
|
@ -169,22 +169,22 @@ const transitionName = computed(() =>
|
||||||
? useSendAnime.value
|
? useSendAnime.value
|
||||||
? "send"
|
? "send"
|
||||||
: type.value === "drawer"
|
: type.value === "drawer"
|
||||||
? "modal-drawer"
|
? "modal-drawer"
|
||||||
: type.value === "popup"
|
: type.value === "popup"
|
||||||
? "modal-popup"
|
? "modal-popup"
|
||||||
: "modal"
|
: "modal"
|
||||||
: "",
|
: "",
|
||||||
);
|
);
|
||||||
const transitionDuration = computed(() =>
|
const transitionDuration = computed(() =>
|
||||||
transitionName.value === "send"
|
transitionName.value === "send"
|
||||||
? 400
|
? 400
|
||||||
: transitionName.value === "modal-popup"
|
: transitionName.value === "modal-popup"
|
||||||
? 100
|
? 100
|
||||||
: transitionName.value === "modal"
|
: transitionName.value === "modal"
|
||||||
? 200
|
? 200
|
||||||
: transitionName.value === "modal-drawer"
|
: transitionName.value === "modal-drawer"
|
||||||
? 200
|
? 200
|
||||||
: 0,
|
: 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
let contentClicking = false;
|
let contentClicking = false;
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
? `${props.height}px`
|
? `${props.height}px`
|
||||||
: null
|
: null
|
||||||
: height
|
: height
|
||||||
? `min(${props.height}px, 100%)`
|
? `min(${props.height}px, 100%)`
|
||||||
: '100%',
|
: '100%',
|
||||||
}"
|
}"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
|
|
|
@ -259,12 +259,12 @@ const fetchMore = async (): Promise<void> => {
|
||||||
offset: offset.value,
|
offset: offset.value,
|
||||||
}
|
}
|
||||||
: props.pagination.reversed
|
: props.pagination.reversed
|
||||||
? {
|
? {
|
||||||
sinceId: items.value[0].id,
|
sinceId: items.value[0].id,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
untilId: items.value[items.value.length - 1].id,
|
untilId: items.value[items.value.length - 1].id,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(res) => {
|
(res) => {
|
||||||
|
@ -320,12 +320,12 @@ const fetchMoreAhead = async (): Promise<void> => {
|
||||||
offset: offset.value,
|
offset: offset.value,
|
||||||
}
|
}
|
||||||
: props.pagination.reversed
|
: props.pagination.reversed
|
||||||
? {
|
? {
|
||||||
untilId: items.value[0].id,
|
untilId: items.value[0].id,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
sinceId: items.value[items.value.length - 1].id,
|
sinceId: items.value[items.value.length - 1].id,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(res) => {
|
(res) => {
|
||||||
|
|
|
@ -81,10 +81,10 @@ const timer = computed(() =>
|
||||||
remaining.value >= 86400
|
remaining.value >= 86400
|
||||||
? "_poll.remainingDays"
|
? "_poll.remainingDays"
|
||||||
: remaining.value >= 3600
|
: remaining.value >= 3600
|
||||||
? "_poll.remainingHours"
|
? "_poll.remainingHours"
|
||||||
: remaining.value >= 60
|
: remaining.value >= 60
|
||||||
? "_poll.remainingMinutes"
|
? "_poll.remainingMinutes"
|
||||||
: "_poll.remainingSeconds",
|
: "_poll.remainingSeconds",
|
||||||
{
|
{
|
||||||
s: Math.floor(remaining.value % 60),
|
s: Math.floor(remaining.value % 60),
|
||||||
m: Math.floor(remaining.value / 60) % 60,
|
m: Math.floor(remaining.value / 60) % 60,
|
||||||
|
|
|
@ -171,8 +171,8 @@ function get() {
|
||||||
...(expiration.value === "at"
|
...(expiration.value === "at"
|
||||||
? { expiresAt: calcAt() }
|
? { expiresAt: calcAt() }
|
||||||
: expiration.value === "after"
|
: expiration.value === "after"
|
||||||
? { expiredAfter: calcAfter() }
|
? { expiredAfter: calcAfter() }
|
||||||
: {}),
|
: {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,8 +76,8 @@
|
||||||
reply
|
reply
|
||||||
? 'ph-arrow-u-up-left'
|
? 'ph-arrow-u-up-left'
|
||||||
: renote
|
: renote
|
||||||
? 'ph-quotes'
|
? 'ph-quotes'
|
||||||
: 'ph-paper-plane-tilt',
|
: 'ph-paper-plane-tilt',
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
></i>
|
></i>
|
||||||
|
@ -360,7 +360,6 @@ const visibleUsers = ref([]);
|
||||||
if (props.initialVisibleUsers) {
|
if (props.initialVisibleUsers) {
|
||||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||||
}
|
}
|
||||||
const autocomplete = ref(null);
|
|
||||||
const draghover = ref(false);
|
const draghover = ref(false);
|
||||||
const quoteId = ref(null);
|
const quoteId = ref(null);
|
||||||
const hasNotSpecifiedMentions = ref(false);
|
const hasNotSpecifiedMentions = ref(false);
|
||||||
|
@ -417,10 +416,10 @@ const submitText = computed((): string => {
|
||||||
return props.editId
|
return props.editId
|
||||||
? i18n.ts.edit
|
? i18n.ts.edit
|
||||||
: props.renote
|
: props.renote
|
||||||
? i18n.ts.quote
|
? i18n.ts.quote
|
||||||
: props.reply
|
: props.reply
|
||||||
? i18n.ts.reply
|
? i18n.ts.reply
|
||||||
: i18n.ts.note;
|
: i18n.ts.note;
|
||||||
});
|
});
|
||||||
|
|
||||||
const textLength = computed((): number => {
|
const textLength = computed((): number => {
|
||||||
|
@ -489,14 +488,14 @@ if (props.reply && props.reply.text != null) {
|
||||||
const mention = x.host
|
const mention = x.host
|
||||||
? `@${x.username}@${toASCII(x.host)}`
|
? `@${x.username}@${toASCII(x.host)}`
|
||||||
: otherHost == null || otherHost === host
|
: otherHost == null || otherHost === host
|
||||||
? `@${x.username}`
|
? `@${x.username}`
|
||||||
: `@${x.username}@${toASCII(otherHost)}`;
|
: `@${x.username}@${toASCII(otherHost)}`;
|
||||||
|
|
||||||
// 自分は除外
|
// exclude me
|
||||||
if ($i.username === x.username && (x.host == null || x.host === host))
|
if ($i.username === x.username && (x.host == null || x.host === host))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// 重複は除外
|
// remove duplicates
|
||||||
if (text.value.includes(`${mention} `)) continue;
|
if (text.value.includes(`${mention} `)) continue;
|
||||||
|
|
||||||
text.value += `${mention} `;
|
text.value += `${mention} `;
|
||||||
|
@ -505,10 +504,10 @@ if (props.reply && props.reply.text != null) {
|
||||||
|
|
||||||
if (props.channel) {
|
if (props.channel) {
|
||||||
visibility.value = "public";
|
visibility.value = "public";
|
||||||
localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
|
localOnly.value = true; // TODO: Delete this once channels get federated
|
||||||
}
|
}
|
||||||
|
|
||||||
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
// Inherit the original visibility
|
||||||
if (
|
if (
|
||||||
props.reply &&
|
props.reply &&
|
||||||
["home", "followers", "specified"].includes(props.reply.visibility)
|
["home", "followers", "specified"].includes(props.reply.visibility)
|
||||||
|
@ -629,10 +628,6 @@ function togglePoll() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTag(tag: string) {
|
|
||||||
insertTextAtCursor(textareaEl.value, ` #${tag} `);
|
|
||||||
}
|
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
if (textareaEl.value) {
|
if (textareaEl.value) {
|
||||||
textareaEl.value.focus();
|
textareaEl.value.focus();
|
||||||
|
@ -736,6 +731,8 @@ function clear() {
|
||||||
quoteId.value = null;
|
quoteId.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: ev.which is deprecated
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/which
|
||||||
function onKeydown(ev: KeyboardEvent) {
|
function onKeydown(ev: KeyboardEvent) {
|
||||||
if (
|
if (
|
||||||
(ev.which === 10 || ev.which === 13) &&
|
(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/")) {
|
if (!props.renote && !quoteId.value && paste.startsWith(url + "/notes/")) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
@ -787,7 +784,7 @@ async function onPaste(ev: ClipboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
quoteId.value = paste
|
quoteId.value = paste
|
||||||
.substr(url.length)
|
.substring(url.length)
|
||||||
.match(/^\/notes\/(.+?)\/?$/)[1];
|
.match(/^\/notes\/(.+?)\/?$/)[1];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -886,8 +883,8 @@ async function post() {
|
||||||
renoteId: props.renote
|
renoteId: props.renote
|
||||||
? props.renote.id
|
? props.renote.id
|
||||||
: quoteId.value
|
: quoteId.value
|
||||||
? quoteId.value
|
? quoteId.value
|
||||||
: undefined,
|
: undefined,
|
||||||
channelId: props.channel ? props.channel.id : undefined,
|
channelId: props.channel ? props.channel.id : undefined,
|
||||||
poll: poll.value,
|
poll: poll.value,
|
||||||
cw: useCw.value ? cw.value || "" : undefined,
|
cw: useCw.value ? cw.value || "" : undefined,
|
||||||
|
|
|
@ -371,10 +371,10 @@ function onChangeUsername(): void {
|
||||||
const err = !username.value.match(/^[a-zA-Z0-9_]+$/)
|
const err = !username.value.match(/^[a-zA-Z0-9_]+$/)
|
||||||
? "invalid-format"
|
? "invalid-format"
|
||||||
: username.value.length < 1
|
: username.value.length < 1
|
||||||
? "min-range"
|
? "min-range"
|
||||||
: username.value.length > 20
|
: username.value.length > 20
|
||||||
? "max-range"
|
? "max-range"
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
usernameState.value = err;
|
usernameState.value = err;
|
||||||
|
@ -410,16 +410,16 @@ function onChangeEmail(): void {
|
||||||
emailState.value = result.available
|
emailState.value = result.available
|
||||||
? "ok"
|
? "ok"
|
||||||
: result.reason === "used"
|
: result.reason === "used"
|
||||||
? "unavailable:used"
|
? "unavailable:used"
|
||||||
: result.reason === "format"
|
: result.reason === "format"
|
||||||
? "unavailable:format"
|
? "unavailable:format"
|
||||||
: result.reason === "disposable"
|
: result.reason === "disposable"
|
||||||
? "unavailable:disposable"
|
? "unavailable:disposable"
|
||||||
: result.reason === "mx"
|
: result.reason === "mx"
|
||||||
? "unavailable:mx"
|
? "unavailable:mx"
|
||||||
: result.reason === "smtp"
|
: result.reason === "smtp"
|
||||||
? "unavailable:smtp"
|
? "unavailable:smtp"
|
||||||
: "unavailable";
|
: "unavailable";
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
emailState.value = "error";
|
emailState.value = "error";
|
||||||
|
|
|
@ -29,11 +29,11 @@ const _time =
|
||||||
props.time == null
|
props.time == null
|
||||||
? NaN
|
? NaN
|
||||||
: typeof props.time === "number"
|
: typeof props.time === "number"
|
||||||
? props.time
|
? props.time
|
||||||
: (props.time instanceof Date
|
: (props.time instanceof Date
|
||||||
? props.time
|
? props.time
|
||||||
: new Date(props.time)
|
: new Date(props.time)
|
||||||
).getTime();
|
).getTime();
|
||||||
const invalid = Number.isNaN(_time);
|
const invalid = Number.isNaN(_time);
|
||||||
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
|
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
|
||||||
|
|
||||||
|
@ -46,30 +46,30 @@ const relative = computed<string>(() => {
|
||||||
return ago >= 31536000
|
return ago >= 31536000
|
||||||
? i18n.t("_ago.yearsAgo", { n: Math.floor(ago / 31536000).toString() })
|
? i18n.t("_ago.yearsAgo", { n: Math.floor(ago / 31536000).toString() })
|
||||||
: ago >= 2592000
|
: ago >= 2592000
|
||||||
? i18n.t("_ago.monthsAgo", {
|
? i18n.t("_ago.monthsAgo", {
|
||||||
n: Math.floor(ago / 2592000).toString(),
|
n: Math.floor(ago / 2592000).toString(),
|
||||||
})
|
})
|
||||||
: ago >= 604800
|
: ago >= 604800
|
||||||
? i18n.t("_ago.weeksAgo", {
|
? i18n.t("_ago.weeksAgo", {
|
||||||
n: Math.floor(ago / 604800).toString(),
|
n: Math.floor(ago / 604800).toString(),
|
||||||
})
|
})
|
||||||
: ago >= 86400
|
: ago >= 86400
|
||||||
? i18n.t("_ago.daysAgo", {
|
? i18n.t("_ago.daysAgo", {
|
||||||
n: Math.floor(ago / 86400).toString(),
|
n: Math.floor(ago / 86400).toString(),
|
||||||
})
|
})
|
||||||
: ago >= 3600
|
: ago >= 3600
|
||||||
? i18n.t("_ago.hoursAgo", {
|
? i18n.t("_ago.hoursAgo", {
|
||||||
n: Math.floor(ago / 3600).toString(),
|
n: Math.floor(ago / 3600).toString(),
|
||||||
})
|
})
|
||||||
: ago >= 60
|
: ago >= 60
|
||||||
? i18n.t("_ago.minutesAgo", { n: (~~(ago / 60)).toString() })
|
? i18n.t("_ago.minutesAgo", { n: (~~(ago / 60)).toString() })
|
||||||
: ago >= 10
|
: ago >= 10
|
||||||
? i18n.t("_ago.secondsAgo", {
|
? i18n.t("_ago.secondsAgo", {
|
||||||
n: (~~(ago % 60)).toString(),
|
n: (~~(ago % 60)).toString(),
|
||||||
})
|
})
|
||||||
: ago >= -1
|
: ago >= -1
|
||||||
? i18n.ts._ago.justNow
|
? i18n.ts._ago.justNow
|
||||||
: i18n.ts._ago.future;
|
: i18n.ts._ago.future;
|
||||||
});
|
});
|
||||||
|
|
||||||
let tickId: number;
|
let tickId: number;
|
||||||
|
|
|
@ -127,18 +127,18 @@ const pagination = {
|
||||||
...(state.value === "federating"
|
...(state.value === "federating"
|
||||||
? { federating: true }
|
? { federating: true }
|
||||||
: state.value === "subscribing"
|
: state.value === "subscribing"
|
||||||
? { subscribing: true }
|
? { subscribing: true }
|
||||||
: state.value === "publishing"
|
: state.value === "publishing"
|
||||||
? { publishing: true }
|
? { publishing: true }
|
||||||
: state.value === "suspended"
|
: state.value === "suspended"
|
||||||
? { suspended: true }
|
? { suspended: true }
|
||||||
: state.value === "blocked"
|
: state.value === "blocked"
|
||||||
? { blocked: true }
|
? { blocked: true }
|
||||||
: state.value === "silenced"
|
: state.value === "silenced"
|
||||||
? { silenced: true }
|
? { silenced: true }
|
||||||
: state.value === "notResponding"
|
: state.value === "notResponding"
|
||||||
? { notResponding: true }
|
? { notResponding: true }
|
||||||
: {}),
|
: {}),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -105,8 +105,8 @@ async function init() {
|
||||||
provider.value = meta.enableHcaptcha
|
provider.value = meta.enableHcaptcha
|
||||||
? "hcaptcha"
|
? "hcaptcha"
|
||||||
: meta.enableRecaptcha
|
: meta.enableRecaptcha
|
||||||
? "recaptcha"
|
? "recaptcha"
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
|
|
|
@ -51,23 +51,23 @@ const label =
|
||||||
props.type === "process"
|
props.type === "process"
|
||||||
? "Process"
|
? "Process"
|
||||||
: props.type === "active"
|
: props.type === "active"
|
||||||
? "Active"
|
? "Active"
|
||||||
: props.type === "delayed"
|
: props.type === "delayed"
|
||||||
? "Delayed"
|
? "Delayed"
|
||||||
: props.type === "waiting"
|
: props.type === "waiting"
|
||||||
? "Waiting"
|
? "Waiting"
|
||||||
: ("?" as never);
|
: ("?" as never);
|
||||||
|
|
||||||
const color =
|
const color =
|
||||||
props.type === "process"
|
props.type === "process"
|
||||||
? "#c4a7e7"
|
? "#c4a7e7"
|
||||||
: props.type === "active"
|
: props.type === "active"
|
||||||
? "#31748f"
|
? "#31748f"
|
||||||
: props.type === "delayed"
|
: props.type === "delayed"
|
||||||
? "#eb6f92"
|
? "#eb6f92"
|
||||||
: props.type === "waiting"
|
: props.type === "waiting"
|
||||||
? "#f6c177"
|
? "#f6c177"
|
||||||
: ("?" as never);
|
: ("?" as never);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const vLineColor = defaultStore.state.darkMode
|
const vLineColor = defaultStore.state.darkMode
|
||||||
|
|
|
@ -96,23 +96,23 @@ const label =
|
||||||
props.type === "process"
|
props.type === "process"
|
||||||
? "Process"
|
? "Process"
|
||||||
: props.type === "active"
|
: props.type === "active"
|
||||||
? "Active"
|
? "Active"
|
||||||
: props.type === "delayed"
|
: props.type === "delayed"
|
||||||
? "Delayed"
|
? "Delayed"
|
||||||
: props.type === "waiting"
|
: props.type === "waiting"
|
||||||
? "Waiting"
|
? "Waiting"
|
||||||
: ("?" as never);
|
: ("?" as never);
|
||||||
|
|
||||||
const color =
|
const color =
|
||||||
props.type === "process"
|
props.type === "process"
|
||||||
? "#9ccfd8"
|
? "#9ccfd8"
|
||||||
: props.type === "active"
|
: props.type === "active"
|
||||||
? "#31748f"
|
? "#31748f"
|
||||||
: props.type === "delayed"
|
: props.type === "delayed"
|
||||||
? "#eb6f92"
|
? "#eb6f92"
|
||||||
: props.type === "waiting"
|
: props.type === "waiting"
|
||||||
? "#f6c177"
|
? "#f6c177"
|
||||||
: ("?" as never);
|
: ("?" as never);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
chartInstance = new Chart(chartEl.value, {
|
chartInstance = new Chart(chartEl.value, {
|
||||||
|
|
|
@ -117,8 +117,8 @@ onMounted(() => {
|
||||||
props.domain === "inbox"
|
props.domain === "inbox"
|
||||||
? "admin/queue/inbox-delayed"
|
? "admin/queue/inbox-delayed"
|
||||||
: props.domain === "deliver"
|
: props.domain === "deliver"
|
||||||
? "admin/queue/deliver-delayed"
|
? "admin/queue/deliver-delayed"
|
||||||
: null,
|
: null,
|
||||||
{},
|
{},
|
||||||
).then((result) => {
|
).then((result) => {
|
||||||
jobs.value = result;
|
jobs.value = result;
|
||||||
|
|
|
@ -290,14 +290,14 @@ async function init() {
|
||||||
meta.sensitiveMediaDetectionSensitivity === "veryLow"
|
meta.sensitiveMediaDetectionSensitivity === "veryLow"
|
||||||
? 0
|
? 0
|
||||||
: meta.sensitiveMediaDetectionSensitivity === "low"
|
: meta.sensitiveMediaDetectionSensitivity === "low"
|
||||||
? 1
|
? 1
|
||||||
: meta.sensitiveMediaDetectionSensitivity === "medium"
|
: meta.sensitiveMediaDetectionSensitivity === "medium"
|
||||||
? 2
|
? 2
|
||||||
: meta.sensitiveMediaDetectionSensitivity === "high"
|
: meta.sensitiveMediaDetectionSensitivity === "high"
|
||||||
? 3
|
? 3
|
||||||
: meta.sensitiveMediaDetectionSensitivity === "veryHigh"
|
: meta.sensitiveMediaDetectionSensitivity === "veryHigh"
|
||||||
? 4
|
? 4
|
||||||
: 0;
|
: 0;
|
||||||
setSensitiveFlagAutomatically.value = meta.setSensitiveFlagAutomatically;
|
setSensitiveFlagAutomatically.value = meta.setSensitiveFlagAutomatically;
|
||||||
enableSensitiveMediaDetectionForVideos.value =
|
enableSensitiveMediaDetectionForVideos.value =
|
||||||
meta.enableSensitiveMediaDetectionForVideos;
|
meta.enableSensitiveMediaDetectionForVideos;
|
||||||
|
@ -317,14 +317,14 @@ function save() {
|
||||||
sensitiveMediaDetectionSensitivity.value === 0
|
sensitiveMediaDetectionSensitivity.value === 0
|
||||||
? "veryLow"
|
? "veryLow"
|
||||||
: sensitiveMediaDetectionSensitivity.value === 1
|
: sensitiveMediaDetectionSensitivity.value === 1
|
||||||
? "low"
|
? "low"
|
||||||
: sensitiveMediaDetectionSensitivity.value === 2
|
: sensitiveMediaDetectionSensitivity.value === 2
|
||||||
? "medium"
|
? "medium"
|
||||||
: sensitiveMediaDetectionSensitivity.value === 3
|
: sensitiveMediaDetectionSensitivity.value === 3
|
||||||
? "high"
|
? "high"
|
||||||
: sensitiveMediaDetectionSensitivity.value === 4
|
: sensitiveMediaDetectionSensitivity.value === 4
|
||||||
? "veryHigh"
|
? "veryHigh"
|
||||||
: 0,
|
: 0,
|
||||||
setSensitiveFlagAutomatically: setSensitiveFlagAutomatically.value,
|
setSensitiveFlagAutomatically: setSensitiveFlagAutomatically.value,
|
||||||
enableSensitiveMediaDetectionForVideos:
|
enableSensitiveMediaDetectionForVideos:
|
||||||
enableSensitiveMediaDetectionForVideos.value,
|
enableSensitiveMediaDetectionForVideos.value,
|
||||||
|
|
|
@ -98,14 +98,14 @@ function onEndpointChange() {
|
||||||
p.type === "String"
|
p.type === "String"
|
||||||
? ""
|
? ""
|
||||||
: p.type === "Number"
|
: p.type === "Number"
|
||||||
? 0
|
? 0
|
||||||
: p.type === "Boolean"
|
: p.type === "Boolean"
|
||||||
? false
|
? false
|
||||||
: p.type === "Array"
|
: p.type === "Array"
|
||||||
? []
|
? []
|
||||||
: p.type === "Object"
|
: p.type === "Object"
|
||||||
? {}
|
? {}
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
body.value = JSON5.stringify(endpointBody, null, 2);
|
body.value = JSON5.stringify(endpointBody, null, 2);
|
||||||
});
|
});
|
||||||
|
|
|
@ -229,14 +229,7 @@ function edit() {
|
||||||
|
|
||||||
watch(() => props.postId, fetchPost, { immediate: true });
|
watch(() => props.postId, fetchPost, { immediate: true });
|
||||||
|
|
||||||
const headerActions = computed(() => [
|
const headerActions = computed(() => []);
|
||||||
{
|
|
||||||
icon: `${icon("ph-pencil")}`,
|
|
||||||
text: i18n.ts.edit,
|
|
||||||
handler: edit,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const headerTabs = computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata(
|
definePageMetadata(
|
||||||
|
|
|
@ -108,11 +108,11 @@
|
||||||
>{{ i18n.ts._theme.manage
|
>{{ i18n.ts._theme.manage
|
||||||
}}<template #suffix>{{ themesCount }}</template></FormLink
|
}}<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
|
><template #icon
|
||||||
><i :class="icon('ph-planet')"></i></template
|
><i :class="icon('ph-planet')"></i></template
|
||||||
>{{ i18n.ts._theme.explore }}</FormLink
|
>{{ i18n.ts._theme.explore }}</FormLink
|
||||||
>
|
> -->
|
||||||
<FormLink to="/settings/theme/install"
|
<FormLink to="/settings/theme/install"
|
||||||
><template #icon
|
><template #icon
|
||||||
><i :class="icon('ph-download-simple')"></i></template
|
><i :class="icon('ph-download-simple')"></i></template
|
||||||
|
|
|
@ -90,8 +90,8 @@
|
||||||
color: color.forPreview
|
color: color.forPreview
|
||||||
? color.forPreview
|
? color.forPreview
|
||||||
: theme.base === 'light'
|
: theme.base === 'light'
|
||||||
? '#5f5f5f'
|
? '#5f5f5f'
|
||||||
: '#dadada',
|
: '#dadada',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
A
|
A
|
||||||
|
|
|
@ -270,12 +270,12 @@ definePageMetadata(
|
||||||
src.value === "local"
|
src.value === "local"
|
||||||
? "ph-users ph-lg"
|
? "ph-users ph-lg"
|
||||||
: src.value === "social"
|
: src.value === "social"
|
||||||
? "ph-handshake ph-lg"
|
? "ph-handshake ph-lg"
|
||||||
: src.value === "recommended"
|
: src.value === "recommended"
|
||||||
? "ph-thumbs-up ph-lg"
|
? "ph-thumbs-up ph-lg"
|
||||||
: src.value === "global"
|
: src.value === "global"
|
||||||
? "ph-planet ph-lg"
|
? "ph-planet ph-lg"
|
||||||
: "ph-house ph-lg",
|
: "ph-house ph-lg",
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import XCheatSheet from "@/components/MkCheatSheetDialog.vue";
|
import XCheatSheet from "@/components/MkCheatSheetDialog.vue";
|
||||||
import XTutorial from "@/components/MkTutorialDialog.vue";
|
import XTutorial from "@/components/MkTutorialDialog.vue";
|
||||||
|
import { defaultStore } from "@/store";
|
||||||
import { host } from "@/config";
|
import { host } from "@/config";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
import { defaultStore } from "@/store";
|
|
||||||
import type { MenuItem } from "@/types/menu";
|
import type { MenuItem } from "@/types/menu";
|
||||||
|
|
||||||
const instanceSpecificItems: MenuItem[] = [];
|
const instanceSpecificItems: MenuItem[] = [];
|
||||||
|
|
|
@ -35,8 +35,8 @@
|
||||||
widgetProps.src === "list"
|
widgetProps.src === "list"
|
||||||
? widgetProps.list.name
|
? widgetProps.list.name
|
||||||
: widgetProps.src === "antenna"
|
: widgetProps.src === "antenna"
|
||||||
? widgetProps.antenna.name
|
? widgetProps.antenna.name
|
||||||
: i18n.t("_timelines." + widgetProps.src)
|
: i18n.t("_timelines." + widgetProps.src)
|
||||||
}}</span>
|
}}</span>
|
||||||
<i
|
<i
|
||||||
:class="
|
:class="
|
||||||
|
@ -57,8 +57,8 @@
|
||||||
widgetProps.src === 'list'
|
widgetProps.src === 'list'
|
||||||
? `list:${widgetProps.list.id}`
|
? `list:${widgetProps.list.id}`
|
||||||
: widgetProps.src === 'antenna'
|
: widgetProps.src === 'antenna'
|
||||||
? `antenna:${widgetProps.antenna.id}`
|
? `antenna:${widgetProps.antenna.id}`
|
||||||
: widgetProps.src
|
: widgetProps.src
|
||||||
"
|
"
|
||||||
:src="widgetProps.src"
|
:src="widgetProps.src"
|
||||||
:list="widgetProps.list ? widgetProps.list.id : null"
|
:list="widgetProps.list ? widgetProps.list.id : null"
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import Response from "./response";
|
import { RequestCanceledError, isCancel } from "./cancel";
|
||||||
import OAuth from "./oauth";
|
import Converter from "./converter";
|
||||||
import { isCancel, RequestCanceledError } from "./cancel";
|
import Entity from "./entity";
|
||||||
import { ProxyConfig } from "./proxy_config";
|
import FilterContext from "./filter_context";
|
||||||
import generator, {
|
import generator, {
|
||||||
detector,
|
|
||||||
MegalodonInterface,
|
MegalodonInterface,
|
||||||
WebSocketInterface,
|
WebSocketInterface,
|
||||||
|
detector,
|
||||||
} from "./megalodon";
|
} from "./megalodon";
|
||||||
import Misskey from "./misskey";
|
import Misskey from "./misskey";
|
||||||
import Entity from "./entity";
|
|
||||||
import NotificationType from "./notification";
|
import NotificationType from "./notification";
|
||||||
import FilterContext from "./filter_context";
|
import OAuth from "./oauth";
|
||||||
import Converter from "./converter";
|
import { ProxyConfig } from "./proxy_config";
|
||||||
|
import Response from "./response";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Response,
|
Response,
|
||||||
|
|
|
@ -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 OAuth from "./oauth";
|
||||||
import proxyAgent, { ProxyConfig } from "./proxy_config";
|
import proxyAgent, { ProxyConfig } from "./proxy_config";
|
||||||
import Entity from "./entity";
|
import Response from "./response";
|
||||||
import axios, { AxiosRequestConfig } from "axios";
|
|
||||||
import Misskey from "./misskey";
|
|
||||||
import { DEFAULT_UA } from "./default";
|
|
||||||
|
|
||||||
export interface WebSocketInterface {
|
export interface WebSocketInterface {
|
||||||
start(): void;
|
start(): void;
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import FormData from "form-data";
|
|
||||||
import AsyncLock from "async-lock";
|
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 { DEFAULT_UA } from "./default";
|
||||||
import { ProxyConfig } from "./proxy_config";
|
|
||||||
import OAuth from "./oauth";
|
|
||||||
import Response from "./response";
|
|
||||||
import Entity from "./entity";
|
import Entity from "./entity";
|
||||||
import {
|
import {
|
||||||
MegalodonInterface,
|
|
||||||
WebSocketInterface,
|
|
||||||
NoImplementedError,
|
|
||||||
ArgumentError,
|
ArgumentError,
|
||||||
|
MegalodonInterface,
|
||||||
|
NoImplementedError,
|
||||||
UnexpectedError,
|
UnexpectedError,
|
||||||
|
WebSocketInterface,
|
||||||
} from "./megalodon";
|
} from "./megalodon";
|
||||||
import MegalodonEntity from "@/entity";
|
import MisskeyAPI from "./misskey/api_client";
|
||||||
import fs from "node:fs";
|
|
||||||
import MisskeyNotificationType from "./misskey/notification";
|
import MisskeyNotificationType from "./misskey/notification";
|
||||||
|
import OAuth from "./oauth";
|
||||||
|
import { ProxyConfig } from "./proxy_config";
|
||||||
|
import Response from "./response";
|
||||||
|
|
||||||
type AccountCache = {
|
type AccountCache = {
|
||||||
locks: AsyncLock;
|
locks: AsyncLock;
|
||||||
|
|
Loading…
Reference in a new issue