Compare commits
334 Commits
2024.5.0-b
...
ed25519
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c33e48f75 | ||
|
|
61d904e8f1 | ||
|
|
8c76c7b8b5 | ||
|
|
aed28060e7 | ||
|
|
6942a920c8 | ||
|
|
29d9bbf05b | ||
|
|
c00b61e90b | ||
|
|
99113d59f4 | ||
|
|
cd19ad694c | ||
|
|
95918607f4 | ||
|
|
72cda5ca80 | ||
|
|
e602f2efda | ||
|
|
de677a5b1f | ||
|
|
1f0e7a40b6 | ||
|
|
b7349e5771 | ||
|
|
fe77f216c3 | ||
|
|
68bcd91d57 | ||
|
|
8b4933cc48 | ||
|
|
ffd12d0539 | ||
|
|
bda1de8a67 | ||
|
|
a0c93bbd4d | ||
|
|
5afc659afa | ||
|
|
41883c451d | ||
|
|
09b2e71e62 | ||
|
|
44f0064301 | ||
|
|
f0d738d8bf | ||
|
|
38a5e09a36 | ||
|
|
9e0a93f110 | ||
|
|
d3280fe7b3 | ||
|
|
c2d084bac4 | ||
|
|
070f0e723d | ||
|
|
a80a7f6458 | ||
|
|
613c1273b8 | ||
|
|
d0aada55c1 | ||
|
|
57bfffedae | ||
|
|
f2c412c180 | ||
|
|
7e2c3e4439 | ||
|
|
c80b16cdf8 | ||
|
|
3777779aa9 | ||
|
|
8ebc3b51f7 | ||
|
|
3b075c9c44 | ||
|
|
1001277d43 | ||
|
|
ce39c3a2fb | ||
|
|
1b84760c19 | ||
|
|
16795f18a7 | ||
|
|
f0b9d70720 | ||
|
|
aa0632727f | ||
|
|
d47fd4ffe1 | ||
|
|
4b9c60ad21 | ||
|
|
c5607d8633 | ||
|
|
722acf5986 | ||
|
|
09d30fef5b | ||
|
|
b9f3fccfac | ||
|
|
76181385d2 | ||
|
|
3c032dd5b9 | ||
|
|
6dd6fcf88f | ||
|
|
31e82fc29a | ||
|
|
7afa593d11 | ||
|
|
58c596cacf | ||
|
|
b5fd6183d2 | ||
|
|
c83c831c53 | ||
|
|
9fcae7d9b2 | ||
|
|
bcc92d546f | ||
|
|
1b175ea759 | ||
|
|
91de35ecdf | ||
|
|
6cd15275bb | ||
|
|
76b1c74a37 | ||
|
|
385969e9f5 | ||
|
|
121af778a0 | ||
|
|
6b876da44a | ||
|
|
f8ac3fe343 | ||
|
|
679318541a | ||
|
|
52d8a54fc7 | ||
|
|
02e0a86b12 | ||
|
|
600f16d625 | ||
|
|
a5407131d4 | ||
|
|
b61f270eae | ||
|
|
55c990e0d9 | ||
|
|
9ef6c4716c | ||
|
|
f119f8c2cc | ||
|
|
984d582796 | ||
|
|
fe852920c3 | ||
|
|
0ea88c07b4 | ||
|
|
8e1d94c6c7 | ||
|
|
b9ed3b2427 | ||
|
|
6dd2e9fc0b | ||
|
|
fab7d5e484 | ||
|
|
5d03efa1bb | ||
|
|
de1fe7cc5a | ||
|
|
eafae79869 | ||
|
|
427648c4b8 | ||
|
|
f1b1e2a7cc | ||
|
|
7353c7397f | ||
|
|
7306a6c7c7 | ||
|
|
a6edd50a5d | ||
|
|
4096dabe1e | ||
|
|
0e512d4ff6 | ||
|
|
77012f2f29 | ||
|
|
1c5d0cf536 | ||
|
|
634764e1a6 | ||
|
|
b95a0457a9 | ||
|
|
b269c43168 | ||
|
|
2acbec6891 | ||
|
|
961cb6c5ee | ||
|
|
00b213373b | ||
|
|
b8b4dc5038 | ||
|
|
9368eb3038 | ||
|
|
7c22a64b8c | ||
|
|
bf403aa656 | ||
|
|
faeab96e01 | ||
|
|
b50eb511b0 | ||
|
|
ac12ab8629 | ||
|
|
ef205fb60e | ||
|
|
3254f7c5cd | ||
|
|
7e21497edc | ||
|
|
1e78ef1cb8 | ||
|
|
8a9de081f1 | ||
|
|
4d2eddec2e | ||
|
|
a9012d3d0c | ||
|
|
2c84d06a66 | ||
|
|
e88f08ad7d | ||
|
|
1d6ccd9781 | ||
|
|
811ffbf3a4 | ||
|
|
bf33382082 | ||
|
|
1df90cef4c | ||
|
|
b683d79f8b | ||
|
|
77ae69355c | ||
|
|
f37d684fab | ||
|
|
a88579ca98 | ||
|
|
d0ee0203e1 | ||
|
|
379ce0145b | ||
|
|
34458d767b | ||
|
|
96fcb9f54c | ||
|
|
d4e2be68ee | ||
|
|
1a82a41f92 | ||
|
|
9bddb81efc | ||
|
|
220e112c83 | ||
|
|
c51347d78b | ||
|
|
dc3629e732 | ||
|
|
c73d739bd6 | ||
|
|
1616cb533e | ||
|
|
92367cf700 | ||
|
|
ff3a38a7f5 | ||
|
|
a3d4eae99d | ||
|
|
133970a184 | ||
|
|
64004fdea2 | ||
|
|
3717ff35a3 | ||
|
|
f31996eb42 | ||
|
|
bdaef5f8e1 | ||
|
|
9849aab402 | ||
|
|
61fae45390 | ||
|
|
e0cf5b2402 | ||
|
|
8592716139 | ||
|
|
00157864e9 | ||
|
|
8f833d742f | ||
|
|
d55e638a23 | ||
|
|
a697a7f97b | ||
|
|
ab69e113f4 | ||
|
|
65d19279a2 | ||
|
|
dbf9e1194b | ||
|
|
d4a8c63264 | ||
|
|
43cccaaee9 | ||
|
|
27ac3d795e | ||
|
|
2b8056a852 | ||
|
|
ecf7945fe8 | ||
|
|
cc1ee0106f | ||
|
|
6078081c33 | ||
|
|
a59aa20be8 | ||
|
|
61eec93f4e | ||
|
|
27d1b7e615 | ||
|
|
316d192bc0 | ||
|
|
2eaa3e256f | ||
|
|
46164f879b | ||
|
|
374c8791d7 | ||
|
|
e8f523f00a | ||
|
|
030082f756 | ||
|
|
dc55adbaf7 | ||
|
|
90ba1ca1f9 | ||
|
|
514a65e453 | ||
|
|
a3468fd05b | ||
|
|
97be1a53ad | ||
|
|
1e007b63aa | ||
|
|
a0c596b030 | ||
|
|
eaa85f5aa3 | ||
|
|
dfeaa1145b | ||
|
|
0082747237 | ||
|
|
5b8f8e7087 | ||
|
|
be11fd7508 | ||
|
|
ac4a001e9f | ||
|
|
24d4124ffc | ||
|
|
eaadd643eb | ||
|
|
cf670e8a3d | ||
|
|
e57ce4fa0f | ||
|
|
44cafbb9f2 | ||
|
|
f75e46752e | ||
|
|
244adef70e | ||
|
|
e2eb7e8ca9 | ||
|
|
80f3cb96b0 | ||
|
|
89b27d8587 | ||
|
|
1bb1a32986 | ||
|
|
de9e391e34 | ||
|
|
934f9f80bd | ||
|
|
4704dfe061 | ||
|
|
d3b969306c | ||
|
|
4579be0f54 | ||
|
|
d7982e471c | ||
|
|
a7a8dc4dbb | ||
|
|
cf2256cf41 | ||
|
|
28e0e20879 | ||
|
|
e50107792c | ||
|
|
579b88adc7 | ||
|
|
6af9492ea5 | ||
|
|
d013e4516d | ||
|
|
805a11aadb | ||
|
|
1b81ca4563 | ||
|
|
1df8ea824e | ||
|
|
3ffbf6296f | ||
|
|
e0b47999fa | ||
|
|
83a9aa4533 | ||
|
|
611e303bab | ||
|
|
be102f2622 | ||
|
|
baca55c814 | ||
|
|
c58b4f8c24 | ||
|
|
d200da8690 | ||
|
|
aa5181cdfc | ||
|
|
d7c32cef70 | ||
|
|
76487de5ed | ||
|
|
e2b574a97c | ||
|
|
9bfa38e601 | ||
|
|
eb8495648e | ||
|
|
154a2026ea | ||
|
|
8104963e1d | ||
|
|
da4a44b337 | ||
|
|
1690e0617e | ||
|
|
70693af4e4 | ||
|
|
d168ec7dd5 | ||
|
|
08e3a7c008 | ||
|
|
4310229ca5 | ||
|
|
75a2f1c1e8 | ||
|
|
d0da9f32dc | ||
|
|
6907b6505a | ||
|
|
74c8f0a483 | ||
|
|
e543ffe368 | ||
|
|
9973610286 | ||
|
|
844feb1bb3 | ||
|
|
fef9ebfe06 | ||
|
|
39fba74dd1 | ||
|
|
a701fed9e5 | ||
|
|
ab29cbab41 | ||
|
|
01b8d2fdb1 | ||
|
|
0127f89298 | ||
|
|
689a9ce5f9 | ||
|
|
834f46537d | ||
|
|
0e509c440e | ||
|
|
6b02efac32 | ||
|
|
a84de3c02f | ||
|
|
021801c721 | ||
|
|
e4fea42436 | ||
|
|
430f0b7911 | ||
|
|
6e4357c378 | ||
|
|
ac4336db43 | ||
|
|
4b9ffb8dc0 | ||
|
|
31bf1dbc95 | ||
|
|
2a622b02dc | ||
|
|
0082f6f8e8 | ||
|
|
15782f7f47 | ||
|
|
ac2cf73a14 | ||
|
|
7d77c7044e | ||
|
|
1af1bc87bd | ||
|
|
821a79ff28 | ||
|
|
7a334a5e28 | ||
|
|
79249a0514 | ||
|
|
eefca034fc | ||
|
|
25cc9e0bf1 | ||
|
|
83f635835e | ||
|
|
941aed6a14 | ||
|
|
d772eacfa1 | ||
|
|
6a56aea422 | ||
|
|
c7eed1c360 | ||
|
|
76b20dc76c | ||
|
|
7eb19d5a8e | ||
|
|
64fcf736cc | ||
|
|
2926f68d8e | ||
|
|
41a461edbe | ||
|
|
2dde845738 | ||
|
|
862ebe23af | ||
|
|
89e1ff699a | ||
|
|
25d5a8cb7e | ||
|
|
aabdb666b7 | ||
|
|
13af6f2313 | ||
|
|
a405b62827 | ||
|
|
e4f70f017e | ||
|
|
1357b076d0 | ||
|
|
30820d9e0a | ||
|
|
ea6c38cc6b | ||
|
|
d86b8c8752 | ||
|
|
9111b5c482 | ||
|
|
65bd187d85 | ||
|
|
86c9f0b0fb | ||
|
|
65fa25a208 | ||
|
|
67758d2d1e | ||
|
|
fd71ad7a5f | ||
|
|
0aa316ee12 | ||
|
|
743b740775 | ||
|
|
bec6159b4a | ||
|
|
54fe8ca600 | ||
|
|
a5cccf3799 | ||
|
|
87ded2bd1c | ||
|
|
16cea7d3b6 | ||
|
|
7751d80056 | ||
|
|
66c0942d7e | ||
|
|
5f89b0a2a3 | ||
|
|
434520a14e | ||
|
|
735714d61c | ||
|
|
fc20ef0181 | ||
|
|
a1e6cb02b8 | ||
|
|
a4e7d6940b | ||
|
|
2bc4221f40 | ||
|
|
aaacfabc1b | ||
|
|
59ae735169 | ||
|
|
8579cb222f | ||
|
|
f6b7872a02 | ||
|
|
9705ec4a47 | ||
|
|
437e69cfc4 | ||
|
|
eb8bef486d | ||
|
|
5876a28f1e | ||
|
|
e2a8f4f880 | ||
|
|
13e0a64a77 | ||
|
|
1d780ac010 | ||
|
|
172546f3ef | ||
|
|
00738b90c2 | ||
|
|
5b7b8503cd | ||
|
|
1835397385 | ||
|
|
02dfe0a3d5 |
@@ -1,5 +1,11 @@
|
|||||||
|
# misskey settings
|
||||||
|
# MISSKEY_URL=https://example.tld/
|
||||||
|
|
||||||
# db settings
|
# db settings
|
||||||
POSTGRES_PASSWORD=example-misskey-pass
|
POSTGRES_PASSWORD=example-misskey-pass
|
||||||
|
# DATABASE_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
POSTGRES_USER=example-misskey-user
|
POSTGRES_USER=example-misskey-user
|
||||||
|
# DATABASE_USER=${POSTGRES_USER}
|
||||||
POSTGRES_DB=misskey
|
POSTGRES_DB=misskey
|
||||||
|
# DATABASE_DB=${POSTGRES_DB}
|
||||||
DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}"
|
DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#───┘ URL └─────────────────────────────────────────────────────
|
#───┘ URL └─────────────────────────────────────────────────────
|
||||||
|
|
||||||
# Final accessible URL seen by a user.
|
# Final accessible URL seen by a user.
|
||||||
|
# You can set url from an environment variable instead.
|
||||||
url: https://example.tld/
|
url: https://example.tld/
|
||||||
|
|
||||||
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
|
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
|
||||||
@@ -38,9 +39,11 @@ db:
|
|||||||
port: 5432
|
port: 5432
|
||||||
|
|
||||||
# Database name
|
# Database name
|
||||||
|
# You can set db from an environment variable instead.
|
||||||
db: misskey
|
db: misskey
|
||||||
|
|
||||||
# Auth
|
# Auth
|
||||||
|
# You can set user and pass from environment variables instead.
|
||||||
user: example-misskey-user
|
user: example-misskey-user
|
||||||
pass: example-misskey-pass
|
pass: example-misskey-pass
|
||||||
|
|
||||||
@@ -136,6 +139,21 @@ redis:
|
|||||||
|
|
||||||
id: 'aidx'
|
id: 'aidx'
|
||||||
|
|
||||||
|
# ┌────────────────┐
|
||||||
|
#───┘ Error tracking └──────────────────────────────────────────
|
||||||
|
|
||||||
|
# Sentry is available for error tracking.
|
||||||
|
# See the Sentry documentation for more details on options.
|
||||||
|
|
||||||
|
#sentryForBackend:
|
||||||
|
# enableNodeProfiling: true
|
||||||
|
# options:
|
||||||
|
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||||
|
|
||||||
|
#sentryForFrontend:
|
||||||
|
# options:
|
||||||
|
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||||
|
|
||||||
# ┌─────────────────────┐
|
# ┌─────────────────────┐
|
||||||
#───┘ Other configuration └─────────────────────────────────────
|
#───┘ Other configuration └─────────────────────────────────────
|
||||||
|
|
||||||
@@ -146,12 +164,12 @@ id: 'aidx'
|
|||||||
#clusterLimit: 1
|
#clusterLimit: 1
|
||||||
|
|
||||||
# Job concurrency per worker
|
# Job concurrency per worker
|
||||||
# deliverJobConcurrency: 128
|
# deliverJobConcurrency: 16
|
||||||
# inboxJobConcurrency: 16
|
# inboxJobConcurrency: 4
|
||||||
|
|
||||||
# Job rate limiter
|
# Job rate limiter
|
||||||
# deliverJobPerSec: 128
|
# deliverJobPerSec: 128
|
||||||
# inboxJobPerSec: 32
|
# inboxJobPerSec: 64
|
||||||
|
|
||||||
# Job attempts
|
# Job attempts
|
||||||
# deliverJobMaxAttempts: 12
|
# deliverJobMaxAttempts: 12
|
||||||
|
|||||||
@@ -205,6 +205,21 @@ redis:
|
|||||||
|
|
||||||
id: 'aidx'
|
id: 'aidx'
|
||||||
|
|
||||||
|
# ┌────────────────┐
|
||||||
|
#───┘ Error tracking └──────────────────────────────────────────
|
||||||
|
|
||||||
|
# Sentry is available for error tracking.
|
||||||
|
# See the Sentry documentation for more details on options.
|
||||||
|
|
||||||
|
#sentryForBackend:
|
||||||
|
# enableNodeProfiling: true
|
||||||
|
# options:
|
||||||
|
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||||
|
|
||||||
|
#sentryForFrontend:
|
||||||
|
# options:
|
||||||
|
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||||
|
|
||||||
# ┌─────────────────────┐
|
# ┌─────────────────────┐
|
||||||
#───┘ Other configuration └─────────────────────────────────────
|
#───┘ Other configuration └─────────────────────────────────────
|
||||||
|
|
||||||
@@ -215,15 +230,15 @@ id: 'aidx'
|
|||||||
#clusterLimit: 1
|
#clusterLimit: 1
|
||||||
|
|
||||||
# Job concurrency per worker
|
# Job concurrency per worker
|
||||||
#deliverJobConcurrency: 128
|
#deliverJobConcurrency: 16
|
||||||
#inboxJobConcurrency: 16
|
#inboxJobConcurrency: 4
|
||||||
#relationshipJobConcurrency: 16
|
#relationshipJobConcurrency: 16
|
||||||
# What's relationshipJob?:
|
# What's relationshipJob?:
|
||||||
# Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
|
# Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
|
||||||
|
|
||||||
# Job rate limiter
|
# Job rate limiter
|
||||||
#deliverJobPerSec: 128
|
#deliverJobPerSec: 1024
|
||||||
#inboxJobPerSec: 32
|
#inboxJobPerSec: 64
|
||||||
#relationshipJobPerSec: 64
|
#relationshipJobPerSec: 64
|
||||||
|
|
||||||
# Job attempts
|
# Job attempts
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
build:
|
build:
|
||||||
@@ -8,6 +6,7 @@ services:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- ../:/workspace:cached
|
- ../:/workspace:cached
|
||||||
|
- node_modules:/workspace/node_modules
|
||||||
|
|
||||||
command: sleep infinity
|
command: sleep infinity
|
||||||
|
|
||||||
@@ -46,6 +45,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
postgres-data:
|
postgres-data:
|
||||||
redis-data:
|
redis-data:
|
||||||
|
node_modules:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
internal_network:
|
internal_network:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Misskey",
|
"name": "Misskey",
|
||||||
"dockerComposeFile": "docker-compose.yml",
|
"dockerComposeFile": "compose.yml",
|
||||||
"service": "app",
|
"service": "app",
|
||||||
"workspaceFolder": "/workspace",
|
"workspaceFolder": "/workspace",
|
||||||
"features": {
|
"features": {
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"ghcr.io/devcontainers-contrib/features/corepack:1": {}
|
"ghcr.io/devcontainers-contrib/features/corepack:1": {}
|
||||||
},
|
},
|
||||||
"forwardPorts": [3000],
|
"forwardPorts": [3000],
|
||||||
"postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh",
|
"postCreateCommand": "/bin/bash .devcontainer/init.sh",
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
|
|||||||
@@ -132,6 +132,21 @@ redis:
|
|||||||
|
|
||||||
id: 'aidx'
|
id: 'aidx'
|
||||||
|
|
||||||
|
# ┌────────────────┐
|
||||||
|
#───┘ Error tracking └──────────────────────────────────────────
|
||||||
|
|
||||||
|
# Sentry is available for error tracking.
|
||||||
|
# See the Sentry documentation for more details on options.
|
||||||
|
|
||||||
|
#sentryForBackend:
|
||||||
|
# enableNodeProfiling: true
|
||||||
|
# options:
|
||||||
|
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||||
|
|
||||||
|
#sentryForFrontend:
|
||||||
|
# options:
|
||||||
|
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||||
|
|
||||||
# ┌─────────────────────┐
|
# ┌─────────────────────┐
|
||||||
#───┘ Other configuration └─────────────────────────────────────
|
#───┘ Other configuration └─────────────────────────────────────
|
||||||
|
|
||||||
@@ -142,12 +157,12 @@ id: 'aidx'
|
|||||||
#clusterLimit: 1
|
#clusterLimit: 1
|
||||||
|
|
||||||
# Job concurrency per worker
|
# Job concurrency per worker
|
||||||
# deliverJobConcurrency: 128
|
# deliverJobConcurrency: 16
|
||||||
# inboxJobConcurrency: 16
|
# inboxJobConcurrency: 4
|
||||||
|
|
||||||
# Job rate limiter
|
# Job rate limiter
|
||||||
# deliverJobPerSec: 128
|
# deliverJobPerSec: 1024
|
||||||
# inboxJobPerSec: 32
|
# inboxJobPerSec: 64
|
||||||
|
|
||||||
# Job attempts
|
# Job attempts
|
||||||
# deliverJobMaxAttempts: 12
|
# deliverJobMaxAttempts: 12
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
set -xe
|
set -xe
|
||||||
|
|
||||||
sudo chown -R node /workspace
|
sudo chown node node_modules
|
||||||
|
git config --global --add safe.directory /workspace
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
corepack install
|
corepack install
|
||||||
corepack enable
|
corepack enable
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Dockerfile
|
|||||||
build/
|
build/
|
||||||
built/
|
built/
|
||||||
db/
|
db/
|
||||||
docker-compose.yml
|
.devcontainer/compose.yml
|
||||||
node_modules/
|
node_modules/
|
||||||
packages/*/node_modules
|
packages/*/node_modules
|
||||||
redis/
|
redis/
|
||||||
@@ -28,4 +28,4 @@ fluent-emojis/
|
|||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
packages/*/.vscode/
|
packages/*/.vscode/
|
||||||
packages/backend/test/docker-compose.yml
|
packages/backend/test/compose.yml
|
||||||
|
|||||||
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@@ -1,4 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: [misskey-dev]
|
|
||||||
patreon: syuilo
|
|
||||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -2,3 +2,7 @@ contact_links:
|
|||||||
- name: 💬 Misskey official Discord
|
- name: 💬 Misskey official Discord
|
||||||
url: https://discord.gg/Wp8gVStHW3
|
url: https://discord.gg/Wp8gVStHW3
|
||||||
about: Chat freely about Misskey
|
about: Chat freely about Misskey
|
||||||
|
# 仮
|
||||||
|
- name: 💬 Start discussion
|
||||||
|
url: https://github.com/misskey-dev/misskey/discussions
|
||||||
|
about: The official forum to join conversation and ask question
|
||||||
|
|||||||
5
.github/workflows/api-misskey-js.yml
vendored
5
.github/workflows/api-misskey-js.yml
vendored
@@ -4,10 +4,11 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
|
- .github/workflows/api-misskey-js.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
|
- .github/workflows/api-misskey-js.yml
|
||||||
jobs:
|
jobs:
|
||||||
report:
|
report:
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ jobs:
|
|||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
2
.github/workflows/changelog-check.yml
vendored
2
.github/workflows/changelog-check.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
- name: Checkout head
|
- name: Checkout head
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
|
|
||||||
- name: setup node
|
- name: setup node
|
||||||
id: setup-node
|
id: setup-node
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: pnpm
|
cache: pnpm
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/misskey-js/package.json
|
- packages/misskey-js/package.json
|
||||||
- package.json
|
- package.json
|
||||||
|
- .github/workflows/check-misskey-js-version.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ develop ]
|
branches: [ develop ]
|
||||||
paths:
|
paths:
|
||||||
- packages/misskey-js/package.json
|
- packages/misskey-js/package.json
|
||||||
- package.json
|
- package.json
|
||||||
|
- .github/workflows/check-misskey-js-version.yml
|
||||||
jobs:
|
jobs:
|
||||||
check-version:
|
check-version:
|
||||||
# ルートの package.json と packages/misskey-js/package.json のバージョンが一致しているかを確認する
|
# ルートの package.json と packages/misskey-js/package.json のバージョンが一致しているかを確認する
|
||||||
|
|||||||
2
.github/workflows/docker-develop.yml
vendored
2
.github/workflows/docker-develop.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
|||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Build and push by digest
|
- name: Build and push by digest
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
|||||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
|||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Build and Push to Docker Hub
|
- name: Build and Push to Docker Hub
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
|||||||
8
.github/workflows/dockle.yml
vendored
8
.github/workflows/dockle.yml
vendored
@@ -13,14 +13,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
DOCKER_CONTENT_TRUST: 1
|
DOCKER_CONTENT_TRUST: 1
|
||||||
|
DOCKLE_VERSION: 0.4.14
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
- run: |
|
- name: Download and install dockle v${{ env.DOCKLE_VERSION }}
|
||||||
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v0.4.10/dockle_0.4.10_Linux-64bit.deb"
|
run: |
|
||||||
|
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb"
|
||||||
sudo dpkg -i dockle.deb
|
sudo dpkg -i dockle.deb
|
||||||
- run: |
|
- run: |
|
||||||
cp .config/docker_example.env .config/docker.env
|
cp .config/docker_example.env .config/docker.env
|
||||||
cp ./docker-compose_example.yml ./docker-compose.yml
|
cp ./compose_example.yml ./compose.yml
|
||||||
- run: |
|
- run: |
|
||||||
docker compose up -d web
|
docker compose up -d web
|
||||||
docker tag "$(docker compose images web | awk 'OFS=":" {print $4}' | tail -n +2)" misskey-web:latest
|
docker tag "$(docker compose images web | awk 'OFS=":" {print $4}' | tail -n +2)" misskey-web:latest
|
||||||
|
|||||||
4
.github/workflows/get-api-diff.yml
vendored
4
.github/workflows/get-api-diff.yml
vendored
@@ -9,7 +9,7 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
- .github/workflows/get-api-diff.yml
|
- .github/workflows/get-api-diff.yml
|
||||||
|
- .github/workflows/get-api-diff.yml
|
||||||
jobs:
|
jobs:
|
||||||
get-from-misskey:
|
get-from-misskey:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
24
.github/workflows/lint.yml
vendored
24
.github/workflows/lint.yml
vendored
@@ -10,15 +10,16 @@ on:
|
|||||||
- packages/frontend/**
|
- packages/frontend/**
|
||||||
- packages/sw/**
|
- packages/sw/**
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
- packages/shared/.eslintrc.js
|
- packages/shared/eslint.config.js
|
||||||
|
- .github/workflows/lint.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
- packages/frontend/**
|
- packages/frontend/**
|
||||||
- packages/sw/**
|
- packages/sw/**
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
- packages/shared/.eslintrc.js
|
- packages/shared/eslint.config.js
|
||||||
|
- .github/workflows/lint.yml
|
||||||
jobs:
|
jobs:
|
||||||
pnpm_install:
|
pnpm_install:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -28,7 +29,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- uses: actions/setup-node@v4.0.2
|
- uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -39,6 +40,8 @@ jobs:
|
|||||||
needs: [pnpm_install]
|
needs: [pnpm_install]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
env:
|
||||||
|
eslint-cache-version: v1
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
workspace:
|
workspace:
|
||||||
@@ -52,13 +55,20 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- uses: actions/setup-node@v4.0.2
|
- uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- run: pnpm i --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- run: pnpm --filter ${{ matrix.workspace }} run eslint
|
- name: Restore eslint cache
|
||||||
|
uses: actions/cache@v4.0.2
|
||||||
|
with:
|
||||||
|
path: node_modules/.cache/eslint
|
||||||
|
key: eslint-${{ env.eslint-cache-version }}-${{ hashFiles('/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
eslint-${{ env.eslint-cache-version }}-${{ hashFiles('/pnpm-lock.yaml') }}-
|
||||||
|
- run: pnpm --filter ${{ matrix.workspace }} run eslint --cache --cache-location node_modules/.cache/eslint --cache-strategy content
|
||||||
|
|
||||||
typecheck:
|
typecheck:
|
||||||
needs: [pnpm_install]
|
needs: [pnpm_install]
|
||||||
@@ -75,7 +85,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- uses: actions/setup-node@v4.0.2
|
- uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
5
.github/workflows/locale.yml
vendored
5
.github/workflows/locale.yml
vendored
@@ -4,10 +4,11 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- locales/**
|
- locales/**
|
||||||
|
- .github/workflows/locale.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- locales/**
|
- locales/**
|
||||||
|
- .github/workflows/locale.yml
|
||||||
jobs:
|
jobs:
|
||||||
locale_verify:
|
locale_verify:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -18,7 +19,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- uses: actions/setup-node@v4.0.2
|
- uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
2
.github/workflows/on-release-created.yml
vendored
2
.github/workflows/on-release-created.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
22
.github/workflows/release-edit-with-push.yml
vendored
22
.github/workflows/release-edit-with-push.yml
vendored
@@ -3,10 +3,10 @@ name: "Release Manager: sync changelog with PR"
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- release/**
|
- develop
|
||||||
paths:
|
paths:
|
||||||
- 'CHANGELOG.md'
|
- 'CHANGELOG.md'
|
||||||
|
# - .github/workflows/release-edit-with-push.yml
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@@ -20,21 +20,29 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# headがrelease/かつopenのPRを1つ取得
|
# headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
|
||||||
- name: Get PR
|
- name: Get PR
|
||||||
run: |
|
run: |
|
||||||
echo "pr_number=$(gh pr list --limit 1 --head "${{ github.ref_name }}" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
|
echo "pr_number=$(gh pr list --limit 1 --search "head:$GITHUB_REF_NAME base:$STABLE_BRANCH is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
|
||||||
id: get_pr
|
id: get_pr
|
||||||
|
env:
|
||||||
|
STABLE_BRANCH: ${{ vars.STABLE_BRANCH }}
|
||||||
- name: Get target version
|
- name: Get target version
|
||||||
uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v1
|
if: steps.get_pr.outputs.pr_number != ''
|
||||||
|
uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v2
|
||||||
id: v
|
id: v
|
||||||
# CHANGELOG.mdの内容を取得
|
# CHANGELOG.mdの内容を取得
|
||||||
- name: Get changelog
|
- name: Get changelog
|
||||||
uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v1
|
if: steps.get_pr.outputs.pr_number != ''
|
||||||
|
uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v2
|
||||||
with:
|
with:
|
||||||
version: ${{ steps.v.outputs.target_version }}
|
version: ${{ steps.v.outputs.target_version }}
|
||||||
id: changelog
|
id: changelog
|
||||||
# PRのnotesを更新
|
# PRのnotesを更新
|
||||||
- name: Update PR
|
- name: Update PR
|
||||||
|
if: steps.get_pr.outputs.pr_number != ''
|
||||||
run: |
|
run: |
|
||||||
gh pr edit ${{ steps.get_pr.outputs.pr_number }} --body "${{ steps.changelog.outputs.changelog }}"
|
gh pr edit "$PR_NUMBER" --body "$CHANGELOG"
|
||||||
|
env:
|
||||||
|
PR_NUMBER: ${{ steps.get_pr.outputs.pr_number }}
|
||||||
|
CHANGELOG: ${{ steps.changelog.outputs.changelog }}
|
||||||
|
|||||||
20
.github/workflows/release-with-dispatch.yml
vendored
20
.github/workflows/release-with-dispatch.yml
vendored
@@ -33,18 +33,21 @@ jobs:
|
|||||||
pr_number: ${{ steps.get_pr.outputs.pr_number }}
|
pr_number: ${{ steps.get_pr.outputs.pr_number }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# headがrelease/かつopenのPRを1つ取得
|
# headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
|
||||||
- name: Get PRs
|
- name: Get PRs
|
||||||
run: |
|
run: |
|
||||||
echo "pr_number=$(gh pr list --limit 1 --search "head:release/ is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
|
echo "pr_number=$(gh pr list --limit 1 --search "head:$GITHUB_REF_NAME base:$STABLE_BRANCH is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
|
||||||
id: get_pr
|
id: get_pr
|
||||||
|
env:
|
||||||
|
STABLE_BRANCH: ${{ vars.STABLE_BRANCH }}
|
||||||
|
|
||||||
merge:
|
merge:
|
||||||
uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v1
|
uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v2
|
||||||
needs: get-pr
|
needs: get-pr
|
||||||
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge == true }}
|
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge == true }}
|
||||||
with:
|
with:
|
||||||
pr_number: ${{ needs.get-pr.outputs.pr_number }}
|
pr_number: ${{ needs.get-pr.outputs.pr_number }}
|
||||||
|
user: 'github-actions[bot]'
|
||||||
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
||||||
# Text to prepend to the changelog
|
# Text to prepend to the changelog
|
||||||
# The first line must be `## Unreleased`
|
# The first line must be `## Unreleased`
|
||||||
@@ -65,15 +68,14 @@ jobs:
|
|||||||
secrets:
|
secrets:
|
||||||
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
|
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
|
||||||
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
||||||
RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
|
|
||||||
RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
create-prerelease:
|
create-prerelease:
|
||||||
uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
|
uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v2
|
||||||
needs: get-pr
|
needs: get-pr
|
||||||
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge != true }}
|
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge != true }}
|
||||||
with:
|
with:
|
||||||
pr_number: ${{ needs.get-pr.outputs.pr_number }}
|
pr_number: ${{ needs.get-pr.outputs.pr_number }}
|
||||||
|
user: 'github-actions[bot]'
|
||||||
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
||||||
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
||||||
indent: ${{ vars.INDENT }}
|
indent: ${{ vars.INDENT }}
|
||||||
@@ -82,10 +84,11 @@ jobs:
|
|||||||
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
create-target:
|
create-target:
|
||||||
uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v1
|
uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v2
|
||||||
needs: get-pr
|
needs: get-pr
|
||||||
if: ${{ needs.get-pr.outputs.pr_number == '' }}
|
if: ${{ needs.get-pr.outputs.pr_number == '' }}
|
||||||
with:
|
with:
|
||||||
|
user: 'github-actions[bot]'
|
||||||
# The script for version increment.
|
# The script for version increment.
|
||||||
# process.env.CURRENT_VERSION: The current version.
|
# process.env.CURRENT_VERSION: The current version.
|
||||||
#
|
#
|
||||||
@@ -118,8 +121,7 @@ jobs:
|
|||||||
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
||||||
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
||||||
indent: ${{ vars.INDENT }}
|
indent: ${{ vars.INDENT }}
|
||||||
|
stable_branch: ${{ vars.STABLE_BRANCH }}
|
||||||
secrets:
|
secrets:
|
||||||
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
|
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
|
||||||
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
||||||
RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
|
|
||||||
RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
|
|
||||||
|
|||||||
15
.github/workflows/release-with-ready.yml
vendored
15
.github/workflows/release-with-ready.yml
vendored
@@ -16,21 +16,26 @@ jobs:
|
|||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
ref: ${{ steps.get_pr.outputs.ref }}
|
head: ${{ steps.get_pr.outputs.head }}
|
||||||
|
base: ${{ steps.get_pr.outputs.base }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# PR情報を取得
|
# PR情報を取得
|
||||||
- name: Get PR
|
- name: Get PR
|
||||||
run: |
|
run: |
|
||||||
pr_json=$(gh pr view ${{ github.event.pull_request.number }} --json isDraft,headRefName)
|
pr_json=$(gh pr view "$PR_NUMBER" --json isDraft,headRefName,baseRefName)
|
||||||
echo "ref=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT
|
echo "head=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT
|
||||||
|
echo "base=$(echo $pr_json | jq -r '.baseRefName')" >> $GITHUB_OUTPUT
|
||||||
id: get_pr
|
id: get_pr
|
||||||
|
env:
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
release:
|
release:
|
||||||
uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
|
uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v2
|
||||||
needs: check
|
needs: check
|
||||||
if: startsWith(needs.check.outputs.ref, 'release/')
|
if: needs.check.outputs.head == github.event.repository.default_branch && needs.check.outputs.base == vars.STABLE_BRANCH
|
||||||
with:
|
with:
|
||||||
pr_number: ${{ github.event.pull_request.number }}
|
pr_number: ${{ github.event.pull_request.number }}
|
||||||
|
user: 'github-actions[bot]'
|
||||||
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
||||||
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
||||||
indent: ${{ vars.INDENT }}
|
indent: ${{ vars.INDENT }}
|
||||||
|
|||||||
4
.github/workflows/storybook.yml
vendored
4
.github/workflows/storybook.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
- name: Use Node.js 20.x
|
- name: Use Node.js 20.x
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
if [ "$BRANCH" = "misskey-dev:$HEAD_REF" ]; then
|
if [ "$BRANCH" = "misskey-dev:$HEAD_REF" ]; then
|
||||||
BRANCH="$HEAD_REF"
|
BRANCH="$HEAD_REF"
|
||||||
fi
|
fi
|
||||||
pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name $BRANCH $(echo "$CHROMATIC_PARAMETER")
|
pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name "$BRANCH" $(echo "$CHROMATIC_PARAMETER")
|
||||||
env:
|
env:
|
||||||
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||||
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||||
|
|||||||
7
.github/workflows/test-backend.yml
vendored
7
.github/workflows/test-backend.yml
vendored
@@ -9,12 +9,13 @@ on:
|
|||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
# for permissions
|
# for permissions
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
|
- .github/workflows/test-backend.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
# for permissions
|
# for permissions
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
|
- .github/workflows/test-backend.yml
|
||||||
jobs:
|
jobs:
|
||||||
unit:
|
unit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -45,7 +46,7 @@ jobs:
|
|||||||
- name: Install FFmpeg
|
- name: Install FFmpeg
|
||||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -92,7 +93,7 @@ jobs:
|
|||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
8
.github/workflows/test-frontend.yml
vendored
8
.github/workflows/test-frontend.yml
vendored
@@ -11,7 +11,7 @@ on:
|
|||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
# for e2e
|
# for e2e
|
||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
|
- .github/workflows/test-frontend.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- packages/frontend/**
|
- packages/frontend/**
|
||||||
@@ -19,7 +19,7 @@ on:
|
|||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
# for e2e
|
# for e2e
|
||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
|
- .github/workflows/test-frontend.yml
|
||||||
jobs:
|
jobs:
|
||||||
vitest:
|
vitest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
5
.github/workflows/test-misskey-js.yml
vendored
5
.github/workflows/test-misskey-js.yml
vendored
@@ -8,11 +8,12 @@ on:
|
|||||||
branches: [ develop ]
|
branches: [ develop ]
|
||||||
paths:
|
paths:
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
|
- .github/workflows/test-misskey-js.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ develop ]
|
branches: [ develop ]
|
||||||
paths:
|
paths:
|
||||||
- packages/misskey-js/**
|
- packages/misskey-js/**
|
||||||
|
- .github/workflows/test-misskey-js.yml
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ jobs:
|
|||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
|
||||||
- name: Setup Node.js ${{ matrix.node-version }}
|
- name: Setup Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
2
.github/workflows/test-production.yml
vendored
2
.github/workflows/test-production.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
5
.github/workflows/validate-api-json.yml
vendored
5
.github/workflows/validate-api-json.yml
vendored
@@ -7,10 +7,11 @@ on:
|
|||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
|
- .github/workflows/validate-api-json.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- packages/backend/**
|
- packages/backend/**
|
||||||
|
- .github/workflows/validate-api-json.yml
|
||||||
jobs:
|
jobs:
|
||||||
validate-api-json:
|
validate-api-json:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -26,7 +27,7 @@ jobs:
|
|||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -35,8 +35,8 @@ coverage
|
|||||||
!/.config/example.yml
|
!/.config/example.yml
|
||||||
!/.config/docker_example.yml
|
!/.config/docker_example.yml
|
||||||
!/.config/docker_example.env
|
!/.config/docker_example.env
|
||||||
docker-compose.yml
|
.devcontainer/compose.yml
|
||||||
!/.devcontainer/docker-compose.yml
|
!/.devcontainer/compose.yml
|
||||||
|
|
||||||
# misskey
|
# misskey
|
||||||
/build
|
/build
|
||||||
|
|||||||
84
CHANGELOG.md
84
CHANGELOG.md
@@ -1,10 +1,82 @@
|
|||||||
|
## 2024.7.0
|
||||||
|
|
||||||
|
### Note
|
||||||
|
- デッキUIの新着ノートをサウンドで通知する機能の追加(v2024.5.0)に伴い、以前から動作しなくなっていたクライアント設定内の「アンテナ受信」「チャンネル通知」サウンドを削除しました。
|
||||||
|
|
||||||
|
### General
|
||||||
|
- Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
|
||||||
|
- Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
|
||||||
|
- 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
|
||||||
|
- Feat: 連合に使うHTTP SignaturesがEd25519鍵に対応するように #13464
|
||||||
|
- Ed25519署名に対応するサーバーが増えると、deliverで要求されるサーバーリソースが削減されます
|
||||||
|
- Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
|
||||||
|
- Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
|
||||||
|
- Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
|
||||||
|
|
||||||
|
### Client
|
||||||
|
- Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
|
||||||
|
- Enhance: 非ログイン時に他サーバーに遷移するアクションを追加
|
||||||
|
- Enhance: 非ログイン時のハイライトTLのデザインを改善
|
||||||
|
- Enhance: フロントエンドのアクセシビリティ改善
|
||||||
|
(Based on https://github.com/taiyme/misskey/pull/226)
|
||||||
|
- Enhance: サーバー情報ページ・お問い合わせページを改善
|
||||||
|
(Cherry-picked from https://github.com/taiyme/misskey/pull/238)
|
||||||
|
- Enhance: AiScriptを0.19.0にアップデート
|
||||||
|
- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
|
||||||
|
- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
|
||||||
|
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
|
||||||
|
- Fix: リバーシの対局を正しく共有できないことがある問題を修正
|
||||||
|
- Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正
|
||||||
|
- Fix: アンテナの編集画面のボタンに隙間を追加
|
||||||
|
- Fix: テーマプレビューが見れない問題を修正
|
||||||
|
- Fix: ショートカットキーが連打できる問題を修正
|
||||||
|
(Cherry-picked from https://github.com/taiyme/misskey/pull/234)
|
||||||
|
- Fix: MkSignin.vueのcredentialRequestからReactivityを削除(ProxyがPasskey認証処理に渡ることを避けるため)
|
||||||
|
|
||||||
|
### Server
|
||||||
|
- Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
|
||||||
|
- Enhance: エンドポイント`clips/update`の必須項目を`clipId`のみに
|
||||||
|
- Enhance: エンドポイント`admin/roles/update`の必須項目を`roleId`のみに
|
||||||
|
- Enhance: エンドポイント`pages/update`の必須項目を`pageId`のみに
|
||||||
|
- Enhance: エンドポイント`gallery/posts/update`の必須項目を`postId`のみに
|
||||||
|
- Enhance: エンドポイント`i/webhook/update`の必須項目を`webhookId`のみに
|
||||||
|
- Enhance: エンドポイント`admin/ad/update`の必須項目を`id`のみに
|
||||||
|
- Enhance: `default.yml`内の`url`, `db.db`, `db.user`, `db.pass`を環境変数から読み込めるように
|
||||||
|
- Fix: チャート生成時にinstance.suspensionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
|
||||||
|
- Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
|
||||||
|
- Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
|
||||||
|
- Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)
|
||||||
|
- Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正
|
||||||
|
- Fix: 自分以外のクリップ内のノート個数が見えることがあるのを修正
|
||||||
|
- Fix: 空文字列のリアクションはフォールバックされるように
|
||||||
|
- Fix: リノートにリアクションできないように
|
||||||
|
- Fix: ユーザー名の前後に空白文字列がある場合は省略するように
|
||||||
|
- Fix: プロフィール編集時に名前を空白文字列のみにできる問題を修正
|
||||||
|
- Fix: ユーザ名のサジェスト時に表示される内容と順番を調整(以下の順番になります) #14149
|
||||||
|
1. フォロー中かつアクティブなユーザ
|
||||||
|
2. フォロー中かつ非アクティブなユーザ
|
||||||
|
3. フォローしていないアクティブなユーザ
|
||||||
|
4. フォローしていない非アクティブなユーザ
|
||||||
|
|
||||||
|
また、自分自身のアカウントもサジェストされるようになりました。
|
||||||
|
- Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正
|
||||||
|
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
|
||||||
|
- Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正
|
||||||
|
- Fix: エラーメッセージの誤字を修正 (#14213)
|
||||||
|
|
||||||
|
### Misskey.js
|
||||||
|
- Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
|
||||||
|
- Feat: `/admin/role/create` のロールポリシーの型を修正
|
||||||
|
|
||||||
## 2024.5.0
|
## 2024.5.0
|
||||||
|
|
||||||
### Note
|
### Note
|
||||||
- コントロールパネル内にあるサマリープロキシの設定個所がセキュリティから全般へ変更となります。
|
- コントロールパネル内にあるサマリープロキシの設定個所がセキュリティから全般へ変更となります。
|
||||||
- 悪意のある第三者がリモートユーザーになりすましたアクティビティを受け取れてしまう問題を修正しました。詳しくは[GitHub security advisory](https://github.com/misskey-dev/misskey/security/advisories/GHSA-2vxv-pv3m-3wvj)をご覧ください。
|
- 悪意のある第三者がリモートユーザーになりすましたアクティビティを受け取れてしまう問題を修正しました。詳しくは[GitHub security advisory](https://github.com/misskey-dev/misskey/security/advisories/GHSA-2vxv-pv3m-3wvj)をご覧ください。
|
||||||
|
- 管理者向け権限 `read:admin:show-users` は `read:admin:show-user` に統合されました。必要に応じてAPIトークンを再発行してください。
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
- Feat: エラートラッキングにSentryを使用できるようになりました
|
||||||
- Enhance: URLプレビューの有効化・無効化を設定できるように #13569
|
- Enhance: URLプレビューの有効化・無効化を設定できるように #13569
|
||||||
- Enhance: アンテナでBotによるノートを除外できるように
|
- Enhance: アンテナでBotによるノートを除外できるように
|
||||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
|
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
|
||||||
@@ -15,12 +87,18 @@
|
|||||||
- サスペンド済みユーザーか
|
- サスペンド済みユーザーか
|
||||||
- 鍵アカウントユーザーか
|
- 鍵アカウントユーザーか
|
||||||
- 「アカウントを見つけやすくする」が有効なユーザーか
|
- 「アカウントを見つけやすくする」が有効なユーザーか
|
||||||
|
- Enhance: Goneを出さずに終了したサーバーへの配信停止を自動的に行うように
|
||||||
|
- もしそのようなサーバーからから配信が届いた場合には自動的に配信を再開します
|
||||||
|
- Enhance: 配信停止の理由を表示するように
|
||||||
|
- Enhance: サーバーのお問い合わせ先URLを設定できるようになりました
|
||||||
- Fix: Play作成時に設定した公開範囲が機能していない問題を修正
|
- Fix: Play作成時に設定した公開範囲が機能していない問題を修正
|
||||||
- Fix: 正規化されていない状態のhashtagが連合されてきたhtmlに含まれているとhashtagが正しくhashtagに復元されない問題を修正
|
- Fix: 正規化されていない状態のhashtagが連合されてきたhtmlに含まれているとhashtagが正しくhashtagに復元されない問題を修正
|
||||||
- Fix: みつけるのアンケート欄にてチャンネルのアンケートが含まれてしまう問題を修正
|
- Fix: みつけるのアンケート欄にてチャンネルのアンケートが含まれてしまう問題を修正
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Feat: アップロードするファイルの名前をランダム文字列にできるように
|
- Feat: アップロードするファイルの名前をランダム文字列にできるように
|
||||||
|
- Feat: 個別のお知らせにリンクで飛べるように
|
||||||
|
(Based on https://github.com/MisskeyIO/misskey/pull/639)
|
||||||
- Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
|
- Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
|
||||||
- Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように
|
- Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように
|
||||||
- Enhance: リアクション・いいねの総数を表示するように
|
- Enhance: リアクション・いいねの総数を表示するように
|
||||||
@@ -43,6 +121,9 @@
|
|||||||
- Enhance: AiScriptを0.18.0にバージョンアップ
|
- Enhance: AiScriptを0.18.0にバージョンアップ
|
||||||
- Enhance: 通常のノートでも、お気に入りに登録したチャンネルにリノートできるように
|
- Enhance: 通常のノートでも、お気に入りに登録したチャンネルにリノートできるように
|
||||||
- Enhance: 長いテキストをペーストした際にテキストファイルとして添付するかどうかを選択できるように
|
- Enhance: 長いテキストをペーストした際にテキストファイルとして添付するかどうかを選択できるように
|
||||||
|
- Enhance: 新着ノートをサウンドで通知する機能をdeck UIに追加しました
|
||||||
|
- Enhance: コントロールパネルのクイックアクションからファイルを照会できるように
|
||||||
|
- Enhance: コントロールパネルのクイックアクションから通常の照会を行えるように
|
||||||
- Fix: 一部のページ内リンクが正しく動作しない問題を修正
|
- Fix: 一部のページ内リンクが正しく動作しない問題を修正
|
||||||
- Fix: 周年の実績が閏年を考慮しない問題を修正
|
- Fix: 周年の実績が閏年を考慮しない問題を修正
|
||||||
- Fix: ローカルURLのプレビューポップアップが左上に表示される
|
- Fix: ローカルURLのプレビューポップアップが左上に表示される
|
||||||
@@ -63,6 +144,7 @@
|
|||||||
- Fix: 通知をグループ化している際に、人数が正常に表示されないことがある問題を修正
|
- Fix: 通知をグループ化している際に、人数が正常に表示されないことがある問題を修正
|
||||||
- Fix: 連合なしの状態の読み書きができない問題を修正
|
- Fix: 連合なしの状態の読み書きができない問題を修正
|
||||||
- Fix: `/share` で日本語等を含むurlがurlエンコードされない問題を修正
|
- Fix: `/share` で日本語等を含むurlがurlエンコードされない問題を修正
|
||||||
|
- Fix: ファイルを5つ以上添付してもテキストがないとノートが折りたたまれない問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
|
- Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
|
||||||
@@ -88,6 +170,8 @@
|
|||||||
- Fix: `/i/notifications`に `includeTypes`か`excludeTypes`を指定しているとき、通知が存在するのに空配列を返すことがある問題を修正
|
- Fix: `/i/notifications`に `includeTypes`か`excludeTypes`を指定しているとき、通知が存在するのに空配列を返すことがある問題を修正
|
||||||
- Fix: 複数idを指定する`users/show`が関係ないユーザを返すことがある問題を修正
|
- Fix: 複数idを指定する`users/show`が関係ないユーザを返すことがある問題を修正
|
||||||
- Fix: `/tags` と `/user-tags` が検索エンジンにインデックスされないように
|
- Fix: `/tags` と `/user-tags` が検索エンジンにインデックスされないように
|
||||||
|
- Fix: もともとセンシティブではないと連合されていたファイルがセンシティブとして連合された場合にセンシティブとしてそのファイルを扱うように
|
||||||
|
- センシティブとして連合したファイルは非センシティブとして連合されてもセンシティブとして扱われます
|
||||||
|
|
||||||
## 2024.3.1
|
## 2024.3.1
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ cp .github/misskey/test.yml .config/
|
|||||||
```
|
```
|
||||||
Prepare DB/Redis for testing.
|
Prepare DB/Redis for testing.
|
||||||
```
|
```
|
||||||
docker compose -f packages/backend/test/docker-compose.yml up
|
docker compose -f packages/backend/test/compose.yml up
|
||||||
```
|
```
|
||||||
Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`.
|
Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`.
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ TODO
|
|||||||
## Environment Variable
|
## Environment Variable
|
||||||
|
|
||||||
- `MISSKEY_CONFIG_YML`: Specify the file path of config.yml instead of default.yml (e.g. `2nd.yml`).
|
- `MISSKEY_CONFIG_YML`: Specify the file path of config.yml instead of default.yml (e.g. `2nd.yml`).
|
||||||
- `MISSKEY_WEBFINGER_USE_HTTP`: If it's set true, WebFinger requests will be http instead of https, useful for testing federation between servers in localhost. NEVER USE IN PRODUCTION.
|
- `MISSKEY_USE_HTTP`: If it's set true, federation requests (like nodeinfo and webfinger) will be http instead of https, useful for testing federation between servers in localhost. NEVER USE IN PRODUCTION. (was `MISSKEY_WEBFINGER_USE_HTTP`)
|
||||||
|
|
||||||
## Continuous integration
|
## Continuous integration
|
||||||
Misskey uses GitHub Actions for executing automated tests.
|
Misskey uses GitHub Actions for executing automated tests.
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ RUN apt-get update \
|
|||||||
USER misskey
|
USER misskey
|
||||||
WORKDIR /misskey
|
WORKDIR /misskey
|
||||||
|
|
||||||
|
# add package.json to add pnpm
|
||||||
|
COPY --chown=misskey:misskey ./package.json ./package.json
|
||||||
|
RUN corepack install
|
||||||
|
|
||||||
COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules
|
COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules
|
||||||
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
|
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
|
||||||
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
|
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
|
||||||
|
|||||||
@@ -28,6 +28,10 @@
|
|||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
|
<a href="https://sentry.io/"><img src="https://github.com/misskey-dev/misskey/assets/4439005/98576556-222f-467a-94be-e98dbda1d852" height="30" alt="Sentry" /></a>
|
||||||
|
|
||||||
|
Thanks to [Sentry](https://sentry.io/) for providing the error tracking platform that helps us catch unexpected errors.
|
||||||
|
|
||||||
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" height="30" alt="Chromatic" /></a>
|
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" height="30" alt="Chromatic" /></a>
|
||||||
|
|
||||||
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
|
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
|
||||||
|
|||||||
@@ -152,6 +152,22 @@ redis:
|
|||||||
# ID SETTINGS AFTER THAT!
|
# ID SETTINGS AFTER THAT!
|
||||||
|
|
||||||
id: "aidx"
|
id: "aidx"
|
||||||
|
|
||||||
|
# ┌────────────────┐
|
||||||
|
#───┘ Error tracking └──────────────────────────────────────────
|
||||||
|
|
||||||
|
# Sentry is available for error tracking.
|
||||||
|
# See the Sentry documentation for more details on options.
|
||||||
|
|
||||||
|
#sentryForBackend:
|
||||||
|
# enableNodeProfiling: true
|
||||||
|
# options:
|
||||||
|
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||||
|
|
||||||
|
#sentryForFrontend:
|
||||||
|
# options:
|
||||||
|
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||||
|
|
||||||
# ┌─────────────────────┐
|
# ┌─────────────────────┐
|
||||||
#───┘ Other configuration └─────────────────────────────────────
|
#───┘ Other configuration └─────────────────────────────────────
|
||||||
|
|
||||||
@@ -162,12 +178,12 @@ id: "aidx"
|
|||||||
#clusterLimit: 1
|
#clusterLimit: 1
|
||||||
|
|
||||||
# Job concurrency per worker
|
# Job concurrency per worker
|
||||||
# deliverJobConcurrency: 128
|
# deliverJobConcurrency: 16
|
||||||
# inboxJobConcurrency: 16
|
# inboxJobConcurrency: 4
|
||||||
|
|
||||||
# Job rate limiter
|
# Job rate limiter
|
||||||
# deliverJobPerSec: 128
|
# deliverJobPerSec: 1024
|
||||||
# inboxJobPerSec: 32
|
# inboxJobPerSec: 64
|
||||||
|
|
||||||
# Job attempts
|
# Job attempts
|
||||||
# deliverJobMaxAttempts: 12
|
# deliverJobMaxAttempts: 12
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
version: "3"
|
|
||||||
|
|
||||||
# このconfigは、 dockerでMisskey本体を起動せず、 redisとpostgresql などだけを起動します
|
# このconfigは、 dockerでMisskey本体を起動せず、 redisとpostgresql などだけを起動します
|
||||||
|
|
||||||
services:
|
services:
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
build: .
|
build: .
|
||||||
@@ -19,6 +17,8 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- internal_network
|
- internal_network
|
||||||
- external_network
|
- external_network
|
||||||
|
# env_file:
|
||||||
|
# - .config/docker.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./files:/misskey/files
|
- ./files:/misskey/files
|
||||||
- ./.config:/misskey/.config:ro
|
- ./.config:/misskey/.config:ro
|
||||||
@@ -4,4 +4,4 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
PORT=$(grep '^port:' /misskey/.config/default.yml | awk 'NR==1{print $2; exit}')
|
PORT=$(grep '^port:' /misskey/.config/default.yml | awk 'NR==1{print $2; exit}')
|
||||||
curl -s -S -o /dev/null "http://localhost:${PORT}"
|
curl -Sfso/dev/null "http://localhost:${PORT}/healthz"
|
||||||
|
|||||||
@@ -1016,6 +1016,8 @@ sourceCode: "الشفرة المصدرية"
|
|||||||
flip: "اقلب"
|
flip: "اقلب"
|
||||||
lastNDays: "آخر {n} أيام"
|
lastNDays: "آخر {n} أيام"
|
||||||
surrender: "ألغِ"
|
surrender: "ألغِ"
|
||||||
|
_delivery:
|
||||||
|
stop: "مُعلّق"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "نجح إنشاء حسابك!"
|
accountCreated: "نجح إنشاء حسابك!"
|
||||||
letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي."
|
letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي."
|
||||||
|
|||||||
@@ -857,6 +857,10 @@ replies: "জবাব"
|
|||||||
renotes: "রিনোট"
|
renotes: "রিনোট"
|
||||||
sourceCode: "সোর্স কোড"
|
sourceCode: "সোর্স কোড"
|
||||||
flip: "উল্টান"
|
flip: "উল্টান"
|
||||||
|
_delivery:
|
||||||
|
stop: "স্থগিত করা হয়েছে"
|
||||||
|
_type:
|
||||||
|
none: "প্রকাশ করা হচ্ছে"
|
||||||
_role:
|
_role:
|
||||||
priority: "অগ্রাধিকার"
|
priority: "অগ্রাধিকার"
|
||||||
_priority:
|
_priority:
|
||||||
|
|||||||
@@ -1224,6 +1224,10 @@ gameRetry: "Torna a provar"
|
|||||||
notUsePleaseLeaveBlank: "Si no voleu usar-ho, deixeu-ho en blanc"
|
notUsePleaseLeaveBlank: "Si no voleu usar-ho, deixeu-ho en blanc"
|
||||||
useTotp: "Usa una contrasenya d'un sol ús"
|
useTotp: "Usa una contrasenya d'un sol ús"
|
||||||
useBackupCode: "Usa un codi de recuperació"
|
useBackupCode: "Usa un codi de recuperació"
|
||||||
|
_delivery:
|
||||||
|
stop: "Suspés"
|
||||||
|
_type:
|
||||||
|
none: "S'està publicant"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "Com es juga"
|
howToPlay: "Com es juga"
|
||||||
_howToPlay:
|
_howToPlay:
|
||||||
@@ -2001,7 +2005,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "Veure informació del servidor"
|
"read:admin:server-info": "Veure informació del servidor"
|
||||||
"read:admin:show-moderation-log": "Veure registre de moderació "
|
"read:admin:show-moderation-log": "Veure registre de moderació "
|
||||||
"read:admin:show-user": "Veure informació privada de l'usuari "
|
"read:admin:show-user": "Veure informació privada de l'usuari "
|
||||||
"read:admin:show-users": "Veure informació privada de l'usuari "
|
|
||||||
"write:admin:suspend-user": "Suspendre usuari"
|
"write:admin:suspend-user": "Suspendre usuari"
|
||||||
"write:admin:unset-user-avatar": "Esborrar avatar d'usuari "
|
"write:admin:unset-user-avatar": "Esborrar avatar d'usuari "
|
||||||
"write:admin:unset-user-banner": "Esborrar bàner de l'usuari "
|
"write:admin:unset-user-banner": "Esborrar bàner de l'usuari "
|
||||||
|
|||||||
@@ -1099,6 +1099,10 @@ sourceCode: "Zdrojový kód"
|
|||||||
flip: "Otočit"
|
flip: "Otočit"
|
||||||
lastNDays: "Posledních {n} dnů"
|
lastNDays: "Posledních {n} dnů"
|
||||||
surrender: "Zrušit"
|
surrender: "Zrušit"
|
||||||
|
_delivery:
|
||||||
|
stop: "Suspendováno"
|
||||||
|
_type:
|
||||||
|
none: "Publikuji"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "Váš účet byl úspěšně vytvořen!"
|
accountCreated: "Váš účet byl úspěšně vytvořen!"
|
||||||
letsStartAccountSetup: "Pro začátek si nastavte svůj profil."
|
letsStartAccountSetup: "Pro začátek si nastavte svůj profil."
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
---
|
---
|
||||||
_lang_: "Dansk"
|
_lang_: "Dansk"
|
||||||
|
headlineMisskey: ""
|
||||||
|
introMisskey: "ようこそ!Misskeyは、オープンソースの分散型マイクロブログサービスです。\n「ノート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡\n「リアクション」機能で、皆のノートに素早く反応を追加することもできます👍\n新しい世界を探検しよう🚀"
|
||||||
|
|||||||
@@ -1185,6 +1185,10 @@ addMfmFunction: "MFM hinzufügen"
|
|||||||
sfx: "Soundeffekte"
|
sfx: "Soundeffekte"
|
||||||
lastNDays: "Letzten {n} Tage"
|
lastNDays: "Letzten {n} Tage"
|
||||||
surrender: "Abbrechen"
|
surrender: "Abbrechen"
|
||||||
|
_delivery:
|
||||||
|
stop: "Gesperrt"
|
||||||
|
_type:
|
||||||
|
none: "Wird veröffentlicht"
|
||||||
_announcement:
|
_announcement:
|
||||||
forExistingUsers: "Nur für existierende Nutzer"
|
forExistingUsers: "Nur für existierende Nutzer"
|
||||||
forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
|
forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
|
||||||
|
|||||||
@@ -108,11 +108,14 @@ enterEmoji: "Enter an emoji"
|
|||||||
renote: "Renote"
|
renote: "Renote"
|
||||||
unrenote: "Remove renote"
|
unrenote: "Remove renote"
|
||||||
renoted: "Renoted."
|
renoted: "Renoted."
|
||||||
|
renotedToX: "Renote from {name} users。"
|
||||||
cantRenote: "This post can't be renoted."
|
cantRenote: "This post can't be renoted."
|
||||||
cantReRenote: "A renote can't be renoted."
|
cantReRenote: "A renote can't be renoted."
|
||||||
quote: "Quote"
|
quote: "Quote"
|
||||||
inChannelRenote: "Channel-only Renote"
|
inChannelRenote: "Channel-only Renote"
|
||||||
inChannelQuote: "Channel-only Quote"
|
inChannelQuote: "Channel-only Quote"
|
||||||
|
renoteToChannel: "Renote to channel"
|
||||||
|
renoteToOtherChannel: "Renote to other channel"
|
||||||
pinnedNote: "Pinned note"
|
pinnedNote: "Pinned note"
|
||||||
pinned: "Pin to profile"
|
pinned: "Pin to profile"
|
||||||
you: "You"
|
you: "You"
|
||||||
@@ -468,6 +471,7 @@ retype: "Enter again"
|
|||||||
noteOf: "Note by {user}"
|
noteOf: "Note by {user}"
|
||||||
quoteAttached: "Quote"
|
quoteAttached: "Quote"
|
||||||
quoteQuestion: "Append as quote?"
|
quoteQuestion: "Append as quote?"
|
||||||
|
attachAsFileQuestion: "The text in clipboard is long. Would you want to attach it as text file?"
|
||||||
noMessagesYet: "No messages yet"
|
noMessagesYet: "No messages yet"
|
||||||
newMessageExists: "There are new messages"
|
newMessageExists: "There are new messages"
|
||||||
onlyOneFileCanBeAttached: "You can only attach one file to a message"
|
onlyOneFileCanBeAttached: "You can only attach one file to a message"
|
||||||
@@ -1235,6 +1239,15 @@ keepOriginalFilenameDescription: "If you turn off this setting, files names will
|
|||||||
noDescription: "There is not the explanation"
|
noDescription: "There is not the explanation"
|
||||||
alwaysConfirmFollow: "Always confirm when following"
|
alwaysConfirmFollow: "Always confirm when following"
|
||||||
inquiry: "Contact"
|
inquiry: "Contact"
|
||||||
|
_delivery:
|
||||||
|
status: "Delivery status"
|
||||||
|
stop: "Suspended"
|
||||||
|
resume: "Delivery resume"
|
||||||
|
_type:
|
||||||
|
none: "Publishing"
|
||||||
|
manuallySuspended: "Manually suspended"
|
||||||
|
goneSuspended: "Server is suspended due to server deletion"
|
||||||
|
autoSuspendedForNotResponding: "Server is suspended due to no responding"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "How to play"
|
howToPlay: "How to play"
|
||||||
hold: "Hold"
|
hold: "Hold"
|
||||||
@@ -2032,7 +2045,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "View server info"
|
"read:admin:server-info": "View server info"
|
||||||
"read:admin:show-moderation-log": "View moderation log"
|
"read:admin:show-moderation-log": "View moderation log"
|
||||||
"read:admin:show-user": "View private user info"
|
"read:admin:show-user": "View private user info"
|
||||||
"read:admin:show-users": "View private user info"
|
|
||||||
"write:admin:suspend-user": "Suspend user"
|
"write:admin:suspend-user": "Suspend user"
|
||||||
"write:admin:unset-user-avatar": "Remove user avatar"
|
"write:admin:unset-user-avatar": "Remove user avatar"
|
||||||
"write:admin:unset-user-banner": "Remove user banner"
|
"write:admin:unset-user-banner": "Remove user banner"
|
||||||
|
|||||||
@@ -1233,6 +1233,10 @@ useNativeUIForVideoAudioPlayer: "Usar la interfaz del navegador cuando se reprod
|
|||||||
keepOriginalFilename: "Mantener el nombre original del archivo"
|
keepOriginalFilename: "Mantener el nombre original del archivo"
|
||||||
noDescription: "No hay descripción"
|
noDescription: "No hay descripción"
|
||||||
alwaysConfirmFollow: "Confirmar siempre cuando se sigue a alguien"
|
alwaysConfirmFollow: "Confirmar siempre cuando se sigue a alguien"
|
||||||
|
_delivery:
|
||||||
|
stop: "Suspendido"
|
||||||
|
_type:
|
||||||
|
none: "Publicando"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "Cómo jugar"
|
howToPlay: "Cómo jugar"
|
||||||
hold: "Mantener"
|
hold: "Mantener"
|
||||||
@@ -2029,7 +2033,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "Ver información del servidor"
|
"read:admin:server-info": "Ver información del servidor"
|
||||||
"read:admin:show-moderation-log": "Ver log de moderación"
|
"read:admin:show-moderation-log": "Ver log de moderación"
|
||||||
"read:admin:show-user": "Ver información privada de usuario"
|
"read:admin:show-user": "Ver información privada de usuario"
|
||||||
"read:admin:show-users": "Ver información privada de usuario"
|
|
||||||
"write:admin:suspend-user": "Suspender cuentas de usuario"
|
"write:admin:suspend-user": "Suspender cuentas de usuario"
|
||||||
"write:admin:unset-user-avatar": "Quitar avatares de usuario"
|
"write:admin:unset-user-avatar": "Quitar avatares de usuario"
|
||||||
"write:admin:unset-user-banner": "Quitar banner de usuarios"
|
"write:admin:unset-user-banner": "Quitar banner de usuarios"
|
||||||
|
|||||||
@@ -1224,6 +1224,10 @@ enableHorizontalSwipe: "Glisser pour changer d'onglet"
|
|||||||
loading: "Chargement en cours"
|
loading: "Chargement en cours"
|
||||||
surrender: "Annuler"
|
surrender: "Annuler"
|
||||||
gameRetry: "Réessayer"
|
gameRetry: "Réessayer"
|
||||||
|
_delivery:
|
||||||
|
stop: "Suspendu·e"
|
||||||
|
_type:
|
||||||
|
none: "Publié"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "Comment jouer"
|
howToPlay: "Comment jouer"
|
||||||
hold: "Réserver"
|
hold: "Réserver"
|
||||||
|
|||||||
@@ -108,11 +108,14 @@ enterEmoji: "Masukkan emoji"
|
|||||||
renote: "Renote"
|
renote: "Renote"
|
||||||
unrenote: "Hapus renote"
|
unrenote: "Hapus renote"
|
||||||
renoted: "Telah direnote"
|
renoted: "Telah direnote"
|
||||||
|
renotedToX: "{name} telah merenote"
|
||||||
cantRenote: "Postingan ini tidak dapat direnote"
|
cantRenote: "Postingan ini tidak dapat direnote"
|
||||||
cantReRenote: "Renote tidak dapat direnote"
|
cantReRenote: "Renote tidak dapat direnote"
|
||||||
quote: "Kutip"
|
quote: "Kutip"
|
||||||
inChannelRenote: "Hanya renote dalam kanal"
|
inChannelRenote: "Hanya renote dalam kanal"
|
||||||
inChannelQuote: "Hanya kutip dalam kanal"
|
inChannelQuote: "Hanya kutip dalam kanal"
|
||||||
|
renoteToChannel: "Renote ke kanal"
|
||||||
|
renoteToOtherChannel: "Renote ke kanal lainnya"
|
||||||
pinnedNote: "Catatan yang disematkan"
|
pinnedNote: "Catatan yang disematkan"
|
||||||
pinned: "Sematkan ke profil"
|
pinned: "Sematkan ke profil"
|
||||||
you: "Kamu"
|
you: "Kamu"
|
||||||
@@ -468,6 +471,7 @@ retype: "Masukkan ulang"
|
|||||||
noteOf: "Catatan milik {user}"
|
noteOf: "Catatan milik {user}"
|
||||||
quoteAttached: "Dikutip"
|
quoteAttached: "Dikutip"
|
||||||
quoteQuestion: "Apakah kamu ingin menambahkan kutipan?"
|
quoteQuestion: "Apakah kamu ingin menambahkan kutipan?"
|
||||||
|
attachAsFileQuestion: "Teks dalam papan klip terlalu panjang. Apakah kamu ingin melampirkannya sebagai berkas teks?"
|
||||||
noMessagesYet: "Tidak ada pesan"
|
noMessagesYet: "Tidak ada pesan"
|
||||||
newMessageExists: "Kamu mendapatkan pesan baru"
|
newMessageExists: "Kamu mendapatkan pesan baru"
|
||||||
onlyOneFileCanBeAttached: "Kamu hanya dapat melampirkan satu berkas ke dalam pesan"
|
onlyOneFileCanBeAttached: "Kamu hanya dapat melampirkan satu berkas ke dalam pesan"
|
||||||
@@ -1235,6 +1239,15 @@ keepOriginalFilenameDescription: "Apabila pengaturan ini dimatikan, nama berkas
|
|||||||
noDescription: "Tidak ada deskripsi"
|
noDescription: "Tidak ada deskripsi"
|
||||||
alwaysConfirmFollow: "Selalu konfirmasi ketika mengikuti"
|
alwaysConfirmFollow: "Selalu konfirmasi ketika mengikuti"
|
||||||
inquiry: "Hubungi kami"
|
inquiry: "Hubungi kami"
|
||||||
|
_delivery:
|
||||||
|
status: "Status pengiriman"
|
||||||
|
stop: "Ditangguhkan"
|
||||||
|
resume: "Lanjutkan pengiriman"
|
||||||
|
_type:
|
||||||
|
none: "Sedang menyiarkan langsung"
|
||||||
|
manuallySuspended: "Ditangguhkan manual"
|
||||||
|
goneSuspended: "Sedang ditangguhkan untuk penghapusan peladen"
|
||||||
|
autoSuspendedForNotResponding: "Sedang ditangguhkan karena peladen tidak menjawab"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "Cara bermain"
|
howToPlay: "Cara bermain"
|
||||||
hold: "Tahan"
|
hold: "Tahan"
|
||||||
@@ -2032,7 +2045,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "Lihat informasi peladen"
|
"read:admin:server-info": "Lihat informasi peladen"
|
||||||
"read:admin:show-moderation-log": "Lihat log moderasi"
|
"read:admin:show-moderation-log": "Lihat log moderasi"
|
||||||
"read:admin:show-user": "Lihat informasi pengguna privat"
|
"read:admin:show-user": "Lihat informasi pengguna privat"
|
||||||
"read:admin:show-users": "Lihat informasi pengguna privat"
|
|
||||||
"write:admin:suspend-user": "Tangguhkan pengguna"
|
"write:admin:suspend-user": "Tangguhkan pengguna"
|
||||||
"write:admin:unset-user-avatar": "Hapus avatar pengguna"
|
"write:admin:unset-user-avatar": "Hapus avatar pengguna"
|
||||||
"write:admin:unset-user-banner": "Hapus banner pengguna"
|
"write:admin:unset-user-banner": "Hapus banner pengguna"
|
||||||
|
|||||||
202
locales/index.d.ts
vendored
202
locales/index.d.ts
vendored
@@ -736,6 +736,22 @@ export interface Locale extends ILocale {
|
|||||||
* リモートで表示
|
* リモートで表示
|
||||||
*/
|
*/
|
||||||
"showOnRemote": string;
|
"showOnRemote": string;
|
||||||
|
/**
|
||||||
|
* リモートで続行
|
||||||
|
*/
|
||||||
|
"continueOnRemote": string;
|
||||||
|
/**
|
||||||
|
* Misskey Hubからサーバーを選択
|
||||||
|
*/
|
||||||
|
"chooseServerOnMisskeyHub": string;
|
||||||
|
/**
|
||||||
|
* サーバーのドメインを直接指定
|
||||||
|
*/
|
||||||
|
"specifyServerHost": string;
|
||||||
|
/**
|
||||||
|
* ドメインを入力してください
|
||||||
|
*/
|
||||||
|
"inputHostName": string;
|
||||||
/**
|
/**
|
||||||
* 全般
|
* 全般
|
||||||
*/
|
*/
|
||||||
@@ -1280,6 +1296,10 @@ export interface Locale extends ILocale {
|
|||||||
* フォルダーを選択
|
* フォルダーを選択
|
||||||
*/
|
*/
|
||||||
"selectFolders": string;
|
"selectFolders": string;
|
||||||
|
/**
|
||||||
|
* ファイルが選択されていません
|
||||||
|
*/
|
||||||
|
"fileNotSelected": string;
|
||||||
/**
|
/**
|
||||||
* ファイル名を変更
|
* ファイル名を変更
|
||||||
*/
|
*/
|
||||||
@@ -1917,9 +1937,13 @@ export interface Locale extends ILocale {
|
|||||||
*/
|
*/
|
||||||
"onlyOneFileCanBeAttached": string;
|
"onlyOneFileCanBeAttached": string;
|
||||||
/**
|
/**
|
||||||
* 続行する前に、サインアップまたはサインインが必要です
|
* 続行する前に、登録またはログインが必要です
|
||||||
*/
|
*/
|
||||||
"signinRequired": string;
|
"signinRequired": string;
|
||||||
|
/**
|
||||||
|
* 続行するには、お使いのサーバーに移動するか、このサーバーに登録・ログインする必要があります
|
||||||
|
*/
|
||||||
|
"signinOrContinueOnRemote": string;
|
||||||
/**
|
/**
|
||||||
* 招待
|
* 招待
|
||||||
*/
|
*/
|
||||||
@@ -3360,6 +3384,10 @@ export interface Locale extends ILocale {
|
|||||||
* 管理者情報が設定されていません。
|
* 管理者情報が設定されていません。
|
||||||
*/
|
*/
|
||||||
"noMaintainerInformationWarning": string;
|
"noMaintainerInformationWarning": string;
|
||||||
|
/**
|
||||||
|
* 問い合わせ先URLが設定されていません。
|
||||||
|
*/
|
||||||
|
"noInquiryUrlWarning": string;
|
||||||
/**
|
/**
|
||||||
* Botプロテクションが設定されていません。
|
* Botプロテクションが設定されていません。
|
||||||
*/
|
*/
|
||||||
@@ -4113,9 +4141,13 @@ export interface Locale extends ILocale {
|
|||||||
*/
|
*/
|
||||||
"thisPostMayBeAnnoyingIgnore": string;
|
"thisPostMayBeAnnoyingIgnore": string;
|
||||||
/**
|
/**
|
||||||
* 見たことのあるリノートを省略して表示
|
* リノートのスマート省略
|
||||||
*/
|
*/
|
||||||
"collapseRenotes": string;
|
"collapseRenotes": string;
|
||||||
|
/**
|
||||||
|
* リアクションやリノートをしたことがあるノートをたたんで表示します。
|
||||||
|
*/
|
||||||
|
"collapseRenotesDescription": string;
|
||||||
/**
|
/**
|
||||||
* サーバー内部エラー
|
* サーバー内部エラー
|
||||||
*/
|
*/
|
||||||
@@ -4972,6 +5004,42 @@ export interface Locale extends ILocale {
|
|||||||
* お問い合わせ
|
* お問い合わせ
|
||||||
*/
|
*/
|
||||||
"inquiry": string;
|
"inquiry": string;
|
||||||
|
/**
|
||||||
|
* もう一度お試しください。
|
||||||
|
*/
|
||||||
|
"tryAgain": string;
|
||||||
|
"_delivery": {
|
||||||
|
/**
|
||||||
|
* 配信状態
|
||||||
|
*/
|
||||||
|
"status": string;
|
||||||
|
/**
|
||||||
|
* 配信停止
|
||||||
|
*/
|
||||||
|
"stop": string;
|
||||||
|
/**
|
||||||
|
* 配信再開
|
||||||
|
*/
|
||||||
|
"resume": string;
|
||||||
|
"_type": {
|
||||||
|
/**
|
||||||
|
* 配信中
|
||||||
|
*/
|
||||||
|
"none": string;
|
||||||
|
/**
|
||||||
|
* 手動停止中
|
||||||
|
*/
|
||||||
|
"manuallySuspended": string;
|
||||||
|
/**
|
||||||
|
* サーバー削除のため停止中
|
||||||
|
*/
|
||||||
|
"goneSuspended": string;
|
||||||
|
/**
|
||||||
|
* サーバー応答なしのため停止中
|
||||||
|
*/
|
||||||
|
"autoSuspendedForNotResponding": string;
|
||||||
|
};
|
||||||
|
};
|
||||||
"_bubbleGame": {
|
"_bubbleGame": {
|
||||||
/**
|
/**
|
||||||
* 遊び方
|
* 遊び方
|
||||||
@@ -5431,6 +5499,14 @@ export interface Locale extends ILocale {
|
|||||||
* 有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。
|
* 有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。
|
||||||
*/
|
*/
|
||||||
"fanoutTimelineDbFallbackDescription": string;
|
"fanoutTimelineDbFallbackDescription": string;
|
||||||
|
/**
|
||||||
|
* 問い合わせ先URL
|
||||||
|
*/
|
||||||
|
"inquiryUrl": string;
|
||||||
|
/**
|
||||||
|
* サーバー運営者へのお問い合わせフォームのURLや、運営者の連絡先等が記載されたWebページのURLを指定します。
|
||||||
|
*/
|
||||||
|
"inquiryUrlDescription": string;
|
||||||
};
|
};
|
||||||
"_accountMigration": {
|
"_accountMigration": {
|
||||||
/**
|
/**
|
||||||
@@ -6542,6 +6618,10 @@ export interface Locale extends ILocale {
|
|||||||
* ファイルにNSFWを常に付与
|
* ファイルにNSFWを常に付与
|
||||||
*/
|
*/
|
||||||
"alwaysMarkNsfw": string;
|
"alwaysMarkNsfw": string;
|
||||||
|
/**
|
||||||
|
* アイコンとバナーの更新を許可
|
||||||
|
*/
|
||||||
|
"canUpdateBioMedia": string;
|
||||||
/**
|
/**
|
||||||
* ノートのピン留めの最大数
|
* ノートのピン留めの最大数
|
||||||
*/
|
*/
|
||||||
@@ -7463,14 +7543,6 @@ export interface Locale extends ILocale {
|
|||||||
* 通知
|
* 通知
|
||||||
*/
|
*/
|
||||||
"notification": string;
|
"notification": string;
|
||||||
/**
|
|
||||||
* アンテナ受信
|
|
||||||
*/
|
|
||||||
"antenna": string;
|
|
||||||
/**
|
|
||||||
* チャンネル通知
|
|
||||||
*/
|
|
||||||
"channel": string;
|
|
||||||
/**
|
/**
|
||||||
* リアクション選択時
|
* リアクション選択時
|
||||||
*/
|
*/
|
||||||
@@ -7903,10 +7975,6 @@ export interface Locale extends ILocale {
|
|||||||
* ユーザーのプライベートな情報を見る
|
* ユーザーのプライベートな情報を見る
|
||||||
*/
|
*/
|
||||||
"read:admin:show-user": string;
|
"read:admin:show-user": string;
|
||||||
/**
|
|
||||||
* ユーザーのプライベートな情報を見る
|
|
||||||
*/
|
|
||||||
"read:admin:show-users": string;
|
|
||||||
/**
|
/**
|
||||||
* ユーザーを凍結する
|
* ユーザーを凍結する
|
||||||
*/
|
*/
|
||||||
@@ -9111,6 +9179,10 @@ export interface Locale extends ILocale {
|
|||||||
* カラムを追加
|
* カラムを追加
|
||||||
*/
|
*/
|
||||||
"addColumn": string;
|
"addColumn": string;
|
||||||
|
/**
|
||||||
|
* 新着ノート通知の設定
|
||||||
|
*/
|
||||||
|
"newNoteNotificationSettings": string;
|
||||||
/**
|
/**
|
||||||
* カラムの設定
|
* カラムの設定
|
||||||
*/
|
*/
|
||||||
@@ -9253,6 +9325,10 @@ export interface Locale extends ILocale {
|
|||||||
* Webhookを作成
|
* Webhookを作成
|
||||||
*/
|
*/
|
||||||
"createWebhook": string;
|
"createWebhook": string;
|
||||||
|
/**
|
||||||
|
* Webhookを編集
|
||||||
|
*/
|
||||||
|
"modifyWebhook": string;
|
||||||
/**
|
/**
|
||||||
* 名前
|
* 名前
|
||||||
*/
|
*/
|
||||||
@@ -9299,6 +9375,72 @@ export interface Locale extends ILocale {
|
|||||||
*/
|
*/
|
||||||
"mention": string;
|
"mention": string;
|
||||||
};
|
};
|
||||||
|
"_systemEvents": {
|
||||||
|
/**
|
||||||
|
* ユーザーから通報があったとき
|
||||||
|
*/
|
||||||
|
"abuseReport": string;
|
||||||
|
/**
|
||||||
|
* ユーザーからの通報を処理したとき
|
||||||
|
*/
|
||||||
|
"abuseReportResolved": string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Webhookを削除しますか?
|
||||||
|
*/
|
||||||
|
"deleteConfirm": string;
|
||||||
|
};
|
||||||
|
"_abuseReport": {
|
||||||
|
"_notificationRecipient": {
|
||||||
|
/**
|
||||||
|
* 通報の通知先を追加
|
||||||
|
*/
|
||||||
|
"createRecipient": string;
|
||||||
|
/**
|
||||||
|
* 通報の通知先を編集
|
||||||
|
*/
|
||||||
|
"modifyRecipient": string;
|
||||||
|
/**
|
||||||
|
* 通知先の種類
|
||||||
|
*/
|
||||||
|
"recipientType": string;
|
||||||
|
"_recipientType": {
|
||||||
|
/**
|
||||||
|
* メール
|
||||||
|
*/
|
||||||
|
"mail": string;
|
||||||
|
/**
|
||||||
|
* Webhook
|
||||||
|
*/
|
||||||
|
"webhook": string;
|
||||||
|
"_captions": {
|
||||||
|
/**
|
||||||
|
* モデレーター権限を持つユーザーのメールアドレスに通知を送ります(通報を受けた時のみ)
|
||||||
|
*/
|
||||||
|
"mail": string;
|
||||||
|
/**
|
||||||
|
* 指定したSystemWebhookに通知を送ります(通報を受けた時と通報を解決した時にそれぞれ発信)
|
||||||
|
*/
|
||||||
|
"webhook": string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* キーワード
|
||||||
|
*/
|
||||||
|
"keywords": string;
|
||||||
|
/**
|
||||||
|
* 通知先ユーザー
|
||||||
|
*/
|
||||||
|
"notifiedUser": string;
|
||||||
|
/**
|
||||||
|
* 使用するWebhook
|
||||||
|
*/
|
||||||
|
"notifiedWebhook": string;
|
||||||
|
/**
|
||||||
|
* 通知先を削除しますか?
|
||||||
|
*/
|
||||||
|
"deleteConfirm": string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
"_moderationLogTypes": {
|
"_moderationLogTypes": {
|
||||||
/**
|
/**
|
||||||
@@ -9445,6 +9587,30 @@ export interface Locale extends ILocale {
|
|||||||
* ユーザーのバナーを解除
|
* ユーザーのバナーを解除
|
||||||
*/
|
*/
|
||||||
"unsetUserBanner": string;
|
"unsetUserBanner": string;
|
||||||
|
/**
|
||||||
|
* SystemWebhookを作成
|
||||||
|
*/
|
||||||
|
"createSystemWebhook": string;
|
||||||
|
/**
|
||||||
|
* SystemWebhookを更新
|
||||||
|
*/
|
||||||
|
"updateSystemWebhook": string;
|
||||||
|
/**
|
||||||
|
* SystemWebhookを削除
|
||||||
|
*/
|
||||||
|
"deleteSystemWebhook": string;
|
||||||
|
/**
|
||||||
|
* 通報の通知先を作成
|
||||||
|
*/
|
||||||
|
"createAbuseReportNotificationRecipient": string;
|
||||||
|
/**
|
||||||
|
* 通報の通知先を更新
|
||||||
|
*/
|
||||||
|
"updateAbuseReportNotificationRecipient": string;
|
||||||
|
/**
|
||||||
|
* 通報の通知先を削除
|
||||||
|
*/
|
||||||
|
"deleteAbuseReportNotificationRecipient": string;
|
||||||
};
|
};
|
||||||
"_fileViewer": {
|
"_fileViewer": {
|
||||||
/**
|
/**
|
||||||
@@ -9615,7 +9781,7 @@ export interface Locale extends ILocale {
|
|||||||
"_dataSaver": {
|
"_dataSaver": {
|
||||||
"_media": {
|
"_media": {
|
||||||
/**
|
/**
|
||||||
* メディアの読み込み
|
* メディアの読み込みを無効化
|
||||||
*/
|
*/
|
||||||
"title": string;
|
"title": string;
|
||||||
/**
|
/**
|
||||||
@@ -9625,7 +9791,7 @@ export interface Locale extends ILocale {
|
|||||||
};
|
};
|
||||||
"_avatar": {
|
"_avatar": {
|
||||||
/**
|
/**
|
||||||
* アイコン画像
|
* アイコン画像のアニメーションを無効化
|
||||||
*/
|
*/
|
||||||
"title": string;
|
"title": string;
|
||||||
/**
|
/**
|
||||||
@@ -9635,7 +9801,7 @@ export interface Locale extends ILocale {
|
|||||||
};
|
};
|
||||||
"_urlPreview": {
|
"_urlPreview": {
|
||||||
/**
|
/**
|
||||||
* URLプレビューのサムネイル
|
* URLプレビューのサムネイルを非表示
|
||||||
*/
|
*/
|
||||||
"title": string;
|
"title": string;
|
||||||
/**
|
/**
|
||||||
@@ -9645,7 +9811,7 @@ export interface Locale extends ILocale {
|
|||||||
};
|
};
|
||||||
"_code": {
|
"_code": {
|
||||||
/**
|
/**
|
||||||
* コードハイライト
|
* コードハイライトを非表示
|
||||||
*/
|
*/
|
||||||
"title": string;
|
"title": string;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -52,7 +52,11 @@ const primaries = {
|
|||||||
const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '');
|
const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '');
|
||||||
|
|
||||||
export function build() {
|
export function build() {
|
||||||
const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(new URL(`${c}.yml`, import.meta.url), 'utf-8'))) || {}, a), {});
|
// vitestの挙動を調整するため、一度ローカル変数化する必要がある
|
||||||
|
// https://github.com/vitest-dev/vitest/issues/3988#issuecomment-1686599577
|
||||||
|
// https://github.com/misskey-dev/misskey/pull/14057#issuecomment-2192833785
|
||||||
|
const metaUrl = import.meta.url;
|
||||||
|
const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(new URL(`${c}.yml`, metaUrl), 'utf-8'))) || {}, a), {});
|
||||||
|
|
||||||
// 空文字列が入ることがあり、フォールバックが動作しなくなるのでプロパティごと消す
|
// 空文字列が入ることがあり、フォールバックが動作しなくなるのでプロパティごと消す
|
||||||
const removeEmpty = (obj) => {
|
const removeEmpty = (obj) => {
|
||||||
|
|||||||
@@ -1233,6 +1233,10 @@ useNativeUIForVideoAudioPlayer: "Riprodurre audio/video usando le funzionalità
|
|||||||
keepOriginalFilename: "Mantieni il nome file originale"
|
keepOriginalFilename: "Mantieni il nome file originale"
|
||||||
keepOriginalFilenameDescription: "Disattivandola, i file verranno caricati usando nomi casuali."
|
keepOriginalFilenameDescription: "Disattivandola, i file verranno caricati usando nomi casuali."
|
||||||
noDescription: "Manca la descrizione"
|
noDescription: "Manca la descrizione"
|
||||||
|
_delivery:
|
||||||
|
stop: "Sospensione"
|
||||||
|
_type:
|
||||||
|
none: "Pubblicazione"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "Come giocare"
|
howToPlay: "Come giocare"
|
||||||
hold: "Tieni"
|
hold: "Tieni"
|
||||||
@@ -2025,7 +2029,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "Vedere le informazioni sul server"
|
"read:admin:server-info": "Vedere le informazioni sul server"
|
||||||
"read:admin:show-moderation-log": "Vedere lo storico di moderazione"
|
"read:admin:show-moderation-log": "Vedere lo storico di moderazione"
|
||||||
"read:admin:show-user": "Vedere le informazioni private degli account utente"
|
"read:admin:show-user": "Vedere le informazioni private degli account utente"
|
||||||
"read:admin:show-users": "Vedere le informazioni private degli account utente"
|
|
||||||
"write:admin:suspend-user": "Sospendere i profili"
|
"write:admin:suspend-user": "Sospendere i profili"
|
||||||
"write:admin:unset-user-avatar": "Rimuovere la foto profilo dai profili"
|
"write:admin:unset-user-avatar": "Rimuovere la foto profilo dai profili"
|
||||||
"write:admin:unset-user-banner": "Rimuovere l'immagine testata dai profili"
|
"write:admin:unset-user-banner": "Rimuovere l'immagine testata dai profili"
|
||||||
|
|||||||
@@ -180,6 +180,10 @@ addAccount: "アカウントを追加"
|
|||||||
reloadAccountsList: "アカウントリストの情報を更新"
|
reloadAccountsList: "アカウントリストの情報を更新"
|
||||||
loginFailed: "ログインに失敗しました"
|
loginFailed: "ログインに失敗しました"
|
||||||
showOnRemote: "リモートで表示"
|
showOnRemote: "リモートで表示"
|
||||||
|
continueOnRemote: "リモートで続行"
|
||||||
|
chooseServerOnMisskeyHub: "Misskey Hubからサーバーを選択"
|
||||||
|
specifyServerHost: "サーバーのドメインを直接指定"
|
||||||
|
inputHostName: "ドメインを入力してください"
|
||||||
general: "全般"
|
general: "全般"
|
||||||
wallpaper: "壁紙"
|
wallpaper: "壁紙"
|
||||||
setWallpaper: "壁紙を設定"
|
setWallpaper: "壁紙を設定"
|
||||||
@@ -316,6 +320,7 @@ selectFile: "ファイルを選択"
|
|||||||
selectFiles: "ファイルを選択"
|
selectFiles: "ファイルを選択"
|
||||||
selectFolder: "フォルダーを選択"
|
selectFolder: "フォルダーを選択"
|
||||||
selectFolders: "フォルダーを選択"
|
selectFolders: "フォルダーを選択"
|
||||||
|
fileNotSelected: "ファイルが選択されていません"
|
||||||
renameFile: "ファイル名を変更"
|
renameFile: "ファイル名を変更"
|
||||||
folderName: "フォルダー名"
|
folderName: "フォルダー名"
|
||||||
createFolder: "フォルダーを作成"
|
createFolder: "フォルダーを作成"
|
||||||
@@ -475,7 +480,8 @@ attachAsFileQuestion: "クリップボードのテキストが長いです。テ
|
|||||||
noMessagesYet: "まだチャットはありません"
|
noMessagesYet: "まだチャットはありません"
|
||||||
newMessageExists: "新しいメッセージがあります"
|
newMessageExists: "新しいメッセージがあります"
|
||||||
onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです"
|
onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです"
|
||||||
signinRequired: "続行する前に、サインアップまたはサインインが必要です"
|
signinRequired: "続行する前に、登録またはログインが必要です"
|
||||||
|
signinOrContinueOnRemote: "続行するには、お使いのサーバーに移動するか、このサーバーに登録・ログインする必要があります"
|
||||||
invitations: "招待"
|
invitations: "招待"
|
||||||
invitationCode: "招待コード"
|
invitationCode: "招待コード"
|
||||||
checking: "確認しています"
|
checking: "確認しています"
|
||||||
@@ -836,6 +842,7 @@ administration: "管理"
|
|||||||
accounts: "アカウント"
|
accounts: "アカウント"
|
||||||
switch: "切り替え"
|
switch: "切り替え"
|
||||||
noMaintainerInformationWarning: "管理者情報が設定されていません。"
|
noMaintainerInformationWarning: "管理者情報が設定されていません。"
|
||||||
|
noInquiryUrlWarning: "問い合わせ先URLが設定されていません。"
|
||||||
noBotProtectionWarning: "Botプロテクションが設定されていません。"
|
noBotProtectionWarning: "Botプロテクションが設定されていません。"
|
||||||
configure: "設定する"
|
configure: "設定する"
|
||||||
postToGallery: "ギャラリーへ投稿"
|
postToGallery: "ギャラリーへ投稿"
|
||||||
@@ -1024,7 +1031,8 @@ thisPostMayBeAnnoying: "この投稿は迷惑になる可能性があります
|
|||||||
thisPostMayBeAnnoyingHome: "ホームに投稿"
|
thisPostMayBeAnnoyingHome: "ホームに投稿"
|
||||||
thisPostMayBeAnnoyingCancel: "やめる"
|
thisPostMayBeAnnoyingCancel: "やめる"
|
||||||
thisPostMayBeAnnoyingIgnore: "このまま投稿"
|
thisPostMayBeAnnoyingIgnore: "このまま投稿"
|
||||||
collapseRenotes: "見たことのあるリノートを省略して表示"
|
collapseRenotes: "リノートのスマート省略"
|
||||||
|
collapseRenotesDescription: "リアクションやリノートをしたことがあるノートをたたんで表示します。"
|
||||||
internalServerError: "サーバー内部エラー"
|
internalServerError: "サーバー内部エラー"
|
||||||
internalServerErrorDescription: "サーバー内部で予期しないエラーが発生しました。"
|
internalServerErrorDescription: "サーバー内部で予期しないエラーが発生しました。"
|
||||||
copyErrorInfo: "エラー情報をコピー"
|
copyErrorInfo: "エラー情報をコピー"
|
||||||
@@ -1239,6 +1247,17 @@ keepOriginalFilenameDescription: "この設定をオフにすると、アップ
|
|||||||
noDescription: "説明文はありません"
|
noDescription: "説明文はありません"
|
||||||
alwaysConfirmFollow: "フォローの際常に確認する"
|
alwaysConfirmFollow: "フォローの際常に確認する"
|
||||||
inquiry: "お問い合わせ"
|
inquiry: "お問い合わせ"
|
||||||
|
tryAgain: "もう一度お試しください。"
|
||||||
|
|
||||||
|
_delivery:
|
||||||
|
status: "配信状態"
|
||||||
|
stop: "配信停止"
|
||||||
|
resume: "配信再開"
|
||||||
|
_type:
|
||||||
|
none: "配信中"
|
||||||
|
manuallySuspended: "手動停止中"
|
||||||
|
goneSuspended: "サーバー削除のため停止中"
|
||||||
|
autoSuspendedForNotResponding: "サーバー応答なしのため停止中"
|
||||||
|
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "遊び方"
|
howToPlay: "遊び方"
|
||||||
@@ -1371,6 +1390,8 @@ _serverSettings:
|
|||||||
fanoutTimelineDescription: "有効にすると、各種タイムラインを取得する際のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。サーバーのメモリ容量が少ない場合、または動作が不安定な場合は無効にすることができます。"
|
fanoutTimelineDescription: "有効にすると、各種タイムラインを取得する際のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。サーバーのメモリ容量が少ない場合、または動作が不安定な場合は無効にすることができます。"
|
||||||
fanoutTimelineDbFallback: "データベースへのフォールバック"
|
fanoutTimelineDbFallback: "データベースへのフォールバック"
|
||||||
fanoutTimelineDbFallbackDescription: "有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。"
|
fanoutTimelineDbFallbackDescription: "有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。"
|
||||||
|
inquiryUrl: "問い合わせ先URL"
|
||||||
|
inquiryUrlDescription: "サーバー運営者へのお問い合わせフォームのURLや、運営者の連絡先等が記載されたWebページのURLを指定します。"
|
||||||
|
|
||||||
_accountMigration:
|
_accountMigration:
|
||||||
moveFrom: "別のアカウントからこのアカウントに移行"
|
moveFrom: "別のアカウントからこのアカウントに移行"
|
||||||
@@ -1690,6 +1711,7 @@ _role:
|
|||||||
canManageAvatarDecorations: "アバターデコレーションの管理"
|
canManageAvatarDecorations: "アバターデコレーションの管理"
|
||||||
driveCapacity: "ドライブ容量"
|
driveCapacity: "ドライブ容量"
|
||||||
alwaysMarkNsfw: "ファイルにNSFWを常に付与"
|
alwaysMarkNsfw: "ファイルにNSFWを常に付与"
|
||||||
|
canUpdateBioMedia: "アイコンとバナーの更新を許可"
|
||||||
pinMax: "ノートのピン留めの最大数"
|
pinMax: "ノートのピン留めの最大数"
|
||||||
antennaMax: "アンテナの作成可能数"
|
antennaMax: "アンテナの作成可能数"
|
||||||
wordMuteMax: "ワードミュートの最大文字数"
|
wordMuteMax: "ワードミュートの最大文字数"
|
||||||
@@ -1956,8 +1978,6 @@ _sfx:
|
|||||||
note: "ノート"
|
note: "ノート"
|
||||||
noteMy: "ノート(自分)"
|
noteMy: "ノート(自分)"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
antenna: "アンテナ受信"
|
|
||||||
channel: "チャンネル通知"
|
|
||||||
reaction: "リアクション選択時"
|
reaction: "リアクション選択時"
|
||||||
|
|
||||||
_soundSettings:
|
_soundSettings:
|
||||||
@@ -2075,7 +2095,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "サーバーの情報を見る"
|
"read:admin:server-info": "サーバーの情報を見る"
|
||||||
"read:admin:show-moderation-log": "モデレーションログを見る"
|
"read:admin:show-moderation-log": "モデレーションログを見る"
|
||||||
"read:admin:show-user": "ユーザーのプライベートな情報を見る"
|
"read:admin:show-user": "ユーザーのプライベートな情報を見る"
|
||||||
"read:admin:show-users": "ユーザーのプライベートな情報を見る"
|
|
||||||
"write:admin:suspend-user": "ユーザーを凍結する"
|
"write:admin:suspend-user": "ユーザーを凍結する"
|
||||||
"write:admin:unset-user-avatar": "ユーザーのアバターを削除する"
|
"write:admin:unset-user-avatar": "ユーザーのアバターを削除する"
|
||||||
"write:admin:unset-user-banner": "ユーザーのバーナーを削除する"
|
"write:admin:unset-user-banner": "ユーザーのバーナーを削除する"
|
||||||
@@ -2410,6 +2429,7 @@ _deck:
|
|||||||
alwaysShowMainColumn: "常にメインカラムを表示"
|
alwaysShowMainColumn: "常にメインカラムを表示"
|
||||||
columnAlign: "カラムの寄せ"
|
columnAlign: "カラムの寄せ"
|
||||||
addColumn: "カラムを追加"
|
addColumn: "カラムを追加"
|
||||||
|
newNoteNotificationSettings: "新着ノート通知の設定"
|
||||||
configureColumn: "カラムの設定"
|
configureColumn: "カラムの設定"
|
||||||
swapLeft: "左に移動"
|
swapLeft: "左に移動"
|
||||||
swapRight: "右に移動"
|
swapRight: "右に移動"
|
||||||
@@ -2453,6 +2473,7 @@ _drivecleaner:
|
|||||||
|
|
||||||
_webhookSettings:
|
_webhookSettings:
|
||||||
createWebhook: "Webhookを作成"
|
createWebhook: "Webhookを作成"
|
||||||
|
modifyWebhook: "Webhookを編集"
|
||||||
name: "名前"
|
name: "名前"
|
||||||
secret: "シークレット"
|
secret: "シークレット"
|
||||||
events: "Webhookを実行するタイミング"
|
events: "Webhookを実行するタイミング"
|
||||||
@@ -2465,6 +2486,26 @@ _webhookSettings:
|
|||||||
renote: "Renoteされたとき"
|
renote: "Renoteされたとき"
|
||||||
reaction: "リアクションがあったとき"
|
reaction: "リアクションがあったとき"
|
||||||
mention: "メンションされたとき"
|
mention: "メンションされたとき"
|
||||||
|
_systemEvents:
|
||||||
|
abuseReport: "ユーザーから通報があったとき"
|
||||||
|
abuseReportResolved: "ユーザーからの通報を処理したとき"
|
||||||
|
deleteConfirm: "Webhookを削除しますか?"
|
||||||
|
|
||||||
|
_abuseReport:
|
||||||
|
_notificationRecipient:
|
||||||
|
createRecipient: "通報の通知先を追加"
|
||||||
|
modifyRecipient: "通報の通知先を編集"
|
||||||
|
recipientType: "通知先の種類"
|
||||||
|
_recipientType:
|
||||||
|
mail: "メール"
|
||||||
|
webhook: "Webhook"
|
||||||
|
_captions:
|
||||||
|
mail: "モデレーター権限を持つユーザーのメールアドレスに通知を送ります(通報を受けた時のみ)"
|
||||||
|
webhook: "指定したSystemWebhookに通知を送ります(通報を受けた時と通報を解決した時にそれぞれ発信)"
|
||||||
|
keywords: "キーワード"
|
||||||
|
notifiedUser: "通知先ユーザー"
|
||||||
|
notifiedWebhook: "使用するWebhook"
|
||||||
|
deleteConfirm: "通知先を削除しますか?"
|
||||||
|
|
||||||
_moderationLogTypes:
|
_moderationLogTypes:
|
||||||
createRole: "ロールを作成"
|
createRole: "ロールを作成"
|
||||||
@@ -2503,6 +2544,12 @@ _moderationLogTypes:
|
|||||||
deleteAvatarDecoration: "アイコンデコレーションを削除"
|
deleteAvatarDecoration: "アイコンデコレーションを削除"
|
||||||
unsetUserAvatar: "ユーザーのアイコンを解除"
|
unsetUserAvatar: "ユーザーのアイコンを解除"
|
||||||
unsetUserBanner: "ユーザーのバナーを解除"
|
unsetUserBanner: "ユーザーのバナーを解除"
|
||||||
|
createSystemWebhook: "SystemWebhookを作成"
|
||||||
|
updateSystemWebhook: "SystemWebhookを更新"
|
||||||
|
deleteSystemWebhook: "SystemWebhookを削除"
|
||||||
|
createAbuseReportNotificationRecipient: "通報の通知先を作成"
|
||||||
|
updateAbuseReportNotificationRecipient: "通報の通知先を更新"
|
||||||
|
deleteAbuseReportNotificationRecipient: "通報の通知先を削除"
|
||||||
|
|
||||||
_fileViewer:
|
_fileViewer:
|
||||||
title: "ファイルの詳細"
|
title: "ファイルの詳細"
|
||||||
@@ -2557,16 +2604,16 @@ _externalResourceInstaller:
|
|||||||
|
|
||||||
_dataSaver:
|
_dataSaver:
|
||||||
_media:
|
_media:
|
||||||
title: "メディアの読み込み"
|
title: "メディアの読み込みを無効化"
|
||||||
description: "画像・動画が自動で読み込まれるのを防止します。隠れている画像・動画はタップすると読み込まれます。"
|
description: "画像・動画が自動で読み込まれるのを防止します。隠れている画像・動画はタップすると読み込まれます。"
|
||||||
_avatar:
|
_avatar:
|
||||||
title: "アイコン画像"
|
title: "アイコン画像のアニメーションを無効化"
|
||||||
description: "アイコン画像のアニメーションが停止します。アニメーション画像は通常の画像よりファイルサイズが大きいことがあるので、データ通信量をさらに削減できます。"
|
description: "アイコン画像のアニメーションが停止します。アニメーション画像は通常の画像よりファイルサイズが大きいことがあるので、データ通信量をさらに削減できます。"
|
||||||
_urlPreview:
|
_urlPreview:
|
||||||
title: "URLプレビューのサムネイル"
|
title: "URLプレビューのサムネイルを非表示"
|
||||||
description: "URLプレビューのサムネイル画像が読み込まれなくなります。"
|
description: "URLプレビューのサムネイル画像が読み込まれなくなります。"
|
||||||
_code:
|
_code:
|
||||||
title: "コードハイライト"
|
title: "コードハイライトを非表示"
|
||||||
description: "MFMなどでコードハイライト記法が使われている場合、タップするまで読み込まれなくなります。コードハイライトではハイライトする言語ごとにその定義ファイルを読み込む必要がありますが、それらが自動で読み込まれなくなるため、通信量の削減が見込めます。"
|
description: "MFMなどでコードハイライト記法が使われている場合、タップするまで読み込まれなくなります。コードハイライトではハイライトする言語ごとにその定義ファイルを読み込む必要がありますが、それらが自動で読み込まれなくなるため、通信量の削減が見込めます。"
|
||||||
|
|
||||||
_hemisphere:
|
_hemisphere:
|
||||||
|
|||||||
@@ -1235,6 +1235,10 @@ keepOriginalFilenameDescription: "この設定をオフにすると、アップ
|
|||||||
noDescription: "説明文はあらへんで"
|
noDescription: "説明文はあらへんで"
|
||||||
alwaysConfirmFollow: "フォローの際常に確認する"
|
alwaysConfirmFollow: "フォローの際常に確認する"
|
||||||
inquiry: "問い合わせ"
|
inquiry: "問い合わせ"
|
||||||
|
_delivery:
|
||||||
|
stop: "配信せぇへん"
|
||||||
|
_type:
|
||||||
|
none: "配信しとる"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "遊び方"
|
howToPlay: "遊び方"
|
||||||
hold: "ホールド"
|
hold: "ホールド"
|
||||||
@@ -2032,7 +2036,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "サーバーの情報見る"
|
"read:admin:server-info": "サーバーの情報見る"
|
||||||
"read:admin:show-moderation-log": "モデレーションログ見る"
|
"read:admin:show-moderation-log": "モデレーションログ見る"
|
||||||
"read:admin:show-user": "ユーザーのプライベートな情報見る"
|
"read:admin:show-user": "ユーザーのプライベートな情報見る"
|
||||||
"read:admin:show-users": "ユーザーのプライベートな情報見る"
|
|
||||||
"write:admin:suspend-user": "ユーザーを凍結"
|
"write:admin:suspend-user": "ユーザーを凍結"
|
||||||
"write:admin:unset-user-avatar": "ユーザーのアバターを削除"
|
"write:admin:unset-user-avatar": "ユーザーのアバターを削除"
|
||||||
"write:admin:unset-user-banner": "ユーザーのバナーを削除"
|
"write:admin:unset-user-banner": "ユーザーのバナーを削除"
|
||||||
|
|||||||
@@ -649,6 +649,10 @@ replies: "답하기"
|
|||||||
renotes: "리노트"
|
renotes: "리노트"
|
||||||
attach: "옇기"
|
attach: "옇기"
|
||||||
surrender: "아이예"
|
surrender: "아이예"
|
||||||
|
_delivery:
|
||||||
|
stop: "고만 보내예"
|
||||||
|
_type:
|
||||||
|
none: "보내고 잇어예"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
startTutorial: "길라잡이 하기"
|
startTutorial: "길라잡이 하기"
|
||||||
_initialTutorial:
|
_initialTutorial:
|
||||||
|
|||||||
@@ -1230,6 +1230,10 @@ useTotp: "일회용 비밀번호 사용"
|
|||||||
useBackupCode: "백업 코드 사용"
|
useBackupCode: "백업 코드 사용"
|
||||||
launchApp: "앱 실행"
|
launchApp: "앱 실행"
|
||||||
useNativeUIForVideoAudioPlayer: "브라우저 UI에서 미디어 재생"
|
useNativeUIForVideoAudioPlayer: "브라우저 UI에서 미디어 재생"
|
||||||
|
_delivery:
|
||||||
|
stop: "정지됨"
|
||||||
|
_type:
|
||||||
|
none: "배포 중"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "설명"
|
howToPlay: "설명"
|
||||||
hold: "홀드"
|
hold: "홀드"
|
||||||
@@ -2021,7 +2025,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "서버 정보 보기"
|
"read:admin:server-info": "서버 정보 보기"
|
||||||
"read:admin:show-moderation-log": "조정 기록 보기"
|
"read:admin:show-moderation-log": "조정 기록 보기"
|
||||||
"read:admin:show-user": "사용자 개인정보 보기"
|
"read:admin:show-user": "사용자 개인정보 보기"
|
||||||
"read:admin:show-users": "사용자 개인정보 보기"
|
|
||||||
"write:admin:suspend-user": "사용자 정지하기"
|
"write:admin:suspend-user": "사용자 정지하기"
|
||||||
"write:admin:unset-user-avatar": "사용자 아바타 삭제하기"
|
"write:admin:unset-user-avatar": "사용자 아바타 삭제하기"
|
||||||
"write:admin:unset-user-banner": "사용자 배너 삭제하기"
|
"write:admin:unset-user-banner": "사용자 배너 삭제하기"
|
||||||
|
|||||||
@@ -395,6 +395,10 @@ searchByGoogle: "ຄົ້ນຫາ"
|
|||||||
file: "ໄຟລ໌"
|
file: "ໄຟລ໌"
|
||||||
replies: "ຕອບໄປທີ"
|
replies: "ຕອບໄປທີ"
|
||||||
renotes: "Renote"
|
renotes: "Renote"
|
||||||
|
_delivery:
|
||||||
|
stop: "ໂຈະ"
|
||||||
|
_type:
|
||||||
|
none: "ການພິມເຜີຍແຜ່"
|
||||||
_role:
|
_role:
|
||||||
_priority:
|
_priority:
|
||||||
middle: "ປານກາງ"
|
middle: "ປານກາງ"
|
||||||
|
|||||||
@@ -429,6 +429,10 @@ loggedInAsBot: "Momenteel als bot ingelogd"
|
|||||||
icon: "Avatar"
|
icon: "Avatar"
|
||||||
replies: "Antwoord"
|
replies: "Antwoord"
|
||||||
renotes: "Herdelen"
|
renotes: "Herdelen"
|
||||||
|
_delivery:
|
||||||
|
stop: "Opgeschort"
|
||||||
|
_type:
|
||||||
|
none: "Publiceren"
|
||||||
_email:
|
_email:
|
||||||
_follow:
|
_follow:
|
||||||
title: "volgde jou"
|
title: "volgde jou"
|
||||||
|
|||||||
@@ -464,6 +464,8 @@ icon: "Avatar"
|
|||||||
replies: "Svar"
|
replies: "Svar"
|
||||||
renotes: "Renote"
|
renotes: "Renote"
|
||||||
surrender: "Avbryt"
|
surrender: "Avbryt"
|
||||||
|
_delivery:
|
||||||
|
stop: "Suspendert"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
theseSettingsCanEditLater: "Du kan endre disse innstillingene senere."
|
theseSettingsCanEditLater: "Du kan endre disse innstillingene senere."
|
||||||
_achievements:
|
_achievements:
|
||||||
|
|||||||
@@ -1023,6 +1023,10 @@ flip: "Odwróć"
|
|||||||
lastNDays: "W ciągu ostatnich {n} dni"
|
lastNDays: "W ciągu ostatnich {n} dni"
|
||||||
surrender: "Odrzuć"
|
surrender: "Odrzuć"
|
||||||
gameRetry: "Spróbuj ponownie"
|
gameRetry: "Spróbuj ponownie"
|
||||||
|
_delivery:
|
||||||
|
stop: "Zawieszono"
|
||||||
|
_type:
|
||||||
|
none: "Publikowanie"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
_score:
|
_score:
|
||||||
score: "Wynik"
|
score: "Wynik"
|
||||||
|
|||||||
@@ -1012,6 +1012,10 @@ keepScreenOn: "Manter a tela do dispositivo sempre ligada"
|
|||||||
flip: "Inversão"
|
flip: "Inversão"
|
||||||
lastNDays: "Últimos {n} dias"
|
lastNDays: "Últimos {n} dias"
|
||||||
surrender: "Cancelar"
|
surrender: "Cancelar"
|
||||||
|
_delivery:
|
||||||
|
stop: "Suspenso"
|
||||||
|
_type:
|
||||||
|
none: "Publicando"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo."
|
followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo."
|
||||||
_serverSettings:
|
_serverSettings:
|
||||||
|
|||||||
@@ -651,6 +651,10 @@ show: "Arată"
|
|||||||
icon: "Avatar"
|
icon: "Avatar"
|
||||||
replies: "Răspunde"
|
replies: "Răspunde"
|
||||||
renotes: "Re-notează"
|
renotes: "Re-notează"
|
||||||
|
_delivery:
|
||||||
|
stop: "Suspendat"
|
||||||
|
_type:
|
||||||
|
none: "Publicare"
|
||||||
_role:
|
_role:
|
||||||
_priority:
|
_priority:
|
||||||
middle: "Mediu"
|
middle: "Mediu"
|
||||||
|
|||||||
@@ -1099,6 +1099,10 @@ flip: "Переворот"
|
|||||||
code: "Код"
|
code: "Код"
|
||||||
lastNDays: "Последние {n} сут"
|
lastNDays: "Последние {n} сут"
|
||||||
surrender: "Этот пост не может быть отменен."
|
surrender: "Этот пост не может быть отменен."
|
||||||
|
_delivery:
|
||||||
|
stop: "Заморожено"
|
||||||
|
_type:
|
||||||
|
none: "Публикация"
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "Аккаунт успешно создан!"
|
accountCreated: "Аккаунт успешно создан!"
|
||||||
letsStartAccountSetup: "Давайте настроим вашу учётную запись."
|
letsStartAccountSetup: "Давайте настроим вашу учётную запись."
|
||||||
|
|||||||
@@ -922,6 +922,10 @@ renotes: "Preposlať"
|
|||||||
sourceCode: "Zdrojový kód"
|
sourceCode: "Zdrojový kód"
|
||||||
flip: "Preklopiť"
|
flip: "Preklopiť"
|
||||||
lastNDays: "Posledných {n} dní"
|
lastNDays: "Posledných {n} dní"
|
||||||
|
_delivery:
|
||||||
|
stop: "Zmrazené"
|
||||||
|
_type:
|
||||||
|
none: "Zverejňovanie"
|
||||||
_role:
|
_role:
|
||||||
priority: "Priorita"
|
priority: "Priorita"
|
||||||
_priority:
|
_priority:
|
||||||
|
|||||||
@@ -488,6 +488,10 @@ dataSaver: "Databesparing"
|
|||||||
icon: "Profilbild"
|
icon: "Profilbild"
|
||||||
replies: "Svara"
|
replies: "Svara"
|
||||||
renotes: "Omnotera"
|
renotes: "Omnotera"
|
||||||
|
_delivery:
|
||||||
|
stop: "Suspenderad"
|
||||||
|
_type:
|
||||||
|
none: "Publiceras"
|
||||||
_achievements:
|
_achievements:
|
||||||
_types:
|
_types:
|
||||||
_open3windows:
|
_open3windows:
|
||||||
|
|||||||
@@ -1235,6 +1235,10 @@ keepOriginalFilenameDescription: "หากปิดการตั้งค่
|
|||||||
noDescription: "ไม่มีข้อความอธิบาย"
|
noDescription: "ไม่มีข้อความอธิบาย"
|
||||||
alwaysConfirmFollow: "แสดงข้อความยืนยันเมื่อกดติดตาม"
|
alwaysConfirmFollow: "แสดงข้อความยืนยันเมื่อกดติดตาม"
|
||||||
inquiry: "ติดต่อเรา"
|
inquiry: "ติดต่อเรา"
|
||||||
|
_delivery:
|
||||||
|
stop: "ถูกระงับ"
|
||||||
|
_type:
|
||||||
|
none: "กำลังเผยแพร่"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "วิธีเล่น"
|
howToPlay: "วิธีเล่น"
|
||||||
hold: "หยุดชั่วคราว"
|
hold: "หยุดชั่วคราว"
|
||||||
@@ -2032,7 +2036,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "ดูข้อมูลเซิร์ฟเวอร์"
|
"read:admin:server-info": "ดูข้อมูลเซิร์ฟเวอร์"
|
||||||
"read:admin:show-moderation-log": "ดูปูมการแก้ไข"
|
"read:admin:show-moderation-log": "ดูปูมการแก้ไข"
|
||||||
"read:admin:show-user": "ดูข้อมูลส่วนตัวของผู้ใช้"
|
"read:admin:show-user": "ดูข้อมูลส่วนตัวของผู้ใช้"
|
||||||
"read:admin:show-users": "ดูข้อมูลส่วนตัวของผู้ใช้"
|
|
||||||
"write:admin:suspend-user": "ระงับผู้ใช้"
|
"write:admin:suspend-user": "ระงับผู้ใช้"
|
||||||
"write:admin:unset-user-avatar": "ลบอวตารผู้ใช้"
|
"write:admin:unset-user-avatar": "ลบอวตารผู้ใช้"
|
||||||
"write:admin:unset-user-banner": "ลบแบนเนอร์ผู้ใช้"
|
"write:admin:unset-user-banner": "ลบแบนเนอร์ผู้ใช้"
|
||||||
|
|||||||
@@ -378,6 +378,10 @@ addMemo: "Kısa not ekle"
|
|||||||
icon: "Avatar"
|
icon: "Avatar"
|
||||||
replies: "yanıt"
|
replies: "yanıt"
|
||||||
renotes: "vazgeçme"
|
renotes: "vazgeçme"
|
||||||
|
_delivery:
|
||||||
|
stop: "Askıya alınmış"
|
||||||
|
_type:
|
||||||
|
none: "Paylaşım"
|
||||||
_accountDelete:
|
_accountDelete:
|
||||||
started: "Silme işlemi başlatıldı"
|
started: "Silme işlemi başlatıldı"
|
||||||
_email:
|
_email:
|
||||||
|
|||||||
@@ -914,6 +914,10 @@ renotes: "Поширити"
|
|||||||
sourceCode: "Вихідний код"
|
sourceCode: "Вихідний код"
|
||||||
flip: "Перевернути"
|
flip: "Перевернути"
|
||||||
lastNDays: "Останні {n} днів"
|
lastNDays: "Останні {n} днів"
|
||||||
|
_delivery:
|
||||||
|
stop: "Призупинено"
|
||||||
|
_type:
|
||||||
|
none: "Публікація"
|
||||||
_achievements:
|
_achievements:
|
||||||
earnedAt: "Відкрито"
|
earnedAt: "Відкрито"
|
||||||
_types:
|
_types:
|
||||||
|
|||||||
@@ -846,6 +846,10 @@ icon: "Avatar"
|
|||||||
replies: "Javob berish"
|
replies: "Javob berish"
|
||||||
renotes: "Qayta qayd etish"
|
renotes: "Qayta qayd etish"
|
||||||
flip: "Teskari"
|
flip: "Teskari"
|
||||||
|
_delivery:
|
||||||
|
stop: "To'xtatilgan"
|
||||||
|
_type:
|
||||||
|
none: "Yuborilmoqda"
|
||||||
_achievements:
|
_achievements:
|
||||||
_types:
|
_types:
|
||||||
_viewInstanceChart:
|
_viewInstanceChart:
|
||||||
|
|||||||
@@ -1118,6 +1118,10 @@ pullDownToRefresh: "Kéo xuống để làm mới"
|
|||||||
cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải có chú thích."
|
cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải có chú thích."
|
||||||
lastNDays: "{n} ngày trước"
|
lastNDays: "{n} ngày trước"
|
||||||
surrender: "Từ chối"
|
surrender: "Từ chối"
|
||||||
|
_delivery:
|
||||||
|
stop: "Đã vô hiệu hóa"
|
||||||
|
_type:
|
||||||
|
none: "Đang đăng"
|
||||||
_announcement:
|
_announcement:
|
||||||
forExistingUsers: "Chỉ những người dùng đã tồn tại"
|
forExistingUsers: "Chỉ những người dùng đã tồn tại"
|
||||||
forExistingUsersDescription: "Nếu được bật, thông báo này sẽ chỉ hiển thị với những người dùng đã tồn tại vào lúc thông báo được tạo. Nếu tắt đi, những tài khoản mới đăng ký sau khi thông báo được đăng lên cũng sẽ thấy nó."
|
forExistingUsersDescription: "Nếu được bật, thông báo này sẽ chỉ hiển thị với những người dùng đã tồn tại vào lúc thông báo được tạo. Nếu tắt đi, những tài khoản mới đăng ký sau khi thông báo được đăng lên cũng sẽ thấy nó."
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ selectFile: "选择文件"
|
|||||||
selectFiles: "选择文件"
|
selectFiles: "选择文件"
|
||||||
selectFolder: "选择文件夹"
|
selectFolder: "选择文件夹"
|
||||||
selectFolders: "选择多个文件夹"
|
selectFolders: "选择多个文件夹"
|
||||||
|
fileNotSelected: "未选择文件"
|
||||||
renameFile: "重命名文件"
|
renameFile: "重命名文件"
|
||||||
folderName: "文件夹名称"
|
folderName: "文件夹名称"
|
||||||
createFolder: "创建文件夹"
|
createFolder: "创建文件夹"
|
||||||
@@ -471,6 +472,7 @@ retype: "重新输入"
|
|||||||
noteOf: "{user} 的帖子"
|
noteOf: "{user} 的帖子"
|
||||||
quoteAttached: "已引用"
|
quoteAttached: "已引用"
|
||||||
quoteQuestion: "是否引用此链接内容?"
|
quoteQuestion: "是否引用此链接内容?"
|
||||||
|
attachAsFileQuestion: "剪贴板内的文字过长。要转换为文本文件并添加吗?"
|
||||||
noMessagesYet: "现在没有新的聊天"
|
noMessagesYet: "现在没有新的聊天"
|
||||||
newMessageExists: "新信息"
|
newMessageExists: "新信息"
|
||||||
onlyOneFileCanBeAttached: "只能添加一个附件"
|
onlyOneFileCanBeAttached: "只能添加一个附件"
|
||||||
@@ -1024,6 +1026,7 @@ thisPostMayBeAnnoyingHome: "发到首页"
|
|||||||
thisPostMayBeAnnoyingCancel: "取消"
|
thisPostMayBeAnnoyingCancel: "取消"
|
||||||
thisPostMayBeAnnoyingIgnore: "就这样发布"
|
thisPostMayBeAnnoyingIgnore: "就这样发布"
|
||||||
collapseRenotes: "省略显示已经看过的转发内容"
|
collapseRenotes: "省略显示已经看过的转发内容"
|
||||||
|
collapseRenotesDescription: "将回应过或转贴过的贴子折叠表示。"
|
||||||
internalServerError: "内部服务器错误"
|
internalServerError: "内部服务器错误"
|
||||||
internalServerErrorDescription: "内部服务器发生了预期外的错误"
|
internalServerErrorDescription: "内部服务器发生了预期外的错误"
|
||||||
copyErrorInfo: "复制错误信息"
|
copyErrorInfo: "复制错误信息"
|
||||||
@@ -1238,6 +1241,15 @@ keepOriginalFilenameDescription: "若关闭此设置,上传文件时文件名
|
|||||||
noDescription: "没有描述"
|
noDescription: "没有描述"
|
||||||
alwaysConfirmFollow: "总是确认关注"
|
alwaysConfirmFollow: "总是确认关注"
|
||||||
inquiry: "联系我们"
|
inquiry: "联系我们"
|
||||||
|
_delivery:
|
||||||
|
status: "投递状态"
|
||||||
|
stop: "停止投递"
|
||||||
|
resume: "继续投递"
|
||||||
|
_type:
|
||||||
|
none: "投递中"
|
||||||
|
manuallySuspended: "手动停止中"
|
||||||
|
goneSuspended: "因服务器被删除而停止"
|
||||||
|
autoSuspendedForNotResponding: "因服务器无应答而停止"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "游戏说明"
|
howToPlay: "游戏说明"
|
||||||
hold: "抓住"
|
hold: "抓住"
|
||||||
@@ -1696,8 +1708,10 @@ _role:
|
|||||||
roleAssignedTo: "已分配给手动角色"
|
roleAssignedTo: "已分配给手动角色"
|
||||||
isLocal: "是本地用户"
|
isLocal: "是本地用户"
|
||||||
isRemote: "是远程用户"
|
isRemote: "是远程用户"
|
||||||
|
isCat: "猫猫用户"
|
||||||
isBot: "机器人用户"
|
isBot: "机器人用户"
|
||||||
isSuspended: "停用的用户"
|
isSuspended: "停用的用户"
|
||||||
|
isLocked: "锁推用户"
|
||||||
isExplorable: "启用“使账号可见”的用户"
|
isExplorable: "启用“使账号可见”的用户"
|
||||||
createdLessThan: "账户创建时间少于"
|
createdLessThan: "账户创建时间少于"
|
||||||
createdMoreThan: "账户创建时间超过"
|
createdMoreThan: "账户创建时间超过"
|
||||||
@@ -2032,7 +2046,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "查看服务器信息"
|
"read:admin:server-info": "查看服务器信息"
|
||||||
"read:admin:show-moderation-log": "查看管理日志"
|
"read:admin:show-moderation-log": "查看管理日志"
|
||||||
"read:admin:show-user": "查看用户的非公开信息"
|
"read:admin:show-user": "查看用户的非公开信息"
|
||||||
"read:admin:show-users": "查看用户的非公开信息"
|
|
||||||
"write:admin:suspend-user": "冻结用户"
|
"write:admin:suspend-user": "冻结用户"
|
||||||
"write:admin:unset-user-avatar": "删除用户头像"
|
"write:admin:unset-user-avatar": "删除用户头像"
|
||||||
"write:admin:unset-user-banner": "删除用户横幅"
|
"write:admin:unset-user-banner": "删除用户横幅"
|
||||||
@@ -2346,6 +2359,7 @@ _deck:
|
|||||||
alwaysShowMainColumn: "总是显示主列"
|
alwaysShowMainColumn: "总是显示主列"
|
||||||
columnAlign: "列对齐"
|
columnAlign: "列对齐"
|
||||||
addColumn: "添加列"
|
addColumn: "添加列"
|
||||||
|
newNoteNotificationSettings: "新帖子通知设定"
|
||||||
configureColumn: "列设置"
|
configureColumn: "列设置"
|
||||||
swapLeft: "向左移动"
|
swapLeft: "向左移动"
|
||||||
swapRight: "向右移动"
|
swapRight: "向右移动"
|
||||||
|
|||||||
@@ -108,11 +108,14 @@ enterEmoji: "輸入表情符號"
|
|||||||
renote: "轉發"
|
renote: "轉發"
|
||||||
unrenote: "取消轉發"
|
unrenote: "取消轉發"
|
||||||
renoted: "轉發成功。"
|
renoted: "轉發成功。"
|
||||||
|
renotedToX: "轉發給 {name} 了。"
|
||||||
cantRenote: "無法轉發此貼文。"
|
cantRenote: "無法轉發此貼文。"
|
||||||
cantReRenote: "無法轉發之前已經轉發過的內容。"
|
cantReRenote: "無法轉發之前已經轉發過的內容。"
|
||||||
quote: "引用"
|
quote: "引用"
|
||||||
inChannelRenote: "在頻道內轉發"
|
inChannelRenote: "在頻道內轉發"
|
||||||
inChannelQuote: "在頻道內引用"
|
inChannelQuote: "在頻道內引用"
|
||||||
|
renoteToChannel: "轉發至頻道"
|
||||||
|
renoteToOtherChannel: "轉發至其他頻道"
|
||||||
pinnedNote: "已置頂的貼文"
|
pinnedNote: "已置頂的貼文"
|
||||||
pinned: "置頂"
|
pinned: "置頂"
|
||||||
you: "您"
|
you: "您"
|
||||||
@@ -169,7 +172,7 @@ cacheRemoteSensitiveFilesDescription: "若停用這個設定,則不會快取
|
|||||||
flagAsBot: "此使用者是機器人"
|
flagAsBot: "此使用者是機器人"
|
||||||
flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整 Misskey 內部系統將本帳戶識別為機器人。"
|
flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整 Misskey 內部系統將本帳戶識別為機器人。"
|
||||||
flagAsCat: "此帳戶是一隻貓,喵~~~!!!"
|
flagAsCat: "此帳戶是一隻貓,喵~~~!!!"
|
||||||
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
|
flagAsCatDescription: "喵喵喵??"
|
||||||
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
|
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
|
||||||
flagShowTimelineRepliesDescription: "啟用後,時間軸除了顯示使用者的貼文以外,還會顯示使用者對其他貼文的回覆。"
|
flagShowTimelineRepliesDescription: "啟用後,時間軸除了顯示使用者的貼文以外,還會顯示使用者對其他貼文的回覆。"
|
||||||
autoAcceptFollowed: "自動允許來自追隨中使用者的追隨請求"
|
autoAcceptFollowed: "自動允許來自追隨中使用者的追隨請求"
|
||||||
@@ -313,6 +316,7 @@ selectFile: "選擇檔案"
|
|||||||
selectFiles: "選擇檔案"
|
selectFiles: "選擇檔案"
|
||||||
selectFolder: "選擇資料夾"
|
selectFolder: "選擇資料夾"
|
||||||
selectFolders: "選擇資料夾"
|
selectFolders: "選擇資料夾"
|
||||||
|
fileNotSelected: "尚未選擇檔案"
|
||||||
renameFile: "重新命名檔案"
|
renameFile: "重新命名檔案"
|
||||||
folderName: "資料夾名稱"
|
folderName: "資料夾名稱"
|
||||||
createFolder: "新增資料夾"
|
createFolder: "新增資料夾"
|
||||||
@@ -366,7 +370,7 @@ enableRegistration: "開放新使用者註冊"
|
|||||||
invite: "邀請"
|
invite: "邀請"
|
||||||
driveCapacityPerLocalAccount: "每個本地使用者的雲端硬碟容量"
|
driveCapacityPerLocalAccount: "每個本地使用者的雲端硬碟容量"
|
||||||
driveCapacityPerRemoteAccount: "每個非本地用戶的雲端空間大小"
|
driveCapacityPerRemoteAccount: "每個非本地用戶的雲端空間大小"
|
||||||
inMb: "以Mbps為單位"
|
inMb: "以 MB 為單位"
|
||||||
bannerUrl: "橫幅圖片URL"
|
bannerUrl: "橫幅圖片URL"
|
||||||
backgroundImageUrl: "背景圖片的來源網址 "
|
backgroundImageUrl: "背景圖片的來源網址 "
|
||||||
basicInfo: "基本資訊"
|
basicInfo: "基本資訊"
|
||||||
@@ -378,12 +382,12 @@ pinnedClipId: "置頂的摘錄ID"
|
|||||||
pinnedNotes: "已置頂的貼文"
|
pinnedNotes: "已置頂的貼文"
|
||||||
hcaptcha: "hCaptcha"
|
hcaptcha: "hCaptcha"
|
||||||
enableHcaptcha: "啟用 hCaptcha"
|
enableHcaptcha: "啟用 hCaptcha"
|
||||||
hcaptchaSiteKey: "網站金鑰"
|
hcaptchaSiteKey: "hcaptchaSiteKey"
|
||||||
hcaptchaSecretKey: "金鑰"
|
hcaptchaSecretKey: "hcaptchaSecretKey"
|
||||||
mcaptcha: "mCaptcha"
|
mcaptcha: "mCaptcha"
|
||||||
enableMcaptcha: "啟用 mCaptcha"
|
enableMcaptcha: "啟用 mCaptcha"
|
||||||
mcaptchaSiteKey: "網站金鑰"
|
mcaptchaSiteKey: "網站金鑰"
|
||||||
mcaptchaSecretKey: "金鑰"
|
mcaptchaSecretKey: "私密金鑰"
|
||||||
mcaptchaInstanceUrl: "mCaptcha 的實例網址"
|
mcaptchaInstanceUrl: "mCaptcha 的實例網址"
|
||||||
recaptcha: "reCAPTCHA"
|
recaptcha: "reCAPTCHA"
|
||||||
enableRecaptcha: "啟用 reCAPTCHA"
|
enableRecaptcha: "啟用 reCAPTCHA"
|
||||||
@@ -391,8 +395,8 @@ recaptchaSiteKey: "網站金鑰"
|
|||||||
recaptchaSecretKey: "金鑰"
|
recaptchaSecretKey: "金鑰"
|
||||||
turnstile: "Turnstile"
|
turnstile: "Turnstile"
|
||||||
enableTurnstile: "啟用 Turnstile"
|
enableTurnstile: "啟用 Turnstile"
|
||||||
turnstileSiteKey: "網站金鑰"
|
turnstileSiteKey: "turnstileSiteKey"
|
||||||
turnstileSecretKey: "金鑰"
|
turnstileSecretKey: "turnstileSecretKey"
|
||||||
avoidMultiCaptchaConfirm: "使用多種驗證方式可能會造成干擾,您要關閉其他驗證方式嗎?您可以按「取消」保留多種驗證方式。"
|
avoidMultiCaptchaConfirm: "使用多種驗證方式可能會造成干擾,您要關閉其他驗證方式嗎?您可以按「取消」保留多種驗證方式。"
|
||||||
antennas: "天線"
|
antennas: "天線"
|
||||||
manageAntennas: "管理天線"
|
manageAntennas: "管理天線"
|
||||||
@@ -464,10 +468,11 @@ title: "標題"
|
|||||||
text: "文字"
|
text: "文字"
|
||||||
enable: "啟用"
|
enable: "啟用"
|
||||||
next: "下一步"
|
next: "下一步"
|
||||||
retype: "再次輸入"
|
retype: "重新輸入"
|
||||||
noteOf: "{user}的貼文"
|
noteOf: "{user}的貼文"
|
||||||
quoteAttached: "引用"
|
quoteAttached: "引用"
|
||||||
quoteQuestion: "是否要引用?"
|
quoteQuestion: "是否要引用?"
|
||||||
|
attachAsFileQuestion: "剪貼簿的文字較長。請問是否要將其以文字檔的方式附加呢?"
|
||||||
noMessagesYet: "沒有訊息"
|
noMessagesYet: "沒有訊息"
|
||||||
newMessageExists: "有新的訊息"
|
newMessageExists: "有新的訊息"
|
||||||
onlyOneFileCanBeAttached: "只能加入一個附件"
|
onlyOneFileCanBeAttached: "只能加入一個附件"
|
||||||
@@ -602,7 +607,7 @@ addItem: "新增項目"
|
|||||||
rearrange: "排序方式"
|
rearrange: "排序方式"
|
||||||
relays: "中繼器"
|
relays: "中繼器"
|
||||||
addRelay: "新增中繼器"
|
addRelay: "新增中繼器"
|
||||||
inboxUrl: "收件夾URL"
|
inboxUrl: "收件夾 URL"
|
||||||
addedRelays: "已加入的中繼器"
|
addedRelays: "已加入的中繼器"
|
||||||
serviceworkerInfo: "如要使用推播通知,需要啟用此選項並設定金鑰。"
|
serviceworkerInfo: "如要使用推播通知,需要啟用此選項並設定金鑰。"
|
||||||
deletedNote: "已刪除的貼文"
|
deletedNote: "已刪除的貼文"
|
||||||
@@ -791,7 +796,7 @@ newVersionOfClientAvailable: "新版本的客戶端可用。"
|
|||||||
usageAmount: "使用量"
|
usageAmount: "使用量"
|
||||||
capacity: "容量"
|
capacity: "容量"
|
||||||
inUse: "已使用"
|
inUse: "已使用"
|
||||||
editCode: "編輯代碼"
|
editCode: "編輯程式碼"
|
||||||
apply: "套用"
|
apply: "套用"
|
||||||
receiveAnnouncementFromInstance: "接收來自伺服器的通知"
|
receiveAnnouncementFromInstance: "接收來自伺服器的通知"
|
||||||
emailNotification: "郵件通知"
|
emailNotification: "郵件通知"
|
||||||
@@ -1021,6 +1026,7 @@ thisPostMayBeAnnoyingHome: "發佈到首頁"
|
|||||||
thisPostMayBeAnnoyingCancel: "退出"
|
thisPostMayBeAnnoyingCancel: "退出"
|
||||||
thisPostMayBeAnnoyingIgnore: "直接發佈貼文"
|
thisPostMayBeAnnoyingIgnore: "直接發佈貼文"
|
||||||
collapseRenotes: "省略顯示已看過的轉發貼文"
|
collapseRenotes: "省略顯示已看過的轉發貼文"
|
||||||
|
collapseRenotesDescription: "將已做過反應和轉發的貼文折疊顯示。"
|
||||||
internalServerError: "內部伺服器錯誤"
|
internalServerError: "內部伺服器錯誤"
|
||||||
internalServerErrorDescription: "內部伺服器出現意外錯誤。"
|
internalServerErrorDescription: "內部伺服器出現意外錯誤。"
|
||||||
copyErrorInfo: "複製錯誤資訊"
|
copyErrorInfo: "複製錯誤資訊"
|
||||||
@@ -1062,7 +1068,7 @@ enableChartsForFederatedInstances: "生成遠端伺服器的圖表"
|
|||||||
showClipButtonInNoteFooter: "新增摘錄按鈕至貼文"
|
showClipButtonInNoteFooter: "新增摘錄按鈕至貼文"
|
||||||
reactionsDisplaySize: "反應的顯示尺寸"
|
reactionsDisplaySize: "反應的顯示尺寸"
|
||||||
limitWidthOfReaction: "限制反應的最大寬度,並縮小顯示尺寸。"
|
limitWidthOfReaction: "限制反應的最大寬度,並縮小顯示尺寸。"
|
||||||
noteIdOrUrl: "貼文ID或URL"
|
noteIdOrUrl: "貼文 ID 或 URL"
|
||||||
video: "影片"
|
video: "影片"
|
||||||
videos: "影片"
|
videos: "影片"
|
||||||
audio: "音效"
|
audio: "音效"
|
||||||
@@ -1077,7 +1083,7 @@ addMemo: "新增備註"
|
|||||||
editMemo: "編輯備註"
|
editMemo: "編輯備註"
|
||||||
reactionsList: "反應列表"
|
reactionsList: "反應列表"
|
||||||
renotesList: "轉發貼文列表"
|
renotesList: "轉發貼文列表"
|
||||||
notificationDisplay: "通知的顯示"
|
notificationDisplay: "通知"
|
||||||
leftTop: "左上"
|
leftTop: "左上"
|
||||||
rightTop: "右上"
|
rightTop: "右上"
|
||||||
leftBottom: "左下"
|
leftBottom: "左下"
|
||||||
@@ -1179,15 +1185,15 @@ repositoryUrlOrTarballRequired: "如果儲存庫不是公開的,則必須提
|
|||||||
feedback: "意見回饋"
|
feedback: "意見回饋"
|
||||||
feedbackUrl: "意見回饋 URL"
|
feedbackUrl: "意見回饋 URL"
|
||||||
impressum: "營運者資訊"
|
impressum: "營運者資訊"
|
||||||
impressumUrl: "營運者資訊網址"
|
impressumUrl: "營運者資訊 URL"
|
||||||
impressumDescription: "在德國與部份地區必須要明確顯示營運者資訊。"
|
impressumDescription: "在德國與部份地區必須要明確顯示營運者資訊。"
|
||||||
privacyPolicy: "隱私政策"
|
privacyPolicy: "隱私政策"
|
||||||
privacyPolicyUrl: "隱私政策網址"
|
privacyPolicyUrl: "隱私政策 URL"
|
||||||
tosAndPrivacyPolicy: "服務條款和隱私政策"
|
tosAndPrivacyPolicy: "服務條款和隱私政策"
|
||||||
avatarDecorations: "頭像裝飾"
|
avatarDecorations: "頭像裝飾"
|
||||||
attach: "裝上"
|
attach: "裝上"
|
||||||
detach: "取下"
|
detach: "取下"
|
||||||
detachAll: "移除所有裝飾"
|
detachAll: "全部移除"
|
||||||
angle: "角度"
|
angle: "角度"
|
||||||
flip: "翻轉"
|
flip: "翻轉"
|
||||||
showAvatarDecorations: "顯示頭像裝飾"
|
showAvatarDecorations: "顯示頭像裝飾"
|
||||||
@@ -1205,7 +1211,7 @@ remainingN: "剩餘:{n}"
|
|||||||
overwriteContentConfirm: "確定要覆蓋目前的內容嗎?"
|
overwriteContentConfirm: "確定要覆蓋目前的內容嗎?"
|
||||||
seasonalScreenEffect: "隨季節變換畫面的呈現"
|
seasonalScreenEffect: "隨季節變換畫面的呈現"
|
||||||
decorate: "設置頭像裝飾"
|
decorate: "設置頭像裝飾"
|
||||||
addMfmFunction: "插入MFM功能語法"
|
addMfmFunction: "插入 MFM 功能語法"
|
||||||
enableQuickAddMfmFunction: "顯示高級 MFM 選擇器"
|
enableQuickAddMfmFunction: "顯示高級 MFM 選擇器"
|
||||||
bubbleGame: "氣泡遊戲"
|
bubbleGame: "氣泡遊戲"
|
||||||
sfx: "音效"
|
sfx: "音效"
|
||||||
@@ -1225,16 +1231,25 @@ enableHorizontalSwipe: "滑動切換時間軸"
|
|||||||
loading: "載入中"
|
loading: "載入中"
|
||||||
surrender: "退出"
|
surrender: "退出"
|
||||||
gameRetry: "再試一次"
|
gameRetry: "再試一次"
|
||||||
notUsePleaseLeaveBlank: "如不使用,請留空"
|
notUsePleaseLeaveBlank: "如果不使用的話請留白"
|
||||||
useTotp: "使用一次性密碼"
|
useTotp: "使用一次性密碼"
|
||||||
useBackupCode: "使用備用驗證碼"
|
useBackupCode: "使用備用驗證碼"
|
||||||
launchApp: "啟動 App"
|
launchApp: "啟動 APP"
|
||||||
useNativeUIForVideoAudioPlayer: "使用瀏覽器的 UI 播放影片與音訊"
|
useNativeUIForVideoAudioPlayer: "使用瀏覽器的 UI 播放影片與音訊"
|
||||||
keepOriginalFilename: "保留原始檔名"
|
keepOriginalFilename: "保留原始檔名"
|
||||||
keepOriginalFilenameDescription: "如果關閉此設置,上傳時檔案名稱會自動替換為隨機字串。"
|
keepOriginalFilenameDescription: "如果關閉此設置,上傳時檔案名稱會自動替換為隨機字串。"
|
||||||
noDescription: "沒有說明文字"
|
noDescription: "沒有說明文字"
|
||||||
alwaysConfirmFollow: "點擊追隨時總是顯示確認訊息"
|
alwaysConfirmFollow: "點擊追隨時總是顯示確認訊息"
|
||||||
inquiry: "聯絡我們"
|
inquiry: "聯絡我們"
|
||||||
|
_delivery:
|
||||||
|
status: "傳送狀態"
|
||||||
|
stop: "停止傳送"
|
||||||
|
resume: "恢復傳送"
|
||||||
|
_type:
|
||||||
|
none: "直播中"
|
||||||
|
manuallySuspended: "手動暫停中"
|
||||||
|
goneSuspended: "因為伺服器刪除所以暫停中"
|
||||||
|
autoSuspendedForNotResponding: "因為伺服器沒有回應所以暫停中"
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "玩法說明"
|
howToPlay: "玩法說明"
|
||||||
hold: "保留"
|
hold: "保留"
|
||||||
@@ -1243,7 +1258,7 @@ _bubbleGame:
|
|||||||
scoreYen: "賺取的金額"
|
scoreYen: "賺取的金額"
|
||||||
highScore: "最高分"
|
highScore: "最高分"
|
||||||
maxChain: "最大結合數"
|
maxChain: "最大結合數"
|
||||||
yen: "{yen} 日圓"
|
yen: "{yen}円"
|
||||||
estimatedQty: "{qty}個"
|
estimatedQty: "{qty}個"
|
||||||
scoreSweets: "飯糰 {onigiriQtyWithUnit}"
|
scoreSweets: "飯糰 {onigiriQtyWithUnit}"
|
||||||
_howToPlay:
|
_howToPlay:
|
||||||
@@ -1271,7 +1286,7 @@ _initialAccountSetting:
|
|||||||
privacySetting: "隱私設定"
|
privacySetting: "隱私設定"
|
||||||
theseSettingsCanEditLater: "這裡的設定可以在之後變更。"
|
theseSettingsCanEditLater: "這裡的設定可以在之後變更。"
|
||||||
youCanEditMoreSettingsInSettingsPageLater: "除此之外,還可以在「設定」頁面進行各種設定。之後請確認看看。"
|
youCanEditMoreSettingsInSettingsPageLater: "除此之外,還可以在「設定」頁面進行各種設定。之後請確認看看。"
|
||||||
followUsers: "為了構築時間軸,試著追蹤您感興趣的使用者吧。"
|
followUsers: "為了構築時間軸,試著追隨您感興趣的使用者吧。"
|
||||||
pushNotificationDescription: "啟用推送通知,就可以在設備上接收{name}的通知。"
|
pushNotificationDescription: "啟用推送通知,就可以在設備上接收{name}的通知。"
|
||||||
initialAccountSettingCompleted: "初始設定完成了!"
|
initialAccountSettingCompleted: "初始設定完成了!"
|
||||||
haveFun: "盡情享受{name}吧!"
|
haveFun: "盡情享受{name}吧!"
|
||||||
@@ -1326,7 +1341,7 @@ _initialTutorial:
|
|||||||
title: "隱藏內容(CW)"
|
title: "隱藏內容(CW)"
|
||||||
description: "將顯示「註釋」中寫入的內容而不是本文。按一下「顯示內容」以顯示本文。"
|
description: "將顯示「註釋」中寫入的內容而不是本文。按一下「顯示內容」以顯示本文。"
|
||||||
_exampleNote:
|
_exampleNote:
|
||||||
cw: "美食恐怖主義注意"
|
cw: "注意消夜文"
|
||||||
note: "我吃了一個巧克力甜甜圈🍩😋"
|
note: "我吃了一個巧克力甜甜圈🍩😋"
|
||||||
useCases: "伺服器的服務條款可能會規範特定的貼文需要使用隱藏內容,除此之外也會用在隱藏劇情洩漏與敏感內容的貼文。"
|
useCases: "伺服器的服務條款可能會規範特定的貼文需要使用隱藏內容,除此之外也會用在隱藏劇情洩漏與敏感內容的貼文。"
|
||||||
_howToMakeAttachmentsSensitive:
|
_howToMakeAttachmentsSensitive:
|
||||||
@@ -1351,7 +1366,7 @@ _serverRules:
|
|||||||
_serverSettings:
|
_serverSettings:
|
||||||
iconUrl: "圖示的 URL"
|
iconUrl: "圖示的 URL"
|
||||||
appIconDescription: "指定顯示 {host} 為應用程式時的圖示。"
|
appIconDescription: "指定顯示 {host} 為應用程式時的圖示。"
|
||||||
appIconUsageExample: "例如:漸進式網路應用程式(PWA)、於手機桌面新增書籤"
|
appIconUsageExample: "例如:PWA 或是在手機桌面作為書籤等"
|
||||||
appIconStyleRecommendation: "因為可能會裁剪成圓形或圓角,所以建議用單色填滿邊框及背景。"
|
appIconStyleRecommendation: "因為可能會裁剪成圓形或圓角,所以建議用單色填滿邊框及背景。"
|
||||||
appIconResolutionMustBe: "解析度必須為 {resolution}。"
|
appIconResolutionMustBe: "解析度必須為 {resolution}。"
|
||||||
manifestJsonOverride: "覆寫 manifest.json"
|
manifestJsonOverride: "覆寫 manifest.json"
|
||||||
@@ -1360,6 +1375,8 @@ _serverSettings:
|
|||||||
fanoutTimelineDescription: "如果啟用的話,檢索各個時間軸的性能會顯著提昇,資料庫的負荷也會減少。不過,Redis 的記憶體使用量會增加。如果伺服器的記憶體容量比較少或者運行不穩定,可以停用。"
|
fanoutTimelineDescription: "如果啟用的話,檢索各個時間軸的性能會顯著提昇,資料庫的負荷也會減少。不過,Redis 的記憶體使用量會增加。如果伺服器的記憶體容量比較少或者運行不穩定,可以停用。"
|
||||||
fanoutTimelineDbFallback: "資料庫的回退"
|
fanoutTimelineDbFallback: "資料庫的回退"
|
||||||
fanoutTimelineDbFallbackDescription: "若啟用,在時間軸沒有快取的情況下將執行回退處理以額外查詢資料庫。若停用,可以透過不執行回退處理來進一步減少伺服器的負荷,但會限制可取得的時間軸範圍。"
|
fanoutTimelineDbFallbackDescription: "若啟用,在時間軸沒有快取的情況下將執行回退處理以額外查詢資料庫。若停用,可以透過不執行回退處理來進一步減少伺服器的負荷,但會限制可取得的時間軸範圍。"
|
||||||
|
inquiryUrl: "聯絡表單網址"
|
||||||
|
inquiryUrlDescription: "指定伺服器運營者的聯絡表單網址或包含運營者聯絡資訊網頁的網址。"
|
||||||
_accountMigration:
|
_accountMigration:
|
||||||
moveFrom: "從其他帳戶遷移到這個帳戶"
|
moveFrom: "從其他帳戶遷移到這個帳戶"
|
||||||
moveFromSub: "為另一個帳戶建立別名"
|
moveFromSub: "為另一個帳戶建立別名"
|
||||||
@@ -1559,7 +1576,7 @@ _achievements:
|
|||||||
_postedAt0min0sec:
|
_postedAt0min0sec:
|
||||||
title: "報時"
|
title: "報時"
|
||||||
description: "在零分零秒發佈貼文"
|
description: "在零分零秒發佈貼文"
|
||||||
flavor: "啵、啵、啵、嗶ーー"
|
flavor: "啵.啵.啵.嗶ー"
|
||||||
_selfQuote:
|
_selfQuote:
|
||||||
title: "自我引用"
|
title: "自我引用"
|
||||||
description: "引用了自己的貼文"
|
description: "引用了自己的貼文"
|
||||||
@@ -1694,8 +1711,8 @@ _role:
|
|||||||
roleAssignedTo: "手動指派角色完成"
|
roleAssignedTo: "手動指派角色完成"
|
||||||
isLocal: "本地使用者"
|
isLocal: "本地使用者"
|
||||||
isRemote: "遠端使用者"
|
isRemote: "遠端使用者"
|
||||||
isCat: "使用者是貓"
|
isCat: "貓使用者"
|
||||||
isBot: "使用者是機器人"
|
isBot: "機器人使用者"
|
||||||
isSuspended: "被停權的使用者"
|
isSuspended: "被停權的使用者"
|
||||||
isLocked: "上鎖的使用者"
|
isLocked: "上鎖的使用者"
|
||||||
isExplorable: "開啟了「使您的帳戶更容易被找到」功能的使用者"
|
isExplorable: "開啟了「使您的帳戶更容易被找到」功能的使用者"
|
||||||
@@ -1857,7 +1874,7 @@ _theme:
|
|||||||
invalid: "佈景主題格式錯誤"
|
invalid: "佈景主題格式錯誤"
|
||||||
make: "製作佈景主題"
|
make: "製作佈景主題"
|
||||||
base: "基於"
|
base: "基於"
|
||||||
addConstant: "添加常數"
|
addConstant: "新增常數"
|
||||||
constant: "常數"
|
constant: "常數"
|
||||||
defaultValue: "預設值"
|
defaultValue: "預設值"
|
||||||
color: "顏色"
|
color: "顏色"
|
||||||
@@ -1932,22 +1949,22 @@ _soundSettings:
|
|||||||
_ago:
|
_ago:
|
||||||
future: "未來"
|
future: "未來"
|
||||||
justNow: "剛剛"
|
justNow: "剛剛"
|
||||||
secondsAgo: "{n} 秒前"
|
secondsAgo: "{n}秒前"
|
||||||
minutesAgo: "{n} 分鐘前 "
|
minutesAgo: "{n}分鐘前"
|
||||||
hoursAgo: "{n} 小時前"
|
hoursAgo: "{n}小時前"
|
||||||
daysAgo: "{n} 天前"
|
daysAgo: "{n}天前"
|
||||||
weeksAgo: "{n} 週前"
|
weeksAgo: "{n}周前"
|
||||||
monthsAgo: "{n} 個月前"
|
monthsAgo: "{n}個月前"
|
||||||
yearsAgo: "{n} 年前"
|
yearsAgo: "{n}年前"
|
||||||
invalid: "無"
|
invalid: "無"
|
||||||
_timeIn:
|
_timeIn:
|
||||||
seconds: "{n} 秒後"
|
seconds: "{n}秒後"
|
||||||
minutes: "{n} 分後"
|
minutes: "{n}分鐘後"
|
||||||
hours: "{n} 小時後"
|
hours: "{n}小時後"
|
||||||
days: "{n} 日後"
|
days: "{n}天後"
|
||||||
weeks: "{n} 週後"
|
weeks: "{n}周後"
|
||||||
months: "{n} 個月後"
|
months: "{n}個月後"
|
||||||
years: "{n} 年後"
|
years: "{n}年後"
|
||||||
_time:
|
_time:
|
||||||
second: "秒"
|
second: "秒"
|
||||||
minute: "分鐘"
|
minute: "分鐘"
|
||||||
@@ -2032,7 +2049,6 @@ _permissions:
|
|||||||
"read:admin:server-info": "查看伺服器的資訊"
|
"read:admin:server-info": "查看伺服器的資訊"
|
||||||
"read:admin:show-moderation-log": "查看審查紀錄"
|
"read:admin:show-moderation-log": "查看審查紀錄"
|
||||||
"read:admin:show-user": "查看使用者的私密資訊"
|
"read:admin:show-user": "查看使用者的私密資訊"
|
||||||
"read:admin:show-users": "查看使用者的私密資訊"
|
|
||||||
"write:admin:suspend-user": "凍結使用者"
|
"write:admin:suspend-user": "凍結使用者"
|
||||||
"write:admin:unset-user-avatar": "刪除使用者的頭像"
|
"write:admin:unset-user-avatar": "刪除使用者的頭像"
|
||||||
"write:admin:unset-user-banner": "刪除使用者的橫幅"
|
"write:admin:unset-user-banner": "刪除使用者的橫幅"
|
||||||
@@ -2085,13 +2101,13 @@ _antennaSources:
|
|||||||
userList: "來自特定清單中的貼文"
|
userList: "來自特定清單中的貼文"
|
||||||
userBlacklist: "除指定使用者外的所有貼文"
|
userBlacklist: "除指定使用者外的所有貼文"
|
||||||
_weekday:
|
_weekday:
|
||||||
sunday: "週日"
|
sunday: "星期天"
|
||||||
monday: "週一"
|
monday: "星期一"
|
||||||
tuesday: "週二"
|
tuesday: "星期二"
|
||||||
wednesday: "週三"
|
wednesday: "星期三"
|
||||||
thursday: "週四"
|
thursday: "星期四"
|
||||||
friday: "週五"
|
friday: "星期五"
|
||||||
saturday: "週六"
|
saturday: "星期六"
|
||||||
_widgets:
|
_widgets:
|
||||||
profile: "個人檔案"
|
profile: "個人檔案"
|
||||||
instanceInfo: "伺服器資訊"
|
instanceInfo: "伺服器資訊"
|
||||||
@@ -2140,7 +2156,7 @@ _poll:
|
|||||||
deadlineDate: "截止日期"
|
deadlineDate: "截止日期"
|
||||||
deadlineTime: "小時"
|
deadlineTime: "小時"
|
||||||
duration: "時長"
|
duration: "時長"
|
||||||
votesCount: "{n} 票"
|
votesCount: "{n}票"
|
||||||
totalVotes: "合計 {n} 票"
|
totalVotes: "合計 {n} 票"
|
||||||
vote: "投票"
|
vote: "投票"
|
||||||
showResult: "顯示結果"
|
showResult: "顯示結果"
|
||||||
@@ -2173,7 +2189,7 @@ _postForm:
|
|||||||
e: "寫些什麼吧……"
|
e: "寫些什麼吧……"
|
||||||
f: "靜待發文……"
|
f: "靜待發文……"
|
||||||
_profile:
|
_profile:
|
||||||
name: "名稱"
|
name: "名字"
|
||||||
username: "使用者名稱"
|
username: "使用者名稱"
|
||||||
description: "關於我"
|
description: "關於我"
|
||||||
youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag"
|
youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag"
|
||||||
@@ -2231,10 +2247,10 @@ _timelines:
|
|||||||
_play:
|
_play:
|
||||||
new: "新增 Play"
|
new: "新增 Play"
|
||||||
edit: "編輯 Play"
|
edit: "編輯 Play"
|
||||||
created: "已新增Play "
|
created: "已新增 Play "
|
||||||
updated: "已更新Play "
|
updated: "已更新 Play "
|
||||||
deleted: "已刪除 Play"
|
deleted: "已刪除 Play"
|
||||||
pageSetting: "Play設定"
|
pageSetting: "Play 設定"
|
||||||
editThisPage: "編輯此 Play"
|
editThisPage: "編輯此 Play"
|
||||||
viewSource: "檢視原始碼"
|
viewSource: "檢視原始碼"
|
||||||
my: "自己的 Play"
|
my: "自己的 Play"
|
||||||
@@ -2247,7 +2263,7 @@ _play:
|
|||||||
_pages:
|
_pages:
|
||||||
newPage: "建立頁面"
|
newPage: "建立頁面"
|
||||||
editPage: "編輯頁面"
|
editPage: "編輯頁面"
|
||||||
readPage: "正檢視原始碼"
|
readPage: "正在檢視原始碼"
|
||||||
created: "頁面已建立"
|
created: "頁面已建立"
|
||||||
updated: "頁面已更新"
|
updated: "頁面已更新"
|
||||||
deleted: "頁面已被刪除"
|
deleted: "頁面已被刪除"
|
||||||
@@ -2274,7 +2290,7 @@ _pages:
|
|||||||
hideTitleWhenPinned: "被置頂於個人資料時隱藏頁面標題"
|
hideTitleWhenPinned: "被置頂於個人資料時隱藏頁面標題"
|
||||||
font: "字型"
|
font: "字型"
|
||||||
fontSerif: "襯線體"
|
fontSerif: "襯線體"
|
||||||
fontSansSerif: "無襯線體"
|
fontSansSerif: "黑體"
|
||||||
eyeCatchingImageSet: "設定封面影像"
|
eyeCatchingImageSet: "設定封面影像"
|
||||||
eyeCatchingImageRemove: "刪除封面影像"
|
eyeCatchingImageRemove: "刪除封面影像"
|
||||||
chooseBlock: "新增方塊"
|
chooseBlock: "新增方塊"
|
||||||
@@ -2346,6 +2362,7 @@ _deck:
|
|||||||
alwaysShowMainColumn: "總是顯示主欄"
|
alwaysShowMainColumn: "總是顯示主欄"
|
||||||
columnAlign: "對齊欄位"
|
columnAlign: "對齊欄位"
|
||||||
addColumn: "新增欄位"
|
addColumn: "新增欄位"
|
||||||
|
newNoteNotificationSettings: "新貼文通知的設定"
|
||||||
configureColumn: "欄位的設定"
|
configureColumn: "欄位的設定"
|
||||||
swapLeft: "向左移動"
|
swapLeft: "向左移動"
|
||||||
swapRight: "向右移動"
|
swapRight: "向右移動"
|
||||||
@@ -2384,7 +2401,7 @@ _drivecleaner:
|
|||||||
orderByCreatedAtAsc: "按新增日期降序排列"
|
orderByCreatedAtAsc: "按新增日期降序排列"
|
||||||
_webhookSettings:
|
_webhookSettings:
|
||||||
createWebhook: "建立 Webhook"
|
createWebhook: "建立 Webhook"
|
||||||
name: "名稱"
|
name: "名字"
|
||||||
secret: "密鑰"
|
secret: "密鑰"
|
||||||
events: "何時運行 Webhook"
|
events: "何時運行 Webhook"
|
||||||
active: "已啟用"
|
active: "已啟用"
|
||||||
|
|||||||
24
package.json
24
package.json
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2024.5.0-beta.3",
|
"version": "2024.7.0-beta.0",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/misskey-dev/misskey.git"
|
"url": "https://github.com/misskey-dev/misskey.git"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.0.6",
|
"packageManager": "pnpm@9.5.0",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/frontend",
|
"packages/frontend",
|
||||||
"packages/backend",
|
"packages/backend",
|
||||||
@@ -55,20 +55,22 @@
|
|||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"postcss": "8.4.38",
|
"postcss": "8.4.38",
|
||||||
"tar": "6.2.1",
|
"tar": "6.2.1",
|
||||||
"terser": "5.30.3",
|
"terser": "5.31.1",
|
||||||
"typescript": "5.4.5",
|
"typescript": "5.5.3",
|
||||||
"esbuild": "0.20.2",
|
"esbuild": "0.22.0",
|
||||||
"glob": "10.3.12"
|
"glob": "10.3.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "20.12.7",
|
"@misskey-dev/eslint-plugin": "2.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "7.7.1",
|
"@types/node": "20.14.9",
|
||||||
"@typescript-eslint/parser": "7.7.1",
|
"@typescript-eslint/eslint-plugin": "7.15.0",
|
||||||
|
"@typescript-eslint/parser": "7.15.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "13.7.3",
|
"cypress": "13.13.0",
|
||||||
"eslint": "8.57.0",
|
"eslint": "9.6.0",
|
||||||
|
"globals": "15.7.0",
|
||||||
"ncp": "2.0.0",
|
"ncp": "2.0.0",
|
||||||
"start-server-and-test": "2.0.3"
|
"start-server-and-test": "2.0.4"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@tensorflow/tfjs-core": "4.4.0"
|
"@tensorflow/tfjs-core": "4.4.0"
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
node_modules
|
|
||||||
/built
|
|
||||||
/.eslintrc.js
|
|
||||||
/@types/**/*
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
parserOptions: {
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
project: ['./tsconfig.json', './test/tsconfig.json'],
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'../shared/.eslintrc.js',
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'import/order': ['warn', {
|
|
||||||
'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
|
|
||||||
'pathGroups': [
|
|
||||||
{
|
|
||||||
'pattern': '@/**',
|
|
||||||
'group': 'external',
|
|
||||||
'position': 'after'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
'no-restricted-globals': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
'name': '__dirname',
|
|
||||||
'message': 'Not in ESModule. Use `import.meta.url` instead.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': '__filename',
|
|
||||||
'message': 'Not in ESModule. Use `import.meta.url` instead.'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
};
|
|
||||||
20
packages/backend/assets/api-doc.html
Normal file
20
packages/backend/assets/api-doc.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Misskey API</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script
|
||||||
|
id="api-reference"
|
||||||
|
data-url="/api.json"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Misskey API</title>
|
|
||||||
<!-- needed for adaptive design -->
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
|
||||||
|
|
||||||
<!--
|
|
||||||
ReDoc doesn't change outer page styles
|
|
||||||
-->
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<redoc spec-url="/api.json" expand-responses="200" expand-single-schema-field="true"></redoc>
|
|
||||||
<script src="https://cdn.redoc.ly/redoc/v2.1.3/bundles/redoc.standalone.js" integrity="sha256-u4DgqzYXoArvNF/Ymw3puKexfOC6lYfw0sfmeliBJ1I=" crossorigin="anonymous"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
46
packages/backend/eslint.config.js
Normal file
46
packages/backend/eslint.config.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import tsParser from '@typescript-eslint/parser';
|
||||||
|
import sharedConfig from '../shared/eslint.config.js';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...sharedConfig,
|
||||||
|
{
|
||||||
|
ignores: ['**/node_modules', 'built', '@types/**/*'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
project: ['./tsconfig.json', './test/tsconfig.json'],
|
||||||
|
sourceType: 'module',
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'import/order': ['warn', {
|
||||||
|
groups: [
|
||||||
|
'builtin',
|
||||||
|
'external',
|
||||||
|
'internal',
|
||||||
|
'parent',
|
||||||
|
'sibling',
|
||||||
|
'index',
|
||||||
|
'object',
|
||||||
|
'type',
|
||||||
|
],
|
||||||
|
pathGroups: [{
|
||||||
|
pattern: '@/**',
|
||||||
|
group: 'external',
|
||||||
|
position: 'after',
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
'no-restricted-globals': ['error', {
|
||||||
|
name: '__dirname',
|
||||||
|
message: 'Not in ESModule. Use `import.meta.url` instead.',
|
||||||
|
}, {
|
||||||
|
name: '__filename',
|
||||||
|
message: 'Not in ESModule. Use `import.meta.url` instead.',
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
39
packages/backend/migration/1708980134301-APMultipleKeys.js
Normal file
39
packages/backend/migration/1708980134301-APMultipleKeys.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class APMultipleKeys1708980134301 {
|
||||||
|
name = 'APMultipleKeys1708980134301'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_171e64971c780ebd23fae140bb"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PublicKey" character varying(128)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PrivateKey" character varying(128)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_10c146e4b39b443ede016f6736d"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_0db6a5fdb992323449edc8ee421" PRIMARY KEY ("userId", "keyId")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_0db6a5fdb992323449edc8ee421"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_171e64971c780ebd23fae140bba" PRIMARY KEY ("keyId")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_10c146e4b39b443ede016f6736" ON "user_publickey" ("userId") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_10c146e4b39b443ede016f6736"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_171e64971c780ebd23fae140bba"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_0db6a5fdb992323449edc8ee421" PRIMARY KEY ("userId", "keyId")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_0db6a5fdb992323449edc8ee421"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_10c146e4b39b443ede016f6736d" PRIMARY KEY ("userId")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" TYPE "public"."user_profile_followersVisibility_enum_old" USING "followersVisibility"::"text"::"public"."user_profile_followersVisibility_enum_old"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" SET DEFAULT 'public'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PrivateKey"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PublicKey"`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_171e64971c780ebd23fae140bb" ON "user_publickey" ("keyId") `);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
packages/backend/migration/1709242519122-HttpSignImplLv.js
Normal file
16
packages/backend/migration/1709242519122-HttpSignImplLv.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class HttpSignImplLv1709242519122 {
|
||||||
|
name = 'HttpSignImplLv1709242519122'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ADD "httpMessageSignaturesImplementationLevel" character varying(16) NOT NULL DEFAULT '00'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "httpMessageSignaturesImplementationLevel"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class APMultipleKeys1709269211718 {
|
||||||
|
name = 'APMultipleKeys1709269211718'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class AbuseReportNotification1713656541000 {
|
||||||
|
name = 'AbuseReportNotification1713656541000'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE "system_webhook" (
|
||||||
|
"id" varchar(32) NOT NULL,
|
||||||
|
"isActive" boolean NOT NULL DEFAULT true,
|
||||||
|
"updatedAt" timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"latestSentAt" timestamp with time zone NULL DEFAULT NULL,
|
||||||
|
"latestStatus" integer NULL DEFAULT NULL,
|
||||||
|
"name" varchar(255) NOT NULL,
|
||||||
|
"on" varchar(128) [] NOT NULL DEFAULT '{}'::character varying[],
|
||||||
|
"url" varchar(1024) NOT NULL,
|
||||||
|
"secret" varchar(1024) NOT NULL,
|
||||||
|
CONSTRAINT "PK_system_webhook_id" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
CREATE INDEX "IDX_system_webhook_isActive" ON "system_webhook" ("isActive");
|
||||||
|
CREATE INDEX "IDX_system_webhook_on" ON "system_webhook" USING gin ("on");
|
||||||
|
|
||||||
|
CREATE TABLE "abuse_report_notification_recipient" (
|
||||||
|
"id" varchar(32) NOT NULL,
|
||||||
|
"isActive" boolean NOT NULL DEFAULT true,
|
||||||
|
"updatedAt" timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"name" varchar(255) NOT NULL,
|
||||||
|
"method" varchar(64) NOT NULL,
|
||||||
|
"userId" varchar(32) NULL DEFAULT NULL,
|
||||||
|
"systemWebhookId" varchar(32) NULL DEFAULT NULL,
|
||||||
|
CONSTRAINT "PK_abuse_report_notification_recipient_id" PRIMARY KEY ("id"),
|
||||||
|
CONSTRAINT "FK_abuse_report_notification_recipient_userId1" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION,
|
||||||
|
CONSTRAINT "FK_abuse_report_notification_recipient_userId2" FOREIGN KEY ("userId") REFERENCES "user_profile"("userId") ON DELETE CASCADE ON UPDATE NO ACTION,
|
||||||
|
CONSTRAINT "FK_abuse_report_notification_recipient_systemWebhookId" FOREIGN KEY ("systemWebhookId") REFERENCES "system_webhook"("id") ON DELETE CASCADE ON UPDATE NO ACTION
|
||||||
|
);
|
||||||
|
CREATE INDEX "IDX_abuse_report_notification_recipient_isActive" ON "abuse_report_notification_recipient" ("isActive");
|
||||||
|
CREATE INDEX "IDX_abuse_report_notification_recipient_method" ON "abuse_report_notification_recipient" ("method");
|
||||||
|
CREATE INDEX "IDX_abuse_report_notification_recipient_userId" ON "abuse_report_notification_recipient" ("userId");
|
||||||
|
CREATE INDEX "IDX_abuse_report_notification_recipient_systemWebhookId" ON "abuse_report_notification_recipient" ("systemWebhookId");
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`
|
||||||
|
ALTER TABLE "abuse_report_notification_recipient" DROP CONSTRAINT "FK_abuse_report_notification_recipient_userId1";
|
||||||
|
ALTER TABLE "abuse_report_notification_recipient" DROP CONSTRAINT "FK_abuse_report_notification_recipient_userId2";
|
||||||
|
ALTER TABLE "abuse_report_notification_recipient" DROP CONSTRAINT "FK_abuse_report_notification_recipient_systemWebhookId";
|
||||||
|
DROP INDEX "IDX_abuse_report_notification_recipient_isActive";
|
||||||
|
DROP INDEX "IDX_abuse_report_notification_recipient_method";
|
||||||
|
DROP INDEX "IDX_abuse_report_notification_recipient_userId";
|
||||||
|
DROP INDEX "IDX_abuse_report_notification_recipient_systemWebhookId";
|
||||||
|
DROP TABLE "abuse_report_notification_recipient";
|
||||||
|
|
||||||
|
DROP INDEX "IDX_system_webhook_isActive";
|
||||||
|
DROP INDEX "IDX_system_webhook_on";
|
||||||
|
DROP TABLE "system_webhook";
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class NotRespondingSince1716345015347 {
|
||||||
|
name = 'NotRespondingSince1716345015347'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ADD "notRespondingSince" TIMESTAMP WITH TIME ZONE`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "notRespondingSince"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class SuspensionStateInsteadOfIsSspended1716345771510 {
|
||||||
|
name = 'SuspensionStateInsteadOfIsSspended1716345771510'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."instance_suspensionstate_enum" AS ENUM('none', 'manuallySuspended', 'goneSuspended', 'autoSuspendedForNotResponding')`);
|
||||||
|
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_34500da2e38ac393f7bb6b299c"`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "isSuspended" TO "suspensionState"`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" DROP DEFAULT`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" TYPE "public"."instance_suspensionstate_enum" USING (
|
||||||
|
CASE "suspensionState"
|
||||||
|
WHEN TRUE THEN 'manuallySuspended'::instance_suspensionstate_enum
|
||||||
|
ELSE 'none'::instance_suspensionstate_enum
|
||||||
|
END
|
||||||
|
)`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" SET DEFAULT 'none'`);
|
||||||
|
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_3ede46f507c87ad698051d56a8" ON "instance" ("suspensionState") `);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_3ede46f507c87ad698051d56a8"`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" DROP DEFAULT`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" TYPE boolean USING (
|
||||||
|
CASE "suspensionState"
|
||||||
|
WHEN 'none'::instance_suspensionstate_enum THEN FALSE
|
||||||
|
ELSE TRUE
|
||||||
|
END
|
||||||
|
)`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" SET DEFAULT false`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "suspensionState" TO "isSuspended"`);
|
||||||
|
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_34500da2e38ac393f7bb6b299c" ON "instance" ("isSuspended") `);
|
||||||
|
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."instance_suspensionstate_enum"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class RemoveAntennaNotify1716450883149 {
|
||||||
|
name = 'RemoveAntennaNotify1716450883149'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "notify"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "antenna" ADD "notify" boolean NOT NULL`);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
packages/backend/migration/1717117195275-inquiryUrl.js
Normal file
16
packages/backend/migration/1717117195275-inquiryUrl.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class InquiryUrl1717117195275 {
|
||||||
|
name = 'InquiryUrl1717117195275'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "inquiryUrl" character varying(1024)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "inquiryUrl"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.10.0"
|
"node": "^20.10.0 || ^22.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./built/boot/entry.js",
|
"start": "node ./built/boot/entry.js",
|
||||||
@@ -65,41 +65,43 @@
|
|||||||
"utf-8-validate": "6.0.3"
|
"utf-8-validate": "6.0.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.412.0",
|
"@aws-sdk/client-s3": "3.600.0",
|
||||||
"@aws-sdk/lib-storage": "3.412.0",
|
"@aws-sdk/lib-storage": "3.600.0",
|
||||||
"@bull-board/api": "5.17.0",
|
"@bull-board/api": "5.20.5",
|
||||||
"@bull-board/fastify": "5.17.0",
|
"@bull-board/fastify": "5.20.5",
|
||||||
"@bull-board/ui": "5.17.0",
|
"@bull-board/ui": "5.20.5",
|
||||||
"@discordapp/twemoji": "15.0.3",
|
"@discordapp/twemoji": "15.0.3",
|
||||||
"@fastify/accepts": "4.3.0",
|
"@fastify/accepts": "4.3.0",
|
||||||
"@fastify/cookie": "9.3.1",
|
"@fastify/cookie": "9.3.1",
|
||||||
"@fastify/cors": "9.0.1",
|
"@fastify/cors": "9.0.1",
|
||||||
"@fastify/express": "3.0.0",
|
"@fastify/express": "3.0.0",
|
||||||
"@fastify/http-proxy": "9.5.0",
|
"@fastify/http-proxy": "9.5.0",
|
||||||
"@fastify/multipart": "8.2.0",
|
"@fastify/multipart": "8.3.0",
|
||||||
"@fastify/static": "7.0.3",
|
"@fastify/static": "7.0.4",
|
||||||
"@fastify/view": "9.1.0",
|
"@fastify/view": "9.1.0",
|
||||||
|
"@misskey-dev/node-http-message-signatures": "0.0.10",
|
||||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||||
"@misskey-dev/summaly": "5.1.0",
|
"@misskey-dev/summaly": "5.1.0",
|
||||||
"@napi-rs/canvas": "^0.1.52",
|
"@napi-rs/canvas": "^0.1.53",
|
||||||
"@nestjs/common": "10.3.8",
|
"@nestjs/common": "10.3.10",
|
||||||
"@nestjs/core": "10.3.8",
|
"@nestjs/core": "10.3.10",
|
||||||
"@nestjs/testing": "10.3.8",
|
"@nestjs/testing": "10.3.10",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@sentry/node": "8.13.0",
|
||||||
|
"@sentry/profiling-node": "8.13.0",
|
||||||
"@simplewebauthn/server": "10.0.0",
|
"@simplewebauthn/server": "10.0.0",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
"@smithy/node-http-handler": "2.5.0",
|
"@smithy/node-http-handler": "2.5.0",
|
||||||
"@swc/cli": "0.3.12",
|
"@swc/cli": "0.3.12",
|
||||||
"@swc/core": "1.4.17",
|
"@swc/core": "1.6.6",
|
||||||
"@twemoji/parser": "15.1.1",
|
"@twemoji/parser": "15.1.1",
|
||||||
"accepts": "1.3.8",
|
"accepts": "1.3.8",
|
||||||
"ajv": "8.13.0",
|
"ajv": "8.16.0",
|
||||||
"archiver": "7.0.1",
|
"archiver": "7.0.1",
|
||||||
"async-mutex": "0.5.0",
|
"async-mutex": "0.5.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
"bullmq": "5.7.8",
|
"bullmq": "5.8.3",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.2",
|
"cbor": "9.0.2",
|
||||||
"chalk": "5.3.0",
|
"chalk": "5.3.0",
|
||||||
@@ -110,27 +112,27 @@
|
|||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"fastify": "4.26.2",
|
"fastify": "4.28.1",
|
||||||
"fastify-raw-body": "4.3.0",
|
"fastify-raw-body": "4.3.0",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "19.0.0",
|
"file-type": "19.0.0",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.3",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"got": "14.2.1",
|
"got": "14.4.1",
|
||||||
"happy-dom": "10.0.3",
|
"happy-dom": "10.0.3",
|
||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"htmlescape": "1.1.1",
|
"htmlescape": "1.1.1",
|
||||||
"http-link-header": "1.1.3",
|
"http-link-header": "1.1.3",
|
||||||
"ioredis": "5.4.1",
|
"ioredis": "5.4.1",
|
||||||
"ip-cidr": "3.1.0",
|
"ip-cidr": "4.0.1",
|
||||||
"ipaddr.js": "2.2.0",
|
"ipaddr.js": "2.2.0",
|
||||||
"is-svg": "5.0.0",
|
"is-svg": "5.0.1",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"jsdom": "24.0.0",
|
"jsdom": "24.1.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"jsonld": "8.3.2",
|
"jsonld": "8.3.2",
|
||||||
"jsrsasign": "11.1.0",
|
"jsrsasign": "11.1.0",
|
||||||
"meilisearch": "0.38.0",
|
"meilisearch": "0.41.0",
|
||||||
"mfm-js": "0.24.0",
|
"mfm-js": "0.24.0",
|
||||||
"microformats-parser": "2.0.2",
|
"microformats-parser": "2.0.2",
|
||||||
"mime-types": "2.1.35",
|
"mime-types": "2.1.35",
|
||||||
@@ -140,24 +142,24 @@
|
|||||||
"nanoid": "5.0.7",
|
"nanoid": "5.0.7",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
"nodemailer": "6.9.13",
|
"nodemailer": "6.9.14",
|
||||||
"nsfwjs": "2.4.2",
|
"nsfwjs": "2.4.2",
|
||||||
"oauth": "0.10.0",
|
"oauth": "0.10.0",
|
||||||
"oauth2orize": "1.12.0",
|
"oauth2orize": "1.12.0",
|
||||||
"oauth2orize-pkce": "0.1.2",
|
"oauth2orize-pkce": "0.1.2",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"otpauth": "9.2.3",
|
"otpauth": "9.3.1",
|
||||||
"parse5": "7.1.2",
|
"parse5": "7.1.2",
|
||||||
"pg": "8.11.5",
|
"pg": "8.12.0",
|
||||||
"pkce-challenge": "4.1.0",
|
"pkce-challenge": "4.1.0",
|
||||||
"probe-image-size": "7.2.3",
|
"probe-image-size": "7.2.3",
|
||||||
"promise-limit": "2.7.0",
|
"promise-limit": "2.7.0",
|
||||||
"pug": "3.0.2",
|
"pug": "3.0.3",
|
||||||
"punycode": "2.3.1",
|
"punycode": "2.3.1",
|
||||||
"qrcode": "1.5.3",
|
"qrcode": "1.5.3",
|
||||||
"random-seed": "0.3.0",
|
"random-seed": "0.3.0",
|
||||||
"ratelimiter": "3.4.1",
|
"ratelimiter": "3.4.1",
|
||||||
"re2": "1.20.10",
|
"re2": "1.21.3",
|
||||||
"redis-lock": "0.1.4",
|
"redis-lock": "0.1.4",
|
||||||
"reflect-metadata": "0.2.2",
|
"reflect-metadata": "0.2.2",
|
||||||
"rename": "1.0.4",
|
"rename": "1.0.4",
|
||||||
@@ -165,27 +167,26 @@
|
|||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.1",
|
||||||
"sanitize-html": "2.13.0",
|
"sanitize-html": "2.13.0",
|
||||||
"secure-json-parse": "2.7.0",
|
"secure-json-parse": "2.7.0",
|
||||||
"sharp": "0.33.3",
|
"sharp": "0.33.4",
|
||||||
"slacc": "0.0.10",
|
"slacc": "0.0.10",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"systeminformation": "5.22.7",
|
"systeminformation": "5.22.11",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tmp": "0.2.3",
|
"tmp": "0.2.3",
|
||||||
"tsc-alias": "1.8.8",
|
"tsc-alias": "1.8.10",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typeorm": "0.3.20",
|
"typeorm": "0.3.20",
|
||||||
"typescript": "5.4.5",
|
"typescript": "5.5.3",
|
||||||
"ulid": "2.3.0",
|
"ulid": "2.3.0",
|
||||||
"vary": "1.1.2",
|
"vary": "1.1.2",
|
||||||
"web-push": "3.6.7",
|
"web-push": "3.6.7",
|
||||||
"ws": "8.17.0",
|
"ws": "8.17.1",
|
||||||
"xev": "3.0.2"
|
"xev": "3.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "29.7.0",
|
"@jest/globals": "29.7.0",
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@nestjs/platform-express": "10.3.10",
|
||||||
"@nestjs/platform-express": "10.3.8",
|
|
||||||
"@simplewebauthn/types": "10.0.0",
|
"@simplewebauthn/types": "10.0.0",
|
||||||
"@swc/jest": "0.2.36",
|
"@swc/jest": "0.2.36",
|
||||||
"@types/accepts": "1.3.7",
|
"@types/accepts": "1.3.7",
|
||||||
@@ -195,22 +196,21 @@
|
|||||||
"@types/color-convert": "2.0.3",
|
"@types/color-convert": "2.0.3",
|
||||||
"@types/content-disposition": "0.5.8",
|
"@types/content-disposition": "0.5.8",
|
||||||
"@types/fluent-ffmpeg": "2.1.24",
|
"@types/fluent-ffmpeg": "2.1.24",
|
||||||
"@types/htmlescape": "^1.1.3",
|
"@types/htmlescape": "1.1.3",
|
||||||
"@types/http-link-header": "1.0.5",
|
"@types/http-link-header": "1.0.7",
|
||||||
"@types/jest": "29.5.12",
|
"@types/jest": "29.5.12",
|
||||||
"@types/js-yaml": "4.0.9",
|
"@types/js-yaml": "4.0.9",
|
||||||
"@types/jsdom": "21.1.6",
|
"@types/jsdom": "21.1.7",
|
||||||
"@types/jsonld": "1.5.13",
|
"@types/jsonld": "1.5.14",
|
||||||
"@types/jsrsasign": "10.5.14",
|
"@types/jsrsasign": "10.5.14",
|
||||||
"@types/mime-types": "2.1.4",
|
"@types/mime-types": "2.1.4",
|
||||||
"@types/ms": "0.7.34",
|
"@types/ms": "0.7.34",
|
||||||
"@types/node": "20.12.7",
|
"@types/node": "20.14.9",
|
||||||
"@types/node-fetch": "3.0.3",
|
|
||||||
"@types/nodemailer": "6.4.15",
|
"@types/nodemailer": "6.4.15",
|
||||||
"@types/oauth": "0.9.4",
|
"@types/oauth": "0.9.5",
|
||||||
"@types/oauth2orize": "1.11.5",
|
"@types/oauth2orize": "1.11.5",
|
||||||
"@types/oauth2orize-pkce": "0.1.2",
|
"@types/oauth2orize-pkce": "0.1.2",
|
||||||
"@types/pg": "8.11.5",
|
"@types/pg": "8.11.6",
|
||||||
"@types/pug": "2.0.10",
|
"@types/pug": "2.0.10",
|
||||||
"@types/punycode": "2.1.4",
|
"@types/punycode": "2.1.4",
|
||||||
"@types/qrcode": "1.5.5",
|
"@types/qrcode": "1.5.5",
|
||||||
@@ -226,18 +226,17 @@
|
|||||||
"@types/vary": "1.1.3",
|
"@types/vary": "1.1.3",
|
||||||
"@types/web-push": "3.6.3",
|
"@types/web-push": "3.6.3",
|
||||||
"@types/ws": "8.5.10",
|
"@types/ws": "8.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "7.7.1",
|
"@typescript-eslint/eslint-plugin": "7.15.0",
|
||||||
"@typescript-eslint/parser": "7.7.1",
|
"@typescript-eslint/parser": "7.15.0",
|
||||||
"aws-sdk-client-mock": "3.0.1",
|
"aws-sdk-client-mock": "4.0.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.57.0",
|
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"execa": "8.0.1",
|
"execa": "9.2.0",
|
||||||
"fkill": "^9.0.0",
|
"fkill": "9.0.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-mock": "29.7.0",
|
"jest-mock": "29.7.0",
|
||||||
"nodemon": "3.1.0",
|
"nodemon": "3.1.4",
|
||||||
"pid-port": "1.0.0",
|
"pid-port": "1.0.0",
|
||||||
"simple-oauth2": "5.0.0"
|
"simple-oauth2": "5.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ function execStart() {
|
|||||||
|
|
||||||
async function killProc() {
|
async function killProc() {
|
||||||
if (backendProcess) {
|
if (backendProcess) {
|
||||||
|
backendProcess.catch(() => {}); // backendProcess.kill()によって発生する例外を無視するためにcatch()を呼び出す
|
||||||
backendProcess.kill();
|
backendProcess.kill();
|
||||||
await new Promise(resolve => backendProcess.on('exit', resolve));
|
await new Promise(resolve => backendProcess.on('exit', resolve));
|
||||||
backendProcess = undefined;
|
backendProcess = undefined;
|
||||||
@@ -46,6 +47,7 @@ async function killProc() {
|
|||||||
],
|
],
|
||||||
{
|
{
|
||||||
stdio: [process.stdin, process.stdout, process.stderr, 'ipc'],
|
stdio: [process.stdin, process.stdout, process.stderr, 'ipc'],
|
||||||
|
serialization: "json",
|
||||||
})
|
})
|
||||||
.on('message', async (message) => {
|
.on('message', async (message) => {
|
||||||
if (message.type === 'exit') {
|
if (message.type === 'exit') {
|
||||||
|
|||||||
82
packages/backend/src/@types/http-signature.d.ts
vendored
82
packages/backend/src/@types/http-signature.d.ts
vendored
@@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare module '@peertube/http-signature' {
|
|
||||||
import type { IncomingMessage, ClientRequest } from 'node:http';
|
|
||||||
|
|
||||||
interface ISignature {
|
|
||||||
keyId: string;
|
|
||||||
algorithm: string;
|
|
||||||
headers: string[];
|
|
||||||
signature: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IOptions {
|
|
||||||
headers?: string[];
|
|
||||||
algorithm?: string;
|
|
||||||
strict?: boolean;
|
|
||||||
authorizationHeaderName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IParseRequestOptions extends IOptions {
|
|
||||||
clockSkew?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IParsedSignature {
|
|
||||||
scheme: string;
|
|
||||||
params: ISignature;
|
|
||||||
signingString: string;
|
|
||||||
algorithm: string;
|
|
||||||
keyId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type RequestSignerConstructorOptions =
|
|
||||||
IRequestSignerConstructorOptionsFromProperties |
|
|
||||||
IRequestSignerConstructorOptionsFromFunction;
|
|
||||||
|
|
||||||
interface IRequestSignerConstructorOptionsFromProperties {
|
|
||||||
keyId: string;
|
|
||||||
key: string | Buffer;
|
|
||||||
algorithm?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRequestSignerConstructorOptionsFromFunction {
|
|
||||||
sign?: (data: string, cb: (err: any, sig: ISignature) => void) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RequestSigner {
|
|
||||||
constructor(options: RequestSignerConstructorOptions);
|
|
||||||
|
|
||||||
public writeHeader(header: string, value: string): string;
|
|
||||||
|
|
||||||
public writeDateHeader(): string;
|
|
||||||
|
|
||||||
public writeTarget(method: string, path: string): void;
|
|
||||||
|
|
||||||
public sign(cb: (err: any, authz: string) => void): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ISignRequestOptions extends IOptions {
|
|
||||||
keyId: string;
|
|
||||||
key: string;
|
|
||||||
httpVersion?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parse(request: IncomingMessage, options?: IParseRequestOptions): IParsedSignature;
|
|
||||||
export function parseRequest(request: IncomingMessage, options?: IParseRequestOptions): IParsedSignature;
|
|
||||||
|
|
||||||
export function sign(request: ClientRequest, options: ISignRequestOptions): boolean;
|
|
||||||
export function signRequest(request: ClientRequest, options: ISignRequestOptions): boolean;
|
|
||||||
export function createSigner(): RequestSigner;
|
|
||||||
export function isSigner(obj: any): obj is RequestSigner;
|
|
||||||
|
|
||||||
export function sshKeyToPEM(key: string): string;
|
|
||||||
export function sshKeyFingerprint(key: string): string;
|
|
||||||
export function pemToRsaSSHKey(pem: string, comment: string): string;
|
|
||||||
|
|
||||||
export function verify(parsedSignature: IParsedSignature, pubkey: string | Buffer): boolean;
|
|
||||||
export function verifySignature(parsedSignature: IParsedSignature, pubkey: string | Buffer): boolean;
|
|
||||||
export function verifyHMAC(parsedSignature: IParsedSignature, secret: string): boolean;
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ import { LoggerService } from '@nestjs/common';
|
|||||||
import Logger from '@/logger.js';
|
import Logger from '@/logger.js';
|
||||||
|
|
||||||
const logger = new Logger('core', 'cyan');
|
const logger = new Logger('core', 'cyan');
|
||||||
const nestLogger = logger.createSubLogger('nest', 'green', false);
|
const nestLogger = logger.createSubLogger('nest', 'green');
|
||||||
|
|
||||||
export class NestLogger implements LoggerService {
|
export class NestLogger implements LoggerService {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import Logger from '@/logger.js';
|
|||||||
import { envOption } from '../env.js';
|
import { envOption } from '../env.js';
|
||||||
import { masterMain } from './master.js';
|
import { masterMain } from './master.js';
|
||||||
import { workerMain } from './worker.js';
|
import { workerMain } from './worker.js';
|
||||||
|
import { readyRef } from './ready.js';
|
||||||
|
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ Error.stackTraceLimit = Infinity;
|
|||||||
EventEmitter.defaultMaxListeners = 128;
|
EventEmitter.defaultMaxListeners = 128;
|
||||||
|
|
||||||
const logger = new Logger('core', 'cyan');
|
const logger = new Logger('core', 'cyan');
|
||||||
const clusterLogger = logger.createSubLogger('cluster', 'orange', false);
|
const clusterLogger = logger.createSubLogger('cluster', 'orange');
|
||||||
const ev = new Xev();
|
const ev = new Xev();
|
||||||
|
|
||||||
//#region Events
|
//#region Events
|
||||||
@@ -79,6 +80,8 @@ if (cluster.isWorker || envOption.disableClustering) {
|
|||||||
await workerMain();
|
await workerMain();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readyRef.value = true;
|
||||||
|
|
||||||
// ユニットテスト時にMisskeyが子プロセスで起動された時のため
|
// ユニットテスト時にMisskeyが子プロセスで起動された時のため
|
||||||
// それ以外のときは process.send は使えないので弾く
|
// それ以外のときは process.send は使えないので弾く
|
||||||
if (process.send) {
|
if (process.send) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import * as os from 'node:os';
|
|||||||
import cluster from 'node:cluster';
|
import cluster from 'node:cluster';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import chalkTemplate from 'chalk-template';
|
import chalkTemplate from 'chalk-template';
|
||||||
|
import * as Sentry from '@sentry/node';
|
||||||
|
import { nodeProfilingIntegration } from '@sentry/profiling-node';
|
||||||
import Logger from '@/logger.js';
|
import Logger from '@/logger.js';
|
||||||
import { loadConfig } from '@/config.js';
|
import { loadConfig } from '@/config.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
@@ -23,7 +25,7 @@ const _dirname = dirname(_filename);
|
|||||||
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8'));
|
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8'));
|
||||||
|
|
||||||
const logger = new Logger('core', 'cyan');
|
const logger = new Logger('core', 'cyan');
|
||||||
const bootLogger = logger.createSubLogger('boot', 'magenta', false);
|
const bootLogger = logger.createSubLogger('boot', 'magenta');
|
||||||
|
|
||||||
const themeColor = chalk.hex('#86b300');
|
const themeColor = chalk.hex('#86b300');
|
||||||
|
|
||||||
@@ -71,6 +73,24 @@ export async function masterMain() {
|
|||||||
|
|
||||||
bootLogger.succ('Misskey initialized');
|
bootLogger.succ('Misskey initialized');
|
||||||
|
|
||||||
|
if (config.sentryForBackend) {
|
||||||
|
Sentry.init({
|
||||||
|
integrations: [
|
||||||
|
...(config.sentryForBackend.enableNodeProfiling ? [nodeProfilingIntegration()] : []),
|
||||||
|
],
|
||||||
|
|
||||||
|
// Performance Monitoring
|
||||||
|
tracesSampleRate: 1.0, // Capture 100% of the transactions
|
||||||
|
|
||||||
|
// Set sampling rate for profiling - this is relative to tracesSampleRate
|
||||||
|
profilesSampleRate: 1.0,
|
||||||
|
|
||||||
|
maxBreadcrumbs: 0,
|
||||||
|
|
||||||
|
...config.sentryForBackend.options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (envOption.disableClustering) {
|
if (envOption.disableClustering) {
|
||||||
if (envOption.onlyServer) {
|
if (envOption.onlyServer) {
|
||||||
await server();
|
await server();
|
||||||
|
|||||||
@@ -3,4 +3,4 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const fallback = Symbol('fallback');
|
export const readyRef = { value: false };
|
||||||
@@ -4,13 +4,36 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import cluster from 'node:cluster';
|
import cluster from 'node:cluster';
|
||||||
|
import * as Sentry from '@sentry/node';
|
||||||
|
import { nodeProfilingIntegration } from '@sentry/profiling-node';
|
||||||
import { envOption } from '@/env.js';
|
import { envOption } from '@/env.js';
|
||||||
|
import { loadConfig } from '@/config.js';
|
||||||
import { jobQueue, server } from './common.js';
|
import { jobQueue, server } from './common.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init worker process
|
* Init worker process
|
||||||
*/
|
*/
|
||||||
export async function workerMain() {
|
export async function workerMain() {
|
||||||
|
const config = loadConfig();
|
||||||
|
|
||||||
|
if (config.sentryForBackend) {
|
||||||
|
Sentry.init({
|
||||||
|
integrations: [
|
||||||
|
...(config.sentryForBackend.enableNodeProfiling ? [nodeProfilingIntegration()] : []),
|
||||||
|
],
|
||||||
|
|
||||||
|
// Performance Monitoring
|
||||||
|
tracesSampleRate: 1.0, // Capture 100% of the transactions
|
||||||
|
|
||||||
|
// Set sampling rate for profiling - this is relative to tracesSampleRate
|
||||||
|
profilesSampleRate: 1.0,
|
||||||
|
|
||||||
|
maxBreadcrumbs: 0,
|
||||||
|
|
||||||
|
...config.sentryForBackend.options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (envOption.onlyServer) {
|
if (envOption.onlyServer) {
|
||||||
await server();
|
await server();
|
||||||
} else if (envOption.onlyQueue) {
|
} else if (envOption.onlyQueue) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import * as fs from 'node:fs';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { dirname, resolve } from 'node:path';
|
import { dirname, resolve } from 'node:path';
|
||||||
import * as yaml from 'js-yaml';
|
import * as yaml from 'js-yaml';
|
||||||
|
import * as Sentry from '@sentry/node';
|
||||||
import type { RedisOptions } from 'ioredis';
|
import type { RedisOptions } from 'ioredis';
|
||||||
|
|
||||||
type RedisOptionsSource = Partial<RedisOptions> & {
|
type RedisOptionsSource = Partial<RedisOptions> & {
|
||||||
@@ -22,7 +23,7 @@ type RedisOptionsSource = Partial<RedisOptions> & {
|
|||||||
* 設定ファイルの型
|
* 設定ファイルの型
|
||||||
*/
|
*/
|
||||||
type Source = {
|
type Source = {
|
||||||
url: string;
|
url?: string;
|
||||||
port?: number;
|
port?: number;
|
||||||
socket?: string;
|
socket?: string;
|
||||||
chmodSocket?: string;
|
chmodSocket?: string;
|
||||||
@@ -30,9 +31,9 @@ type Source = {
|
|||||||
db: {
|
db: {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
db: string;
|
db?: string;
|
||||||
user: string;
|
user?: string;
|
||||||
pass: string;
|
pass?: string;
|
||||||
disableCache?: boolean;
|
disableCache?: boolean;
|
||||||
extra?: { [x: string]: string };
|
extra?: { [x: string]: string };
|
||||||
};
|
};
|
||||||
@@ -56,6 +57,8 @@ type Source = {
|
|||||||
index: string;
|
index: string;
|
||||||
scope?: 'local' | 'global' | string[];
|
scope?: 'local' | 'global' | string[];
|
||||||
};
|
};
|
||||||
|
sentryForBackend?: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; };
|
||||||
|
sentryForFrontend?: { options: Partial<Sentry.NodeOptions> };
|
||||||
|
|
||||||
publishTarballInsteadOfProvideRepositoryUrl?: boolean;
|
publishTarballInsteadOfProvideRepositoryUrl?: boolean;
|
||||||
|
|
||||||
@@ -166,6 +169,8 @@ export type Config = {
|
|||||||
redisForPubsub: RedisOptions & RedisOptionsSource;
|
redisForPubsub: RedisOptions & RedisOptionsSource;
|
||||||
redisForJobQueue: RedisOptions & RedisOptionsSource;
|
redisForJobQueue: RedisOptions & RedisOptionsSource;
|
||||||
redisForTimelines: RedisOptions & RedisOptionsSource;
|
redisForTimelines: RedisOptions & RedisOptionsSource;
|
||||||
|
sentryForBackend: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; } | undefined;
|
||||||
|
sentryForFrontend: { options: Partial<Sentry.NodeOptions> } | undefined;
|
||||||
perChannelMaxNoteCacheCount: number;
|
perChannelMaxNoteCacheCount: number;
|
||||||
perUserNotificationsMaxCount: number;
|
perUserNotificationsMaxCount: number;
|
||||||
deactivateAntennaThreshold: number;
|
deactivateAntennaThreshold: number;
|
||||||
@@ -197,13 +202,17 @@ export function loadConfig(): Config {
|
|||||||
: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
|
: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
|
||||||
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
|
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
|
||||||
|
|
||||||
const url = tryCreateUrl(config.url);
|
const url = tryCreateUrl(config.url ?? process.env.MISSKEY_URL ?? '');
|
||||||
const version = meta.version;
|
const version = meta.version;
|
||||||
const host = url.host;
|
const host = url.host;
|
||||||
const hostname = url.hostname;
|
const hostname = url.hostname;
|
||||||
const scheme = url.protocol.replace(/:$/, '');
|
const scheme = url.protocol.replace(/:$/, '');
|
||||||
const wsScheme = scheme.replace('http', 'ws');
|
const wsScheme = scheme.replace('http', 'ws');
|
||||||
|
|
||||||
|
const dbDb = config.db.db ?? process.env.DATABASE_DB ?? '';
|
||||||
|
const dbUser = config.db.user ?? process.env.DATABASE_USER ?? '';
|
||||||
|
const dbPass = config.db.pass ?? process.env.DATABASE_PASSWORD ?? '';
|
||||||
|
|
||||||
const externalMediaProxy = config.mediaProxy ?
|
const externalMediaProxy = config.mediaProxy ?
|
||||||
config.mediaProxy.endsWith('/') ? config.mediaProxy.substring(0, config.mediaProxy.length - 1) : config.mediaProxy
|
config.mediaProxy.endsWith('/') ? config.mediaProxy.substring(0, config.mediaProxy.length - 1) : config.mediaProxy
|
||||||
: null;
|
: null;
|
||||||
@@ -226,7 +235,7 @@ export function loadConfig(): Config {
|
|||||||
apiUrl: `${scheme}://${host}/api`,
|
apiUrl: `${scheme}://${host}/api`,
|
||||||
authUrl: `${scheme}://${host}/auth`,
|
authUrl: `${scheme}://${host}/auth`,
|
||||||
driveUrl: `${scheme}://${host}/files`,
|
driveUrl: `${scheme}://${host}/files`,
|
||||||
db: config.db,
|
db: { ...config.db, db: dbDb, user: dbUser, pass: dbPass },
|
||||||
dbReplications: config.dbReplications,
|
dbReplications: config.dbReplications,
|
||||||
dbSlaves: config.dbSlaves,
|
dbSlaves: config.dbSlaves,
|
||||||
meilisearch: config.meilisearch,
|
meilisearch: config.meilisearch,
|
||||||
@@ -234,6 +243,8 @@ export function loadConfig(): Config {
|
|||||||
redisForPubsub: config.redisForPubsub ? convertRedisOptions(config.redisForPubsub, host) : redis,
|
redisForPubsub: config.redisForPubsub ? convertRedisOptions(config.redisForPubsub, host) : redis,
|
||||||
redisForJobQueue: config.redisForJobQueue ? convertRedisOptions(config.redisForJobQueue, host) : redis,
|
redisForJobQueue: config.redisForJobQueue ? convertRedisOptions(config.redisForJobQueue, host) : redis,
|
||||||
redisForTimelines: config.redisForTimelines ? convertRedisOptions(config.redisForTimelines, host) : redis,
|
redisForTimelines: config.redisForTimelines ? convertRedisOptions(config.redisForTimelines, host) : redis,
|
||||||
|
sentryForBackend: config.sentryForBackend,
|
||||||
|
sentryForFrontend: config.sentryForFrontend,
|
||||||
id: config.id,
|
id: config.id,
|
||||||
proxy: config.proxy,
|
proxy: config.proxy,
|
||||||
proxySmtp: config.proxySmtp,
|
proxySmtp: config.proxySmtp,
|
||||||
@@ -252,7 +263,7 @@ export function loadConfig(): Config {
|
|||||||
deliverJobMaxAttempts: config.deliverJobMaxAttempts,
|
deliverJobMaxAttempts: config.deliverJobMaxAttempts,
|
||||||
inboxJobMaxAttempts: config.inboxJobMaxAttempts,
|
inboxJobMaxAttempts: config.inboxJobMaxAttempts,
|
||||||
proxyRemoteFiles: config.proxyRemoteFiles,
|
proxyRemoteFiles: config.proxyRemoteFiles,
|
||||||
signToActivityPubGet: config.signToActivityPubGet,
|
signToActivityPubGet: config.signToActivityPubGet ?? true,
|
||||||
mediaProxy: externalMediaProxy ?? internalMediaProxy,
|
mediaProxy: externalMediaProxy ?? internalMediaProxy,
|
||||||
externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy,
|
externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy,
|
||||||
videoThumbnailGenerator: config.videoThumbnailGenerator ?
|
videoThumbnailGenerator: config.videoThumbnailGenerator ?
|
||||||
|
|||||||
@@ -3,11 +3,17 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// dummy
|
||||||
export const MAX_NOTE_TEXT_LENGTH = 3000;
|
export const MAX_NOTE_TEXT_LENGTH = 3000;
|
||||||
|
|
||||||
export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
|
export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
|
||||||
export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
|
export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
|
||||||
|
|
||||||
|
export const REMOTE_USER_CACHE_TTL = 1000 * 60 * 60 * 3; // 3hours
|
||||||
|
export const REMOTE_USER_MOVE_COOLDOWN = 1000 * 60 * 60 * 24 * 14; // 14days
|
||||||
|
|
||||||
|
export const REMOTE_SERVER_CACHE_TTL = 1000 * 60 * 60 * 3; // 3hours
|
||||||
|
|
||||||
//#region hard limits
|
//#region hard limits
|
||||||
// If you change DB_* values, you must also change the DB schema.
|
// If you change DB_* values, you must also change the DB schema.
|
||||||
|
|
||||||
|
|||||||
405
packages/backend/src/core/AbuseReportNotificationService.ts
Normal file
405
packages/backend/src/core/AbuseReportNotificationService.ts
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable, type OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
import { Brackets, In, IsNull, Not } from 'typeorm';
|
||||||
|
import * as Redis from 'ioredis';
|
||||||
|
import sanitizeHtml from 'sanitize-html';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { GlobalEvents, GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
|
import type {
|
||||||
|
AbuseReportNotificationRecipientRepository,
|
||||||
|
MiAbuseReportNotificationRecipient,
|
||||||
|
MiAbuseUserReport,
|
||||||
|
MiUser,
|
||||||
|
} from '@/models/_.js';
|
||||||
|
import { EmailService } from '@/core/EmailService.js';
|
||||||
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
import { RecipientMethod } from '@/models/AbuseReportNotificationRecipient.js';
|
||||||
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
|
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||||
|
import { IdService } from './IdService.js';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AbuseReportNotificationService implements OnApplicationShutdown {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.abuseReportNotificationRecipientRepository)
|
||||||
|
private abuseReportNotificationRecipientRepository: AbuseReportNotificationRecipientRepository,
|
||||||
|
@Inject(DI.redisForSub)
|
||||||
|
private redisForSub: Redis.Redis,
|
||||||
|
private idService: IdService,
|
||||||
|
private roleService: RoleService,
|
||||||
|
private systemWebhookService: SystemWebhookService,
|
||||||
|
private emailService: EmailService,
|
||||||
|
private metaService: MetaService,
|
||||||
|
private moderationLogService: ModerationLogService,
|
||||||
|
private globalEventService: GlobalEventService,
|
||||||
|
) {
|
||||||
|
this.redisForSub.on('message', this.onMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理者用Redisイベントを用いて{@link abuseReports}の内容を管理者各位に通知する.
|
||||||
|
* 通知先ユーザは{@link RoleService.getModeratorIds}の取得結果に依る.
|
||||||
|
*
|
||||||
|
* @see RoleService.getModeratorIds
|
||||||
|
* @see GlobalEventService.publishAdminStream
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async notifyAdminStream(abuseReports: MiAbuseUserReport[]) {
|
||||||
|
if (abuseReports.length <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moderatorIds = await this.roleService.getModeratorIds(true, true);
|
||||||
|
|
||||||
|
for (const moderatorId of moderatorIds) {
|
||||||
|
for (const abuseReport of abuseReports) {
|
||||||
|
this.globalEventService.publishAdminStream(
|
||||||
|
moderatorId,
|
||||||
|
'newAbuseUserReport',
|
||||||
|
{
|
||||||
|
id: abuseReport.id,
|
||||||
|
targetUserId: abuseReport.targetUserId,
|
||||||
|
reporterId: abuseReport.reporterId,
|
||||||
|
comment: abuseReport.comment,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mailを用いて{@link abuseReports}の内容を管理者各位に通知する.
|
||||||
|
* メールアドレスの送信先は以下の通り.
|
||||||
|
* - モデレータ権限所有者ユーザ(設定画面からメールアドレスの設定を行っているユーザに限る)
|
||||||
|
* - metaテーブルに設定されているメールアドレス
|
||||||
|
*
|
||||||
|
* @see EmailService.sendEmail
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async notifyMail(abuseReports: MiAbuseUserReport[]) {
|
||||||
|
if (abuseReports.length <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipientEMailAddresses = await this.fetchEMailRecipients().then(it => it
|
||||||
|
.filter(it => it.isActive && it.userProfile?.emailVerified)
|
||||||
|
.map(it => it.userProfile?.email)
|
||||||
|
.filter(x => x != null),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 送信先の鮮度を保つため、毎回取得する
|
||||||
|
const meta = await this.metaService.fetch(true);
|
||||||
|
recipientEMailAddresses.push(
|
||||||
|
...(meta.email ? [meta.email] : []),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (recipientEMailAddresses.length <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const mailAddress of recipientEMailAddresses) {
|
||||||
|
await Promise.all(
|
||||||
|
abuseReports.map(it => {
|
||||||
|
// TODO: 送信処理はJobQueue化したい
|
||||||
|
return this.emailService.sendEmail(
|
||||||
|
mailAddress,
|
||||||
|
'New Abuse Report',
|
||||||
|
sanitizeHtml(it.comment),
|
||||||
|
sanitizeHtml(it.comment),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SystemWebhookを用いて{@link abuseReports}の内容を管理者各位に通知する.
|
||||||
|
* ここではJobQueueへのエンキューのみを行うため、即時実行されない.
|
||||||
|
*
|
||||||
|
* @see SystemWebhookService.enqueueSystemWebhook
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async notifySystemWebhook(
|
||||||
|
abuseReports: MiAbuseUserReport[],
|
||||||
|
type: 'abuseReport' | 'abuseReportResolved',
|
||||||
|
) {
|
||||||
|
if (abuseReports.length <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipientWebhookIds = await this.fetchWebhookRecipients()
|
||||||
|
.then(it => it
|
||||||
|
.filter(it => it.isActive && it.systemWebhookId && it.method === 'webhook')
|
||||||
|
.map(it => it.systemWebhookId)
|
||||||
|
.filter(x => x != null));
|
||||||
|
for (const webhookId of recipientWebhookIds) {
|
||||||
|
await Promise.all(
|
||||||
|
abuseReports.map(it => {
|
||||||
|
return this.systemWebhookService.enqueueSystemWebhook(
|
||||||
|
webhookId,
|
||||||
|
type,
|
||||||
|
it,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通報の通知先一覧を取得する.
|
||||||
|
*
|
||||||
|
* @param {Object} [params] クエリの取得条件
|
||||||
|
* @param {Object} [params.method] 取得する通知先の通知方法
|
||||||
|
* @param {Object} [opts] 動作時の詳細なオプション
|
||||||
|
* @param {boolean} [opts.removeUnauthorized] 副作用としてモデレータ権限を持たない送信先ユーザをDBから削除するかどうか(default: true)
|
||||||
|
* @param {boolean} [opts.joinUser] 通知先のユーザ情報をJOINするかどうか(default: false)
|
||||||
|
* @param {boolean} [opts.joinSystemWebhook] 通知先のSystemWebhook情報をJOINするかどうか(default: false)
|
||||||
|
* @see removeUnauthorizedRecipientUsers
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async fetchRecipients(
|
||||||
|
params?: {
|
||||||
|
ids?: MiAbuseReportNotificationRecipient['id'][],
|
||||||
|
method?: RecipientMethod[],
|
||||||
|
},
|
||||||
|
opts?: {
|
||||||
|
removeUnauthorized?: boolean,
|
||||||
|
joinUser?: boolean,
|
||||||
|
joinSystemWebhook?: boolean,
|
||||||
|
},
|
||||||
|
): Promise<MiAbuseReportNotificationRecipient[]> {
|
||||||
|
const query = this.abuseReportNotificationRecipientRepository.createQueryBuilder('recipient');
|
||||||
|
|
||||||
|
if (opts?.joinUser) {
|
||||||
|
query.innerJoinAndSelect('user', 'user', 'recipient.userId = user.id');
|
||||||
|
query.innerJoinAndSelect('recipient.userProfile', 'userProfile');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts?.joinSystemWebhook) {
|
||||||
|
query.innerJoinAndSelect('recipient.systemWebhook', 'systemWebhook');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params?.ids) {
|
||||||
|
query.andWhere({ id: In(params.ids) });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params?.method) {
|
||||||
|
query.andWhere(new Brackets(qb => {
|
||||||
|
if (params.method?.includes('email')) {
|
||||||
|
qb.orWhere({ method: 'email', userId: Not(IsNull()) });
|
||||||
|
}
|
||||||
|
if (params.method?.includes('webhook')) {
|
||||||
|
qb.orWhere({ method: 'webhook', userId: IsNull() });
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipients = await query.getMany();
|
||||||
|
if (recipients.length <= 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// アサイン有効期限切れはイベントで拾えないので、このタイミングでチェック及び削除(オプション)
|
||||||
|
return (opts?.removeUnauthorized ?? true)
|
||||||
|
? await this.removeUnauthorizedRecipientUsers(recipients)
|
||||||
|
: recipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EMailの通知先一覧を取得する.
|
||||||
|
* リレーション先の{@link MiUser}および{@link MiUserProfile}も同時に取得する.
|
||||||
|
*
|
||||||
|
* @param {Object} [opts]
|
||||||
|
* @param {boolean} [opts.removeUnauthorized] 副作用としてモデレータ権限を持たない送信先ユーザをDBから削除するかどうか(default: true)
|
||||||
|
* @see removeUnauthorizedRecipientUsers
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async fetchEMailRecipients(opts?: {
|
||||||
|
removeUnauthorized?: boolean
|
||||||
|
}): Promise<MiAbuseReportNotificationRecipient[]> {
|
||||||
|
return this.fetchRecipients({ method: ['email'] }, { joinUser: true, ...opts });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webhookの通知先一覧を取得する.
|
||||||
|
* リレーション先の{@link MiSystemWebhook}も同時に取得する.
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public fetchWebhookRecipients(): Promise<MiAbuseReportNotificationRecipient[]> {
|
||||||
|
return this.fetchRecipients({ method: ['webhook'] }, { joinSystemWebhook: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知先を作成する.
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async createRecipient(
|
||||||
|
params: {
|
||||||
|
isActive: MiAbuseReportNotificationRecipient['isActive'];
|
||||||
|
name: MiAbuseReportNotificationRecipient['name'];
|
||||||
|
method: MiAbuseReportNotificationRecipient['method'];
|
||||||
|
userId: MiAbuseReportNotificationRecipient['userId'];
|
||||||
|
systemWebhookId: MiAbuseReportNotificationRecipient['systemWebhookId'];
|
||||||
|
},
|
||||||
|
updater: MiUser,
|
||||||
|
): Promise<MiAbuseReportNotificationRecipient> {
|
||||||
|
const id = this.idService.gen();
|
||||||
|
await this.abuseReportNotificationRecipientRepository.insert({
|
||||||
|
...params,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const created = await this.abuseReportNotificationRecipientRepository.findOneByOrFail({ id: id });
|
||||||
|
|
||||||
|
this.moderationLogService
|
||||||
|
.log(updater, 'createAbuseReportNotificationRecipient', {
|
||||||
|
recipientId: id,
|
||||||
|
recipient: created,
|
||||||
|
})
|
||||||
|
.then();
|
||||||
|
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知先を更新する.
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async updateRecipient(
|
||||||
|
params: {
|
||||||
|
id: MiAbuseReportNotificationRecipient['id'];
|
||||||
|
isActive: MiAbuseReportNotificationRecipient['isActive'];
|
||||||
|
name: MiAbuseReportNotificationRecipient['name'];
|
||||||
|
method: MiAbuseReportNotificationRecipient['method'];
|
||||||
|
userId: MiAbuseReportNotificationRecipient['userId'];
|
||||||
|
systemWebhookId: MiAbuseReportNotificationRecipient['systemWebhookId'];
|
||||||
|
},
|
||||||
|
updater: MiUser,
|
||||||
|
): Promise<MiAbuseReportNotificationRecipient> {
|
||||||
|
const beforeEntity = await this.abuseReportNotificationRecipientRepository.findOneByOrFail({ id: params.id });
|
||||||
|
|
||||||
|
await this.abuseReportNotificationRecipientRepository.update(params.id, {
|
||||||
|
isActive: params.isActive,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
name: params.name,
|
||||||
|
method: params.method,
|
||||||
|
userId: params.userId,
|
||||||
|
systemWebhookId: params.systemWebhookId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const afterEntity = await this.abuseReportNotificationRecipientRepository.findOneByOrFail({ id: params.id });
|
||||||
|
|
||||||
|
this.moderationLogService
|
||||||
|
.log(updater, 'updateAbuseReportNotificationRecipient', {
|
||||||
|
recipientId: params.id,
|
||||||
|
before: beforeEntity,
|
||||||
|
after: afterEntity,
|
||||||
|
})
|
||||||
|
.then();
|
||||||
|
|
||||||
|
return afterEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知先を削除する.
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async deleteRecipient(
|
||||||
|
id: MiAbuseReportNotificationRecipient['id'],
|
||||||
|
updater: MiUser,
|
||||||
|
) {
|
||||||
|
const entity = await this.abuseReportNotificationRecipientRepository.findBy({ id });
|
||||||
|
|
||||||
|
await this.abuseReportNotificationRecipientRepository.delete(id);
|
||||||
|
|
||||||
|
this.moderationLogService
|
||||||
|
.log(updater, 'deleteAbuseReportNotificationRecipient', {
|
||||||
|
recipientId: id,
|
||||||
|
recipient: entity,
|
||||||
|
})
|
||||||
|
.then();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデレータ権限を持たない(*1)通知先ユーザを削除する.
|
||||||
|
*
|
||||||
|
* *1: 以下の両方を満たすものの事を言う
|
||||||
|
* - 通知先にユーザIDが設定されている
|
||||||
|
* - 付与ロールにモデレータ権限がない or アサインの有効期限が切れている
|
||||||
|
*
|
||||||
|
* @param recipients 通知先一覧の配列
|
||||||
|
* @returns {@lisk recipients}からモデレータ権限を持たない通知先を削除した配列
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
private async removeUnauthorizedRecipientUsers(recipients: MiAbuseReportNotificationRecipient[]): Promise<MiAbuseReportNotificationRecipient[]> {
|
||||||
|
const userRecipients = recipients.filter(it => it.userId !== null);
|
||||||
|
const recipientUserIds = new Set(userRecipients.map(it => it.userId).filter(x => x != null));
|
||||||
|
if (recipientUserIds.size <= 0) {
|
||||||
|
// ユーザが通知先として設定されていない場合、この関数での処理を行うべきレコードが無い
|
||||||
|
return recipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
// モデレータ権限の有無で通知先設定を振り分ける
|
||||||
|
const authorizedUserIds = await this.roleService.getModeratorIds(true, true);
|
||||||
|
const authorizedUserRecipients = Array.of<MiAbuseReportNotificationRecipient>();
|
||||||
|
const unauthorizedUserRecipients = Array.of<MiAbuseReportNotificationRecipient>();
|
||||||
|
for (const recipient of userRecipients) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
if (authorizedUserIds.includes(recipient.userId!)) {
|
||||||
|
authorizedUserRecipients.push(recipient);
|
||||||
|
} else {
|
||||||
|
unauthorizedUserRecipients.push(recipient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// モデレータ権限を持たない通知先をDBから削除する
|
||||||
|
if (unauthorizedUserRecipients.length > 0) {
|
||||||
|
await this.abuseReportNotificationRecipientRepository.delete(unauthorizedUserRecipients.map(it => it.id));
|
||||||
|
}
|
||||||
|
const nonUserRecipients = recipients.filter(it => it.userId === null);
|
||||||
|
return [...nonUserRecipients, ...authorizedUserRecipients].sort((a, b) => a.id.localeCompare(b.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async onMessage(_: string, data: string): Promise<void> {
|
||||||
|
const obj = JSON.parse(data);
|
||||||
|
if (obj.channel !== 'internal') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { type } = obj.message as GlobalEvents['internal']['payload'];
|
||||||
|
switch (type) {
|
||||||
|
case 'roleUpdated':
|
||||||
|
case 'roleDeleted':
|
||||||
|
case 'userRoleUnassigned': {
|
||||||
|
// 場合によってはキャッシュ更新よりも先にここが呼ばれてしまう可能性があるのでnextTickで遅延実行
|
||||||
|
process.nextTick(async () => {
|
||||||
|
const recipients = await this.abuseReportNotificationRecipientRepository.findBy({
|
||||||
|
userId: Not(IsNull()),
|
||||||
|
});
|
||||||
|
await this.removeUnauthorizedRecipientUsers(recipients);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public dispose(): void {
|
||||||
|
this.redisForSub.off('message', this.onMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public onApplicationShutdown(signal?: string | undefined): void {
|
||||||
|
this.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
128
packages/backend/src/core/AbuseReportService.ts
Normal file
128
packages/backend/src/core/AbuseReportService.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { In } from 'typeorm';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import type { AbuseUserReportsRepository, MiAbuseUserReport, MiUser, UsersRepository } from '@/models/_.js';
|
||||||
|
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
|
||||||
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
|
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
||||||
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
|
import { IdService } from './IdService.js';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AbuseReportService {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.abuseUserReportsRepository)
|
||||||
|
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
||||||
|
@Inject(DI.usersRepository)
|
||||||
|
private usersRepository: UsersRepository,
|
||||||
|
private idService: IdService,
|
||||||
|
private abuseReportNotificationService: AbuseReportNotificationService,
|
||||||
|
private queueService: QueueService,
|
||||||
|
private instanceActorService: InstanceActorService,
|
||||||
|
private apRendererService: ApRendererService,
|
||||||
|
private moderationLogService: ModerationLogService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザからの通報をDBに記録し、その内容を下記の手段で管理者各位に通知する.
|
||||||
|
* - 管理者用Redisイベント
|
||||||
|
* - EMail(モデレータ権限所有者ユーザ+metaテーブルに設定されているメールアドレス)
|
||||||
|
* - SystemWebhook
|
||||||
|
*
|
||||||
|
* @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
|
||||||
|
* @see AbuseReportNotificationService.notify
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async report(params: {
|
||||||
|
targetUserId: MiAbuseUserReport['targetUserId'],
|
||||||
|
targetUserHost: MiAbuseUserReport['targetUserHost'],
|
||||||
|
reporterId: MiAbuseUserReport['reporterId'],
|
||||||
|
reporterHost: MiAbuseUserReport['reporterHost'],
|
||||||
|
comment: string,
|
||||||
|
}[]) {
|
||||||
|
const entities = params.map(param => {
|
||||||
|
return {
|
||||||
|
id: this.idService.gen(),
|
||||||
|
targetUserId: param.targetUserId,
|
||||||
|
targetUserHost: param.targetUserHost,
|
||||||
|
reporterId: param.reporterId,
|
||||||
|
reporterHost: param.reporterHost,
|
||||||
|
comment: param.comment,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const reports = Array.of<MiAbuseUserReport>();
|
||||||
|
for (const entity of entities) {
|
||||||
|
const report = await this.abuseUserReportsRepository.insertOne(entity);
|
||||||
|
reports.push(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
this.abuseReportNotificationService.notifyAdminStream(reports),
|
||||||
|
this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReport'),
|
||||||
|
this.abuseReportNotificationService.notifyMail(reports),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通報を解決し、その内容を下記の手段で管理者各位に通知する.
|
||||||
|
* - SystemWebhook
|
||||||
|
*
|
||||||
|
* @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
|
||||||
|
* @param operator 通報を処理したユーザ
|
||||||
|
* @see AbuseReportNotificationService.notify
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async resolve(
|
||||||
|
params: {
|
||||||
|
reportId: string;
|
||||||
|
forward: boolean;
|
||||||
|
}[],
|
||||||
|
operator: MiUser,
|
||||||
|
) {
|
||||||
|
const paramsMap = new Map(params.map(it => [it.reportId, it]));
|
||||||
|
const reports = await this.abuseUserReportsRepository.findBy({
|
||||||
|
id: In(params.map(it => it.reportId)),
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const report of reports) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
const ps = paramsMap.get(report.id)!;
|
||||||
|
|
||||||
|
await this.abuseUserReportsRepository.update(report.id, {
|
||||||
|
resolved: true,
|
||||||
|
assigneeId: operator.id,
|
||||||
|
forwarded: ps.forward && report.targetUserHost !== null,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ps.forward && report.targetUserHost != null) {
|
||||||
|
const actor = await this.instanceActorService.getInstanceActor();
|
||||||
|
const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);
|
||||||
|
const contextAssignedFlag = this.apRendererService.addContext(flag);
|
||||||
|
this.queueService.deliver(actor, contextAssignedFlag, targetUser.inbox, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.moderationLogService
|
||||||
|
.log(operator, 'resolveAbuseReport', {
|
||||||
|
reportId: report.id,
|
||||||
|
report: report,
|
||||||
|
forwarded: ps.forward && report.targetUserHost !== null,
|
||||||
|
})
|
||||||
|
.then();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.abuseUserReportsRepository.findBy({ id: In(reports.map(it => it.id)) })
|
||||||
|
.then(reports => this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReportResolved'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
|
||||||
|
import { ModuleRef } from '@nestjs/core';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { UsersRepository } from '@/models/_.js';
|
import type { UsersRepository } from '@/models/_.js';
|
||||||
import type { MiUser } from '@/models/User.js';
|
import type { MiUser } from '@/models/User.js';
|
||||||
@@ -12,30 +13,44 @@ import { RelayService } from '@/core/RelayService.js';
|
|||||||
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import type { PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AccountUpdateService {
|
export class AccountUpdateService implements OnModuleInit {
|
||||||
|
private apDeliverManagerService: ApDeliverManagerService;
|
||||||
constructor(
|
constructor(
|
||||||
|
private moduleRef: ModuleRef,
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private apRendererService: ApRendererService,
|
private apRendererService: ApRendererService,
|
||||||
private apDeliverManagerService: ApDeliverManagerService,
|
|
||||||
private relayService: RelayService,
|
private relayService: RelayService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onModuleInit() {
|
||||||
|
this.apDeliverManagerService = this.moduleRef.get(ApDeliverManagerService.name);
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async publishToFollowers(userId: MiUser['id']) {
|
/**
|
||||||
|
* Deliver account update to followers
|
||||||
|
* @param userId user id
|
||||||
|
* @param deliverKey optional. Private key to sign the deliver.
|
||||||
|
*/
|
||||||
|
public async publishToFollowers(userId: MiUser['id'], deliverKey?: PrivateKeyWithPem) {
|
||||||
const user = await this.usersRepository.findOneBy({ id: userId });
|
const user = await this.usersRepository.findOneBy({ id: userId });
|
||||||
if (user == null) throw new Error('user not found');
|
if (user == null) throw new Error('user not found');
|
||||||
|
|
||||||
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
|
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user));
|
const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user));
|
||||||
this.apDeliverManagerService.deliverToFollowers(user, content);
|
await Promise.allSettled([
|
||||||
this.relayService.deliverToRelays(user, content);
|
this.apDeliverManagerService.deliverToFollowers(user, content, deliverKey),
|
||||||
|
this.relayService.deliverToRelays(user, content, deliverKey),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Brackets } from 'typeorm';
|
import { Brackets, EntityNotFoundError } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MiUser } from '@/models/User.js';
|
import type { MiUser } from '@/models/User.js';
|
||||||
import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead, UsersRepository } from '@/models/_.js';
|
import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead, UsersRepository } from '@/models/_.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { Packed } from '@/misc/json-schema.js';
|
import { Packed } from '@/misc/json-schema.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ export class AnnouncementService {
|
|||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
private moderationLogService: ModerationLogService,
|
private moderationLogService: ModerationLogService,
|
||||||
|
private announcementEntityService: AnnouncementEntityService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +67,7 @@ export class AnnouncementService {
|
|||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async create(values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
|
public async create(values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
|
||||||
const announcement = await this.announcementsRepository.insert({
|
const announcement = await this.announcementsRepository.insertOne({
|
||||||
id: this.idService.gen(),
|
id: this.idService.gen(),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
title: values.title,
|
title: values.title,
|
||||||
@@ -77,9 +79,9 @@ export class AnnouncementService {
|
|||||||
silence: values.silence,
|
silence: values.silence,
|
||||||
needConfirmationToRead: values.needConfirmationToRead,
|
needConfirmationToRead: values.needConfirmationToRead,
|
||||||
userId: values.userId,
|
userId: values.userId,
|
||||||
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
|
});
|
||||||
|
|
||||||
const packed = (await this.packMany([announcement]))[0];
|
const packed = await this.announcementEntityService.pack(announcement);
|
||||||
|
|
||||||
if (values.userId) {
|
if (values.userId) {
|
||||||
this.globalEventService.publishMainStream(values.userId, 'announcementCreated', {
|
this.globalEventService.publishMainStream(values.userId, 'announcementCreated', {
|
||||||
@@ -177,6 +179,24 @@ export class AnnouncementService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async getAnnouncement(announcementId: MiAnnouncement['id'], me: MiUser | null): Promise<Packed<'Announcement'>> {
|
||||||
|
const announcement = await this.announcementsRepository.findOneByOrFail({ id: announcementId });
|
||||||
|
if (me) {
|
||||||
|
if (announcement.userId && announcement.userId !== me.id) {
|
||||||
|
throw new EntityNotFoundError(this.announcementsRepository.metadata.target, { id: announcementId });
|
||||||
|
}
|
||||||
|
|
||||||
|
const read = await this.announcementReadsRepository.findOneBy({
|
||||||
|
announcementId: announcement.id,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
return this.announcementEntityService.pack({ ...announcement, isRead: read !== null }, me);
|
||||||
|
} else {
|
||||||
|
return this.announcementEntityService.pack(announcement, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> {
|
public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@@ -193,29 +213,4 @@ export class AnnouncementService {
|
|||||||
this.globalEventService.publishMainStream(user.id, 'readAllAnnouncements');
|
this.globalEventService.publishMainStream(user.id, 'readAllAnnouncements');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async packMany(
|
|
||||||
announcements: MiAnnouncement[],
|
|
||||||
me?: { id: MiUser['id'] } | null | undefined,
|
|
||||||
options?: {
|
|
||||||
reads?: MiAnnouncementRead[];
|
|
||||||
},
|
|
||||||
): Promise<Packed<'Announcement'>[]> {
|
|
||||||
const reads = me ? (options?.reads ?? await this.getReads(me.id)) : [];
|
|
||||||
return announcements.map(announcement => ({
|
|
||||||
id: announcement.id,
|
|
||||||
createdAt: this.idService.parse(announcement.id).date.toISOString(),
|
|
||||||
updatedAt: announcement.updatedAt?.toISOString() ?? null,
|
|
||||||
text: announcement.text,
|
|
||||||
title: announcement.title,
|
|
||||||
imageUrl: announcement.imageUrl,
|
|
||||||
icon: announcement.icon,
|
|
||||||
display: announcement.display,
|
|
||||||
needConfirmationToRead: announcement.needConfirmationToRead,
|
|
||||||
silence: announcement.silence,
|
|
||||||
forYou: announcement.userId === me?.id,
|
|
||||||
isRead: reads.some(read => read.announcementId === announcement.id),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user