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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -492,11 +491,11 @@ if (props.reply && props.reply.text != null) {
|
||||||
? `@${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];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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[] = [];
|
||||||
|
|
|
@ -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