Compare commits
113 commits
v13.0.0-pr
...
main
Author | SHA1 | Date | |
---|---|---|---|
Johann150 | 6c7f1774e3 | ||
Johann150 | af43df15ca | ||
Johann150 | 5f83383ab8 | ||
Johann150 | 8c759dde6c | ||
Johann150 | 84d83d908a | ||
Johann150 | 16d091497a | ||
Johann150 | ef53ec276a | ||
Johann150 | 3582fd8260 | ||
Johann150 | 6256ddbd30 | ||
Johann150 | 00fcc238f7 | ||
Johann150 | 9f1670d5fd | ||
Norm | ff31b8b06d | ||
Johann150 | e317a771b3 | ||
Johann150 | 398ee6435b | ||
Johann150 | ffff2ae5ef | ||
Johann150 | ccc8bf0289 | ||
Johann150 | a231b36d59 | ||
Johann150 | 8e9c65fab0 | ||
Norm | 78a3051313 | ||
Norm | 78717e85d3 | ||
Norm | a9d3cae511 | ||
Norm | bd27b7ca3a | ||
Norm | e28a9eb8e8 | ||
Norm | e5a4c5d2d0 | ||
Norm | 6bba55c196 | ||
Norm | 1d469f3c34 | ||
Norm | 3f0228e14c | ||
Johann150 | 73f81177b4 | ||
Johann150 | 6a26da3516 | ||
Johann150 | 5ea744b1b2 | ||
Johann150 | ae6ba05306 | ||
21069223e3 | |||
Johann150 | d4d1e03479 | ||
Norm | 5513a3eb3a | ||
Johann150 | d5dd7c1ef5 | ||
Johann150 | a80521b6a8 | ||
Norm | 030394b30d | ||
Johann150 | 768d9bbdfb | ||
Johann150 | 3ef1a4b0f9 | ||
Johann150 | ae59ce51b0 | ||
Johann150 | 14a9b9bedd | ||
Johann150 | 985a13f47f | ||
Norm | 3e46433ede | ||
Johann150 | 507b328fdf | ||
Norm | cf7449509f | ||
Norm | f46ba3f700 | ||
Norm | 3cf673960b | ||
Norm | cbfd866122 | ||
Norm | b23a8dbaed | ||
Norm | 80a73a7510 | ||
Norm | 3dec9a47f0 | ||
Norm | b8fb7a38cc | ||
Norm | fdc682e810 | ||
Johann150 | fde751df8f | ||
Johann150 | 1faf1035f9 | ||
Johann150 | e2ce599aca | ||
Johann150 | 73870e85cd | ||
Norm | 350f21d955 | ||
Norm | 873e21f090 | ||
Norm | 2afe54c121 | ||
Johann150 | 501cf834c8 | ||
Norm | b66f7550ab | ||
Johann150 | 18664dbca3 | ||
Johann150 | 0f3f42eb39 | ||
Johann150 | 71b976ec96 | ||
Andy | d3f1ad9a88 | ||
Andy | 1aa3898db5 | ||
Andy | 96c3744555 | ||
Andy | b023741f50 | ||
Andy | 87e1e658f2 | ||
Andy | 7e8d5c3b79 | ||
Andy | c785fbab6e | ||
Andy | 547a1f81d4 | ||
Andy | 95384d0bb2 | ||
Andy | 4cc5b734e7 | ||
Andy | 5d32872999 | ||
Andy | b4b1204f77 | ||
Norm | c1a51547a9 | ||
Chloe Kudryavtsev | 4e74d26e45 | ||
Johann150 | a421dd401c | ||
Johann150 | c4211761e6 | ||
Johann150 | 03b673165f | ||
Johann150 | de18c8306d | ||
Johann150 | 38df8dc734 | ||
Johann150 | 11e4a8cb9b | ||
Johann150 | d1e0d79c19 | ||
Johann150 | 946e862ecd | ||
Johann150 | 97052b1f61 | ||
Johann150 | cda9197700 | ||
Chloe Kudryavtsev | 2dde8273e2 | ||
Johann150 | 7924d5d01b | ||
Johann150 | de927e1f30 | ||
Johann150 | bdcec2b8a7 | ||
Johann150 | 5291f29581 | ||
Johann150 | 15b3ab6d13 | ||
Johann150 | 79e3c20189 | ||
Johann150 | 2f2e6a58a4 | ||
Johann150 | c5568cfdf3 | ||
Johann150 | c65fdebe26 | ||
Johann150 | 418c88bb8f | ||
Johann150 | 2b19b34196 | ||
Johann150 | 7db7fdd9e2 | ||
Johann150 | a13e956af0 | ||
Norm | 18cf228f89 | ||
Norm | bdf2e14a73 | ||
Norm | c5cf167ffa | ||
Norm | e446a11bb7 | ||
Johann150 | 5b6b2b214d | ||
Johann150 | 194fff3603 | ||
Johann150 | b4080d788d | ||
Johann150 | e49b8d0ef3 | ||
Chloe Kudryavtsev | 6600f6e52e | ||
Johann150 | 48a60b03ea |
|
@ -78,8 +78,11 @@ redis:
|
|||
# Whether disable HSTS
|
||||
#disableHsts: true
|
||||
|
||||
# Number of worker processes
|
||||
#clusterLimit: 1
|
||||
# Number of worker processes by type.
|
||||
# The sum must not exceed the number of available cores.
|
||||
#clusterLimits:
|
||||
# web: 1
|
||||
# queue: 1
|
||||
|
||||
# Job concurrency per worker
|
||||
# deliverJobConcurrency: 128
|
||||
|
@ -130,3 +133,10 @@ redis:
|
|||
#allowedPrivateNetworks: [
|
||||
# '127.0.0.1/32'
|
||||
#]
|
||||
|
||||
# images used on error screens. You can use absolute or relative URLs.
|
||||
# If you use relative URLs, be aware that the URL may be used on different pages/paths, so the path component should be absolute.
|
||||
#images:
|
||||
# info: /twemoji/1f440.svg
|
||||
# notFound: /twemoji/2049.svg
|
||||
# error: /twemoji/1f480.svg
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.autogen
|
||||
.vscode
|
||||
.config
|
||||
.woodpecker
|
||||
Dockerfile
|
||||
build/
|
||||
built/
|
||||
|
|
|
@ -2,9 +2,10 @@ root = true
|
|||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
6
.gitattributes
vendored
|
@ -1,7 +1 @@
|
|||
*.svg -diff -text
|
||||
*.psd -diff -text
|
||||
*.ai -diff -text
|
||||
*.mqo -diff -text
|
||||
*.glb -diff -text
|
||||
*.blend -diff -text
|
||||
*.afdesign -diff -text
|
||||
|
|
2
.gitignore
vendored
|
@ -1,6 +1,6 @@
|
|||
# Visual Studio Code
|
||||
/.vscode
|
||||
!/.vscode/extensions.json
|
||||
/.vsls.json
|
||||
|
||||
# Intelij-IDEA
|
||||
/.idea
|
||||
|
|
8
.vscode/extensions.json
vendored
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"editorconfig.editorconfig",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"Vue.volar",
|
||||
"Vue.vscode-typescript-vue-plugin"
|
||||
]
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/vsls",
|
||||
"gitignore": "exclude"
|
||||
}
|
22
.woodpecker/lint-sw.yml
Normal file
|
@ -0,0 +1,22 @@
|
|||
clone:
|
||||
git:
|
||||
image: woodpeckerci/plugin-git
|
||||
settings:
|
||||
depth: 1 # CI does not need commit history
|
||||
recursive: true
|
||||
|
||||
pipeline:
|
||||
install:
|
||||
when:
|
||||
event:
|
||||
- pull_request
|
||||
image: node:18.6.0
|
||||
commands:
|
||||
- yarn install
|
||||
lint:
|
||||
when:
|
||||
event:
|
||||
- pull_request
|
||||
image: node:18.6.0
|
||||
commands:
|
||||
- yarn workspace sw run lint
|
Before Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 317 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 200 KiB |
BIN
assets/ai.png
Before Width: | Height: | Size: 235 KiB |
Before Width: | Height: | Size: 238 KiB |
Before Width: | Height: | Size: 148 KiB |
BIN
assets/title.png
Before Width: | Height: | Size: 3.8 KiB |
42
docs/oauth.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# 3rd party access
|
||||
Foundkey supports:
|
||||
- OAuth 2.0 Authorization Code grant per [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749).
|
||||
- OAuth Bearer Token Usage per [RFC 6750](https://www.rfc-editor.org/rfc/rfc6750).
|
||||
- Proof Key for Code Exchange (PKCE) per [RFC 7636](https://www.rfc-editor.org/rfc/rfc7636).
|
||||
- OAuth 2.0 Authorization Server Metadata per [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414.html).
|
||||
|
||||
# Discovery
|
||||
Because the implementation may change in the future, it is recommended that you use OAuth 2.0 Authorization Server Metadata a.k.a. OpenID Connect Discovery.
|
||||
In short, this means that to discover the URLs for the grant endpoints you should request `/.well-known/oauth-authorization-server`, which is a JSON object.
|
||||
From there, `authorization_endpoint` and `token_endpoint` will probably be most interesting to you.
|
||||
The definitions of all data fields are to be found in [RFC 8414, section 2](https://www.rfc-editor.org/rfc/rfc8414#section-2).
|
||||
|
||||
# App registration
|
||||
Before using the OAuth grant you need to register your application.
|
||||
Currently you will need to use the pre-existing Misskey API to register, though Dynamic Client Registration may be implemented at a later point.
|
||||
(You'd be able to tell from the Authorization Server Metadata, see above.)
|
||||
|
||||
The data you will need to know before registering is the following:
|
||||
- a name for your app,
|
||||
- a short description to be shown to users,
|
||||
- which API permissions you need, and
|
||||
- the callback URL you want to use.
|
||||
|
||||
There can only be 1 callback URL per registration.
|
||||
|
||||
Note that you can specify permissions a 2nd time in the OAuth flow.
|
||||
If you do not provide permissions again in the grant flow, the default is to use all permissions you gave when registering the app.
|
||||
If you do provide permissions in the grant flow, permissions that were not registered will never be granted.
|
||||
A list of available permissions can be viewed on any Foundkey instance by going to the API documentation at `/api-doc`.
|
||||
|
||||
To register your app you need to `POST` to `/api/app/create`.
|
||||
The body of the request must be a JSON object with the following keys:
|
||||
- `name` (string): a name for your app,
|
||||
- `description` (string): a short description to be shown to users,
|
||||
- `permission` (array of permission names) which API permissions you need, and
|
||||
- `callbackUrl` (string): the callback URL you want to use.
|
||||
|
||||
If successful (HTTP response code 200) you will receive back a JSON object containing among other things:
|
||||
- `id` (string): the client ID
|
||||
- `secret` (string): the client secret
|
||||
With these credentials you should be able to use the Authorization Code grant to obtain authorization.
|
|
@ -36,7 +36,7 @@ gulp.task('copy:client:locales', cb => {
|
|||
});
|
||||
|
||||
gulp.task('build:backend:script', () => {
|
||||
return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js'])
|
||||
return gulp.src(['./packages/backend/src/server/web/boot.js'])
|
||||
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
|
||||
.pipe(terser({
|
||||
toplevel: true
|
||||
|
@ -45,7 +45,7 @@ gulp.task('build:backend:script', () => {
|
|||
});
|
||||
|
||||
gulp.task('build:backend:style', () => {
|
||||
return gulp.src(['./packages/backend/src/server/web/style.css', './packages/backend/src/server/web/bios.css', './packages/backend/src/server/web/cli.css'])
|
||||
return gulp.src(['./packages/backend/src/server/web/style.css'])
|
||||
.pipe(cssnano({
|
||||
zindex: false
|
||||
}))
|
||||
|
|
|
@ -293,9 +293,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "الصفحات"
|
||||
integration: "التكامل"
|
||||
connectService: "اتصل"
|
||||
disconnectService: "اقطع الاتصال"
|
||||
enableLocalTimeline: "تفعيل الخيط المحلي"
|
||||
enableGlobalTimeline: "تفعيل الخيط الزمني الشامل"
|
||||
disablingTimelinesInfo: "سيتمكن المديرون والمشرفون من الوصول إلى كل الخيوط الزمنية\
|
||||
|
@ -402,7 +399,6 @@ normalPassword: "الكلمة السرية جيدة"
|
|||
strongPassword: "الكلمة السرية قوية"
|
||||
passwordMatched: "التطابق صحيح!"
|
||||
passwordNotMatched: "غير متطابقتان"
|
||||
signinWith: "الولوج عبر {x}"
|
||||
signinFailed: "فشل الولوج، خطأ في اسم المستخدم أو كلمة المرور."
|
||||
tapSecurityKey: "أنقر مفتاح الأمان"
|
||||
or: "أو"
|
||||
|
|
|
@ -308,9 +308,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "পৃষ্ঠা"
|
||||
integration: "ইন্টিগ্রেশন"
|
||||
connectService: "সংযুক্ত করুন"
|
||||
disconnectService: "সংযোগ বিচ্ছিন্ন করুন"
|
||||
enableLocalTimeline: "স্থানীয় টাইমলাইন চালু করুন"
|
||||
enableGlobalTimeline: "গ্লোবাল টাইমলাইন চালু করুন"
|
||||
disablingTimelinesInfo: "আপনি এই টাইমলাইনগুলি বন্ধ করলেও প্রশাসক এবং মডারেটররা এই\
|
||||
|
@ -417,7 +414,6 @@ normalPassword: "সাধারণ পাসওয়ার্ড"
|
|||
strongPassword: "শক্তিশালী পাসওয়ার্ড"
|
||||
passwordMatched: "মিলেছে"
|
||||
passwordNotMatched: "মিলেনি"
|
||||
signinWith: "{x} এর সাহায্যে সাইন ইন করুন"
|
||||
signinFailed: "লগ ইন করা যায়নি। আপনার ব্যবহারকারীর নাম এবং পাসওয়ার্ড চেক করুন."
|
||||
tapSecurityKey: "সিকিউরিটি কী স্পর্শ করুন"
|
||||
or: "অথবা"
|
||||
|
|
|
@ -277,9 +277,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Stránky"
|
||||
integration: "Integrace"
|
||||
connectService: "Připojit"
|
||||
disconnectService: "Odpojit"
|
||||
enableLocalTimeline: "Povolit lokální čas"
|
||||
enableGlobalTimeline: "Povolit globální čas"
|
||||
enableRegistration: "Povolit registraci novým uživatelům"
|
||||
|
@ -353,7 +350,6 @@ normalPassword: "Dobré heslo"
|
|||
strongPassword: "Silné heslo"
|
||||
passwordMatched: "Hesla se schodují"
|
||||
passwordNotMatched: "Hesla se neschodují"
|
||||
signinWith: "Přihlásit se s {x}"
|
||||
signinFailed: "Nelze se přihlásit. Zkontrolujte prosím své uživatelské jméno a heslo."
|
||||
or: "Nebo"
|
||||
language: "Jazyk"
|
||||
|
|
|
@ -193,7 +193,9 @@ clearCachedFilesConfirm: "Sollen alle im Cache gespeicherten Dateien von anderen
|
|||
blockedInstances: "Blockierte Instanzen"
|
||||
blockedInstancesDescription: "Gib die Hostnamen der Instanzen, welche blockiert werden\
|
||||
\ sollen, durch Zeilenumbrüche getrennt an. Blockierte Instanzen können mit dieser\
|
||||
\ instanz nicht mehr kommunizieren."
|
||||
\ Instanz nicht mehr kommunizieren. Hostnamen, die nicht-ASCII-Zeichen enthalten,\
|
||||
\ müssen zuvor in Punycode umgewandelt werden. Ein Asterisk (*) kann als Platzhalter\
|
||||
\ für beliebig viele Zeichen verwendet werden."
|
||||
muteAndBlock: "Stummschaltungen und Blockierungen"
|
||||
mutedUsers: "Stummgeschaltete Benutzer"
|
||||
blockedUsers: "Blockierte Benutzer"
|
||||
|
@ -319,9 +321,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Seiten"
|
||||
integration: "Integration"
|
||||
connectService: "Verbinden"
|
||||
disconnectService: "Trennen"
|
||||
enableLocalTimeline: "Lokale Chronik aktivieren"
|
||||
enableGlobalTimeline: "Globale Chronik aktivieren"
|
||||
disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle\
|
||||
|
@ -431,7 +430,6 @@ normalPassword: "Durchschnittliches Passwort"
|
|||
strongPassword: "Starkes Passwort"
|
||||
passwordMatched: "Stimmt überein"
|
||||
passwordNotMatched: "Stimmt nicht überein"
|
||||
signinWith: "Mit {x} anmelden"
|
||||
signinFailed: "Anmeldung fehlgeschlagen. Überprüfe Benutzername und Passswort."
|
||||
tapSecurityKey: "Tippe deinen Sicherheitsschlüssel an"
|
||||
or: "Oder"
|
||||
|
@ -1371,18 +1369,6 @@ recommended: "Empfehlung"
|
|||
check: "Check"
|
||||
maxCustomEmojiPicker: Maximale Anzahl vorgeschlagener benutzerdefinierter Emoji
|
||||
maxUnicodeEmojiPicker: Maximale Anzahl vorgeschlagener Unicode-Emoji
|
||||
_services:
|
||||
_discord:
|
||||
connected: 'Discord: @{username}#{discriminator} wurde mit Foundkey-Account @{mkUsername}
|
||||
verknüpft!'
|
||||
disconnected: Discord-Verknüpfung wurde entfernt.
|
||||
_twitter:
|
||||
connected: Twitter-Account @{twitterUserName} wurde mit Foundkey-Account @{userName}
|
||||
verknüpft!
|
||||
disconnected: Twitter-Verknüpfung wurde entfernt.
|
||||
_github:
|
||||
connected: GitHub-Account @{login} wurde mit Foundkey-Account @{userName} verknüpft!
|
||||
disconnected: GitHub-Verknüpfung wurde entfernt.
|
||||
documentation: Dokumentation
|
||||
signinHistoryExpires: Frühere Login-Versuche werden aus Datenschutzgründen nach 60
|
||||
Tagen automatisch gelöscht.
|
||||
|
|
|
@ -96,6 +96,8 @@ unfollow: "Unfollow"
|
|||
followRequestPending: "Follow request pending"
|
||||
renote: "Renote"
|
||||
unrenote: "Take back renote"
|
||||
unrenoteAll: "Take back all renotes"
|
||||
unrenoteAllConfirm: "Are you sure that you want to take back all renotes of this note?"
|
||||
quote: "Quote"
|
||||
pinnedNote: "Pinned note"
|
||||
you: "You"
|
||||
|
@ -187,7 +189,7 @@ clearCachedFiles: "Clear cache"
|
|||
clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?"
|
||||
blockedInstances: "Blocked Instances"
|
||||
blockedInstancesDescription: "List the hostnames of the instances that you want to\
|
||||
\ block. Listed instances will no longer be able to communicate with this instance. Non-ASCII domain names must be encoded in punycode. You can use an asterisk (*) as a placeholder for zero or more character(s)."
|
||||
\ block. Listed instances will no longer be able to communicate with this instance. Non-ASCII domain names must be encoded in punycode. Subdomains of the listed instances will also be blocked."
|
||||
muteAndBlock: "Mutes and Blocks"
|
||||
mutedUsers: "Muted users"
|
||||
blockedUsers: "Blocked users"
|
||||
|
@ -311,9 +313,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Pages"
|
||||
integration: "Integration"
|
||||
connectService: "Connect"
|
||||
disconnectService: "Disconnect"
|
||||
enableLocalTimeline: "Enable local timeline"
|
||||
enableGlobalTimeline: "Enable global timeline"
|
||||
disablingTimelinesInfo: "Adminstrators and Moderators will always have access to all\
|
||||
|
@ -420,7 +419,6 @@ normalPassword: "Average password"
|
|||
strongPassword: "Strong password"
|
||||
passwordMatched: "Matches"
|
||||
passwordNotMatched: "Does not match"
|
||||
signinWith: "Sign in with {x}"
|
||||
signinFailed: "Unable to sign in. The entered username or password is incorrect."
|
||||
tapSecurityKey: "Tap your security key"
|
||||
or: "Or"
|
||||
|
@ -828,6 +826,10 @@ setTag: "Set tag"
|
|||
addTag: "Add tag"
|
||||
removeTag: "Remove tag"
|
||||
externalCssSnippets: "Some CSS snippets for your inspiration (not managed by FoundKey)"
|
||||
oauthErrorGoBack: "An error happened while trying to authenticate a 3rd party app.\
|
||||
\ Please go back and try again."
|
||||
appAuthorization: "App authorization"
|
||||
noPermissionsRequested: "(No permissions requested.)"
|
||||
_emailUnavailable:
|
||||
used: "This email address is already being used"
|
||||
format: "The format of this email address is invalid"
|
||||
|
@ -1078,38 +1080,37 @@ _2fa:
|
|||
\ authentication via hardware security keys that support FIDO2 to further secure\
|
||||
\ your account."
|
||||
_permissions:
|
||||
"read:account": "View your account information"
|
||||
"write:account": "Edit your account information"
|
||||
"read:blocks": "View your list of blocked users"
|
||||
"write:blocks": "Edit your list of blocked users"
|
||||
"read:drive": "Access your Drive files and folders"
|
||||
"write:drive": "Edit or delete your Drive files and folders"
|
||||
"read:favorites": "View your list of favorites"
|
||||
"write:favorites": "Edit your list of favorites"
|
||||
"read:following": "View information on who you follow"
|
||||
"write:following": "Follow or unfollow other accounts"
|
||||
"read:messaging": "View your chats"
|
||||
"write:messaging": "Compose or delete chat messages"
|
||||
"read:mutes": "View your list of muted users"
|
||||
"write:mutes": "Edit your list of muted users"
|
||||
"write:notes": "Compose or delete notes"
|
||||
"read:notifications": "View your notifications"
|
||||
"write:notifications": "Manage your notifications"
|
||||
"read:reactions": "View your reactions"
|
||||
"write:reactions": "Edit your reactions"
|
||||
"write:votes": "Vote on a poll"
|
||||
"read:pages": "View your pages"
|
||||
"write:pages": "Edit or delete your pages"
|
||||
"read:page-likes": "View your likes on pages"
|
||||
"write:page-likes": "Edit your likes on pages"
|
||||
"read:user-groups": "View your user groups"
|
||||
"write:user-groups": "Edit or delete your user groups"
|
||||
"read:channels": "View your channels"
|
||||
"write:channels": "Edit your channels"
|
||||
"read:gallery": "View your gallery"
|
||||
"write:gallery": "Edit your gallery"
|
||||
"read:gallery-likes": "View your list of liked gallery posts"
|
||||
"write:gallery-likes": "Edit your list of liked gallery posts"
|
||||
"read:account": "Read account information"
|
||||
"write:account": "Edit account information"
|
||||
"read:blocks": "Read which users are blocked"
|
||||
"write:blocks": "Block and unblock users"
|
||||
"read:drive": "List files and folders in the drive"
|
||||
"write:drive": "Create, change and delete files in the drive"
|
||||
"read:favorites": "List favourited notes"
|
||||
"write:favorites": "Favorite and unfavorite notes"
|
||||
"read:following": "List followed and following users"
|
||||
"write:following": "Follow and unfollow other users"
|
||||
"read:messaging": "View chat messages and history"
|
||||
"write:messaging": "Create and delete chat messages"
|
||||
"read:mutes": "List users which are muted or whose renotes are muted"
|
||||
"write:mutes": "Mute and unmute users or their renotes"
|
||||
"write:notes": "Create and delete notes"
|
||||
"read:notifications": "Read notifications"
|
||||
"write:notifications": "Mark notifications as read and create custom notifications"
|
||||
"write:reactions": "Create and delete reactions"
|
||||
"write:votes": "Vote in polls"
|
||||
"read:pages": "List and read pages"
|
||||
"write:pages": "Create, change and delete pages"
|
||||
"read:page-likes": "List and read page likes"
|
||||
"write:page-likes": "Like and unlike pages"
|
||||
"read:user-groups": "List and view joined, owned and invited to groups"
|
||||
"write:user-groups": "Create, modify, delete, transfer, join and leave groups. Invite and ban others from groups. Accept and reject group invitations."
|
||||
"read:channels": "List and read followed and joined channels"
|
||||
"write:channels": "Create, modify, follow and unfollow channels"
|
||||
"read:gallery": "List and read gallery posts"
|
||||
"write:gallery": "Create, modify and delete gallery posts"
|
||||
"read:gallery-likes": "List and read gallery post likes"
|
||||
"write:gallery-likes": "Like and unlike gallery posts"
|
||||
_auth:
|
||||
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
|
||||
shareAccessAsk: "Are you sure you want to authorize this application to access your\
|
||||
|
@ -1341,16 +1342,6 @@ _deck:
|
|||
list: "List"
|
||||
mentions: "Mentions"
|
||||
direct: "Direct notes"
|
||||
_services:
|
||||
_discord:
|
||||
connected: "Discord: @{username}#{discriminator} connected to FoundKey: @{mkUsername}!"
|
||||
disconnected: "Discord linkage has been removed."
|
||||
_twitter:
|
||||
connected: "Twitter: @{twitterUserName} connected to FoundKey: @{userName}!"
|
||||
disconnected: "Twitter linkage has been removed."
|
||||
_github:
|
||||
connected: "GitHub: @{login} connected to FoundKey: @{userName}!"
|
||||
disconnected: "GitHub linkage has been removed."
|
||||
_translationService:
|
||||
_deepl:
|
||||
authKey: "DeepL Auth Key"
|
||||
|
|
|
@ -311,9 +311,6 @@ dayX: "Día {day}"
|
|||
monthX: "Mes {month}"
|
||||
yearX: "Año {year}"
|
||||
pages: "Páginas"
|
||||
integration: "Integración"
|
||||
connectService: "Conectar"
|
||||
disconnectService: "Desconectar"
|
||||
enableLocalTimeline: "Habilitar linea de tiempo local"
|
||||
enableGlobalTimeline: "Habilitar linea de tiempo global"
|
||||
disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conveniencia\
|
||||
|
@ -420,7 +417,6 @@ normalPassword: "Buena contraseña"
|
|||
strongPassword: "Muy buena contraseña"
|
||||
passwordMatched: "Correcto"
|
||||
passwordNotMatched: "Las contraseñas no son las mismas"
|
||||
signinWith: "Inicie sesión con {x}"
|
||||
signinFailed: "Autenticación fallida. Asegúrate de haber usado el nombre de usuario\
|
||||
\ y contraseña correctos."
|
||||
tapSecurityKey: "Toque la clave de seguridad"
|
||||
|
|
|
@ -311,9 +311,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Pages"
|
||||
integration: "Intégrations"
|
||||
connectService: "Connexion"
|
||||
disconnectService: "Déconnexion"
|
||||
enableLocalTimeline: "Activer le fil local"
|
||||
enableGlobalTimeline: "Activer le fil global"
|
||||
disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur·rice·s\
|
||||
|
@ -423,7 +420,6 @@ normalPassword: "Mot de passe acceptable"
|
|||
strongPassword: "Mot de passe fort"
|
||||
passwordMatched: "Les mots de passe correspondent"
|
||||
passwordNotMatched: "Les mots de passe ne correspondent pas"
|
||||
signinWith: "Se connecter avec {x}"
|
||||
signinFailed: "Échec d’authentification. Veuillez vérifier que votre nom d’utilisateur\
|
||||
\ et mot de passe sont corrects."
|
||||
tapSecurityKey: "Appuyez sur votre clé de sécurité"
|
||||
|
@ -1331,18 +1327,6 @@ _deck:
|
|||
list: "Listes"
|
||||
mentions: "Mentions"
|
||||
direct: "Direct"
|
||||
_services:
|
||||
_discord:
|
||||
connected: '@{username}#{discriminator} sur Discord est connecté à @{mkUsername}
|
||||
sur FoundKey !'
|
||||
disconnected: La liaison avec Discord à été supprimée.
|
||||
_twitter:
|
||||
connected: '@{twitterUserName} sur Twitter est connecté à @{userName} sur FoundKey
|
||||
!'
|
||||
disconnected: La liaison avec Twitter à été supprimée.
|
||||
_github:
|
||||
disconnected: La liaison avec Github à été supprimée.
|
||||
connected: '@{login} sur Github est connecté à @{userName} sur FoundKey !'
|
||||
exportAll: Tout exporter
|
||||
stopActivityDeliveryDescription: L'activité locale ne sera pas envoyé à cette instance.
|
||||
La réception de l'activité continuera de fonctionner comme avant.
|
||||
|
|
|
@ -310,9 +310,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Halaman"
|
||||
integration: "Integrasi"
|
||||
connectService: "Sambungkan"
|
||||
disconnectService: "Putuskan"
|
||||
enableLocalTimeline: "Nyalakan linimasa lokal"
|
||||
enableGlobalTimeline: "Nyalakan linimasa global"
|
||||
disablingTimelinesInfo: "Admin dan Moderator akan selalu memiliki akses ke semua linimasa\
|
||||
|
@ -419,7 +416,6 @@ normalPassword: "Kata sandi baik"
|
|||
strongPassword: "Kata sandi kuat"
|
||||
passwordMatched: "Kata sandi sama"
|
||||
passwordNotMatched: "Kata sandi tidak sama"
|
||||
signinWith: "Masuk dengan {x}"
|
||||
signinFailed: "Tidak dapat masuk. Nama pengguna atau kata sandi yang kamu masukkan\
|
||||
\ salah."
|
||||
tapSecurityKey: "Ketuk kunci keamanan kamu"
|
||||
|
|
|
@ -304,9 +304,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Pagine"
|
||||
integration: "App collegate"
|
||||
connectService: "Connessione"
|
||||
disconnectService: "Disconnessione "
|
||||
enableLocalTimeline: "Abilita Timeline locale"
|
||||
enableGlobalTimeline: "Abilita Timeline federata"
|
||||
disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e\
|
||||
|
@ -413,7 +410,6 @@ normalPassword: "Password buona"
|
|||
strongPassword: "Password forte"
|
||||
passwordMatched: "Corretta"
|
||||
passwordNotMatched: "Le password non corrispondono."
|
||||
signinWith: "Accedi con {x}"
|
||||
signinFailed: "Autenticazione non riuscita. Controlla la tua password e nome utente."
|
||||
tapSecurityKey: "Premi la chiave di sicurezza"
|
||||
or: "oppure"
|
||||
|
|
|
@ -286,9 +286,6 @@ dayX: "{day}日"
|
|||
monthX: "{month}月"
|
||||
yearX: "{year}年"
|
||||
pages: "ページ"
|
||||
integration: "連携"
|
||||
connectService: "接続する"
|
||||
disconnectService: "切断する"
|
||||
enableLocalTimeline: "ローカルタイムラインを有効にする"
|
||||
enableGlobalTimeline: "グローバルタイムラインを有効にする"
|
||||
disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。"
|
||||
|
@ -392,7 +389,6 @@ normalPassword: "普通のパスワード"
|
|||
strongPassword: "強いパスワード"
|
||||
passwordMatched: "一致しました"
|
||||
passwordNotMatched: "一致していません"
|
||||
signinWith: "{x}でログイン"
|
||||
signinFailed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
|
||||
tapSecurityKey: "セキュリティキーにタッチ"
|
||||
or: "もしくは"
|
||||
|
@ -1273,13 +1269,3 @@ _deck:
|
|||
list: "リスト"
|
||||
mentions: "あなた宛て"
|
||||
direct: "ダイレクト"
|
||||
_services:
|
||||
_discord:
|
||||
connected: "Discord: @{username}#{discriminator} を、FoundKey: @{mkUsername} に接続しました!"
|
||||
disconnected: "Discordの連携を解除しました :v:"
|
||||
_twitter:
|
||||
connected: "Twitter: @{twitterUserName} を、FoundKey: @{userName} に接続しました!"
|
||||
disconnected: "Twitterの連携を解除しました :v:"
|
||||
_github:
|
||||
connected: "GitHub: @{login} を、FoundKey: @{userName} に接続しました!"
|
||||
disconnected: "GitHubの連携を解除しました :v:"
|
||||
|
|
|
@ -288,7 +288,6 @@ dayX: "{day}日"
|
|||
monthX: "{month}月"
|
||||
yearX: "{year}年"
|
||||
pages: "ページ"
|
||||
integration: "連携"
|
||||
enableLocalTimeline: "ローカルタイムラインを使えるようにする"
|
||||
enableGlobalTimeline: "グローバルタイムラインを使えるようにする"
|
||||
disablingTimelinesInfo: "ここらへんのタイムラインを使えんようにしてしもても、管理者とモデレーターは使えるままになってるで、そうやなかったら不便やからな。"
|
||||
|
@ -391,7 +390,6 @@ normalPassword: "普通のパスワード"
|
|||
strongPassword: "ええ感じのパスワード"
|
||||
passwordMatched: "よし!一致や!"
|
||||
passwordNotMatched: "一致しとらんで?"
|
||||
signinWith: "{x}でログイン"
|
||||
or: "それか"
|
||||
language: "言語"
|
||||
uiLanguage: "UIの表示言語"
|
||||
|
|
|
@ -39,7 +39,6 @@ userList: "Tibdarin"
|
|||
securityKey: "Tasarutt n tɣellist"
|
||||
securityKeyName: "Isem n tsarutt"
|
||||
signinRequired: "Ttxil jerred"
|
||||
signinWith: "Tuqqna s {x}"
|
||||
tapSecurityKey: "Sekcem tasarutt-ik·im n tɣellist"
|
||||
uiLanguage: "Tutlayt n wegrudem"
|
||||
plugins: "Izegrar"
|
||||
|
|
|
@ -315,9 +315,6 @@ dayX: "{day}일"
|
|||
monthX: "{month}월"
|
||||
yearX: "{year}년"
|
||||
pages: "페이지"
|
||||
integration: "연동"
|
||||
connectService: "계정 연동"
|
||||
disconnectService: "계정 연동 해제"
|
||||
enableLocalTimeline: "로컬 타임라인 활성화"
|
||||
enableGlobalTimeline: "글로벌 타임라인 활성화"
|
||||
disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다."
|
||||
|
@ -438,7 +435,6 @@ normalPassword: "좋은 비밀번호"
|
|||
strongPassword: "강한 비밀번호"
|
||||
passwordMatched: "일치합니다"
|
||||
passwordNotMatched: "일치하지 않습니다"
|
||||
signinWith: "{x}로 로그인"
|
||||
signinFailed: "로그인할 수 없습니다. 사용자명과 비밀번호를 확인하여 주십시오."
|
||||
tapSecurityKey: "보안 키를 터치"
|
||||
or: "혹은"
|
||||
|
|
|
@ -298,9 +298,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Strony"
|
||||
integration: "Integracja"
|
||||
connectService: "Połącz"
|
||||
disconnectService: "Rozłącz"
|
||||
enableLocalTimeline: "Włącz lokalną oś czasu"
|
||||
enableGlobalTimeline: "Włącz globalną oś czasu"
|
||||
disablingTimelinesInfo: "Administratorzy i moderatorzy będą zawsze mieć dostęp do\
|
||||
|
@ -407,7 +404,6 @@ normalPassword: "Dobre hasło"
|
|||
strongPassword: "Silne hasło"
|
||||
passwordMatched: "Pasuje"
|
||||
passwordNotMatched: "Hasła nie pasują do siebie"
|
||||
signinWith: "Zaloguj się z {x}"
|
||||
signinFailed: "Nie udało się zalogować. Wprowadzona nazwa użytkownika lub hasło są\
|
||||
\ nieprawidłowe."
|
||||
tapSecurityKey: "Wybierz swój klucz bezpieczeństwa"
|
||||
|
|
|
@ -311,9 +311,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Pagini"
|
||||
integration: "Integrare"
|
||||
connectService: "Conectează"
|
||||
disconnectService: "Deconectează"
|
||||
enableLocalTimeline: "Activează cronologia locală"
|
||||
enableGlobalTimeline: "Activeaza cronologia globală"
|
||||
disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate\
|
||||
|
@ -420,7 +417,6 @@ normalPassword: "Parolă medie"
|
|||
strongPassword: "Parolă puternică"
|
||||
passwordMatched: "Se potrivește!"
|
||||
passwordNotMatched: "Nu se potrivește"
|
||||
signinWith: "Autentifică-te cu {x}"
|
||||
signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse\
|
||||
\ sunt incorecte."
|
||||
tapSecurityKey: "Apasă pe cheia ta de securitate."
|
||||
|
|
|
@ -304,9 +304,6 @@ dayX: "{day} день"
|
|||
monthX: "{month} месяц"
|
||||
yearX: "{year} год"
|
||||
pages: "Страницы"
|
||||
integration: "Интеграция"
|
||||
connectService: "Подключиться"
|
||||
disconnectService: "Отключиться"
|
||||
enableLocalTimeline: "Включить локальную ленту"
|
||||
enableGlobalTimeline: "Включить глобальную ленту"
|
||||
disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам,\
|
||||
|
@ -415,7 +412,6 @@ normalPassword: "Годный пароль"
|
|||
strongPassword: "Надёжный пароль"
|
||||
passwordMatched: "Совпали"
|
||||
passwordNotMatched: "Не совпадают"
|
||||
signinWith: "Использовать {x} для входа"
|
||||
signinFailed: "Невозможно войти в систему. Введенное вами имя пользователя или пароль\
|
||||
\ неверны."
|
||||
tapSecurityKey: "Нажмите на свой электронный ключ"
|
||||
|
|
|
@ -305,9 +305,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Stránky"
|
||||
integration: "Integrácia"
|
||||
connectService: "Pripojiť"
|
||||
disconnectService: "Odpojiť"
|
||||
enableLocalTimeline: "Povoliť lokálnu časovú os"
|
||||
enableGlobalTimeline: "Povoliť globálnu časovú os"
|
||||
disablingTimelinesInfo: "Administrátori a moderátori majú vždy prístup ku všetkým\
|
||||
|
@ -414,7 +411,6 @@ normalPassword: "Dobré heslo"
|
|||
strongPassword: "Silné heslo"
|
||||
passwordMatched: "Heslá sú rovnaké"
|
||||
passwordNotMatched: "Heslá nie sú rovnaké"
|
||||
signinWith: "Prihlásiť sa použitím {x}"
|
||||
signinFailed: "Nedá sa prihlásiť. Skontrolujte prosím meno používateľa a heslo."
|
||||
tapSecurityKey: "Ťuknite na bezpečnostný kľúč"
|
||||
or: "Alebo"
|
||||
|
|
|
@ -305,9 +305,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Сторінки"
|
||||
integration: "Інтеграція"
|
||||
connectService: "Під’єднати"
|
||||
disconnectService: "Відключитися"
|
||||
enableLocalTimeline: "Увімкнути локальну стрічку"
|
||||
enableGlobalTimeline: "Увімкнути глобальну стрічку"
|
||||
disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх\
|
||||
|
@ -414,7 +411,6 @@ normalPassword: "Достатній пароль"
|
|||
strongPassword: "Міцний пароль"
|
||||
passwordMatched: "Все вірно"
|
||||
passwordNotMatched: "Паролі не співпадають"
|
||||
signinWith: "Увійти за допомогою {x}"
|
||||
signinFailed: "Не вдалося увійти. Введені ім’я користувача або пароль неправильнi."
|
||||
tapSecurityKey: "Торкніться ключа безпеки"
|
||||
or: "або"
|
||||
|
|
|
@ -305,9 +305,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Trang"
|
||||
integration: "Tương tác"
|
||||
connectService: "Kết nối"
|
||||
disconnectService: "Ngắt kết nối"
|
||||
enableLocalTimeline: "Bật bảng tin máy chủ"
|
||||
enableGlobalTimeline: "Bật bảng tin liên hợp"
|
||||
disablingTimelinesInfo: "Quản trị viên và Kiểm duyệt viên luôn có quyền truy cập mọi\
|
||||
|
@ -415,7 +412,6 @@ normalPassword: "Mật khẩu tạm được"
|
|||
strongPassword: "Mật khẩu mạnh"
|
||||
passwordMatched: "Trùng khớp"
|
||||
passwordNotMatched: "Không trùng khớp"
|
||||
signinWith: "Đăng nhập bằng {x}"
|
||||
signinFailed: "Không thể đăng nhập. Vui lòng kiểm tra tên người dùng và mật khẩu của\
|
||||
\ bạn."
|
||||
tapSecurityKey: "Nhấn mã bảo mật của bạn"
|
||||
|
|
|
@ -284,9 +284,6 @@ dayX: "{day}日"
|
|||
monthX: "{month}月"
|
||||
yearX: "{year}年"
|
||||
pages: "页面"
|
||||
integration: "关联"
|
||||
connectService: "连接"
|
||||
disconnectService: "断开连接"
|
||||
enableLocalTimeline: "启用本地时间线功能"
|
||||
enableGlobalTimeline: "启用全局时间线"
|
||||
disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和数据图表也可以继续使用。"
|
||||
|
@ -390,7 +387,6 @@ normalPassword: "密码强度:中等"
|
|||
strongPassword: "密码强度:强"
|
||||
passwordMatched: "密码一致"
|
||||
passwordNotMatched: "密码不一致"
|
||||
signinWith: "以{x}登录"
|
||||
signinFailed: "无法登录,请检查您的用户名和密码是否正确。"
|
||||
tapSecurityKey: "轻触硬件安全密钥"
|
||||
or: "或者"
|
||||
|
|
|
@ -284,9 +284,6 @@ dayX: "{day}日"
|
|||
monthX: "{month}月"
|
||||
yearX: "{year}年"
|
||||
pages: "頁面"
|
||||
integration: "整合"
|
||||
connectService: "己連結"
|
||||
disconnectService: "己斷開 "
|
||||
enableLocalTimeline: "開啟本地時間軸"
|
||||
enableGlobalTimeline: "啟用公開時間軸"
|
||||
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。"
|
||||
|
@ -390,7 +387,6 @@ normalPassword: "密碼強度普通"
|
|||
strongPassword: "密碼強度高"
|
||||
passwordMatched: "密碼一致"
|
||||
passwordNotMatched: "密碼不一致"
|
||||
signinWith: "以{x}登錄"
|
||||
signinFailed: "登入失敗。 請檢查使用者名稱和密碼。"
|
||||
tapSecurityKey: "點擊安全密鑰"
|
||||
or: "或者"
|
||||
|
|
|
@ -46,11 +46,11 @@
|
|||
"devDependencies": {
|
||||
"@types/gulp": "4.0.9",
|
||||
"@types/gulp-rename": "2.0.1",
|
||||
"@typescript-eslint/parser": "^5.44.0",
|
||||
"@typescript-eslint/parser": "^5.46.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "10.3.0",
|
||||
"start-server-and-test": "1.14.0",
|
||||
"typescript": "^4.9.3"
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"packageManager": "yarn@3.3.0"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
export class tokenPermissions1667653936442 {
|
||||
name = 'tokenPermissions1667653936442'
|
||||
|
||||
async up(queryRunner) {
|
||||
// Carry over the permissions from the app for tokens that have an associated app.
|
||||
await queryRunner.query(`UPDATE "access_token" SET permission = (SELECT permission FROM "app" WHERE "app"."id" = "access_token"."appId") WHERE "appId" IS NOT NULL AND CARDINALITY("permission") = 0`);
|
||||
// The permission column should now always be set explicitly, so the default is not needed any more.
|
||||
await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "permission" DROP DEFAULT`);
|
||||
// Drop all currently running authorization sessions. Already created tokens remain untouched.
|
||||
// If you were registering an app just before upgrade started, try again later. ¯\_(ツ)_/¯
|
||||
await queryRunner.query(`TRUNCATE TABLE "auth_session"`);
|
||||
// Refactor scheme to allow multiple access tokens per app.
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "FK_c072b729d71697f959bde66ade0"`);
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" RENAME COLUMN "userId" TO "accessTokenId"`);
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "UQ_8e001e5a101c6dca37df1a76d66" UNIQUE ("accessTokenId")`);
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "FK_8e001e5a101c6dca37df1a76d66" FOREIGN KEY ("accessTokenId") REFERENCES "access_token"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
// Drop all currently running authorization sessions. Already created tokens remain untouched.
|
||||
// If you were registering an app just before downgrade started, try again later. ¯\_(ツ)_/¯
|
||||
await queryRunner.query(`TRUNCATE TABLE "auth_session"`);
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "FK_8e001e5a101c6dca37df1a76d66"`);
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "UQ_8e001e5a101c6dca37df1a76d66"`);
|
||||
await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "permission" DROP DEFAULT`);
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" RENAME COLUMN "accessTokenId" TO "userId"`);
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "FK_c072b729d71697f959bde66ade0" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "permission" SET DEFAULT '{}'::varchar[]`);
|
||||
await queryRunner.query(`UPDATE "access_token" SET permission = '{}'::varchar[] WHERE "appId" IS NOT NULL`);
|
||||
}
|
||||
}
|
12
packages/backend/migration/1667738304733-pkce.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
export class pkce1667738304733 {
|
||||
name = 'pkce1667738304733'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" ADD "pkceChallenge" text`);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "auth_session"."pkceChallenge" IS 'PKCE code_challenge value, if provided (OAuth only)'`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "auth_session" DROP COLUMN "pkceChallenge"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
export class removeIntegrations1670359028055 {
|
||||
name = 'removeIntegrations1670359028055'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableTwitterIntegration"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "twitterConsumerKey"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "twitterConsumerSecret"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGithubIntegration"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "githubClientId"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "githubClientSecret"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableDiscordIntegration"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "discordClientId"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "discordClientSecret"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "integrations"`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD "integrations" jsonb NOT NULL DEFAULT '{}'`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "discordClientSecret" character varying(128)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "discordClientId" character varying(128)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "enableDiscordIntegration" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "githubClientSecret" character varying(128)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "githubClientId" character varying(128)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "enableGithubIntegration" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "twitterConsumerSecret" character varying(128)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "twitterConsumerKey" character varying(128)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "enableTwitterIntegration" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
|
||||
"watch": "node watch.mjs",
|
||||
"lint": "eslint src --ext .ts",
|
||||
"lint": "tsc --noEmit && eslint src --ext .ts",
|
||||
"mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
|
||||
"migrate": "npx typeorm migration:run -d ormconfig.js",
|
||||
"start": "node --experimental-json-modules ./built/index.js",
|
||||
|
@ -29,7 +29,6 @@
|
|||
"ajv": "8.11.0",
|
||||
"archiver": "5.3.1",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autwh": "0.1.0",
|
||||
"aws-sdk": "2.1165.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "1.1.5",
|
||||
|
@ -92,7 +91,6 @@
|
|||
"reflect-metadata": "0.1.13",
|
||||
"rename": "1.0.4",
|
||||
"require-all": "3.0.0",
|
||||
"rndstr": "1.0.0",
|
||||
"rss-parser": "3.12.0",
|
||||
"sanitize-html": "2.7.0",
|
||||
"semver": "7.3.7",
|
||||
|
@ -124,6 +122,7 @@
|
|||
"@types/bcryptjs": "2.4.2",
|
||||
"@types/bull": "3.15.8",
|
||||
"@types/cbor": "6.0.0",
|
||||
"@types/color-convert": "^2.0.0",
|
||||
"@types/escape-regexp": "0.0.1",
|
||||
"@types/fluent-ffmpeg": "2.1.20",
|
||||
"@types/is-url": "1.2.30",
|
||||
|
@ -146,7 +145,6 @@
|
|||
"@types/node": "18.7.16",
|
||||
"@types/node-fetch": "3.0.3",
|
||||
"@types/nodemailer": "6.4.5",
|
||||
"@types/oauth": "^0.9.1",
|
||||
"@types/pg": "^8.6.5",
|
||||
"@types/pug": "2.0.6",
|
||||
"@types/punycode": "2.1.0",
|
||||
|
@ -161,20 +159,21 @@
|
|||
"@types/sinon": "^10.0.13",
|
||||
"@types/sinonjs__fake-timers": "8.1.2",
|
||||
"@types/speakeasy": "2.0.7",
|
||||
"@types/syslog-pro": "^1.0.0",
|
||||
"@types/tinycolor2": "1.4.3",
|
||||
"@types/tmp": "0.2.3",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@types/web-push": "3.3.2",
|
||||
"@types/websocket": "1.0.5",
|
||||
"@types/ws": "8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.44.0",
|
||||
"@typescript-eslint/parser": "^5.44.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.46.1",
|
||||
"@typescript-eslint/parser": "^5.46.1",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint": "^8.29.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"execa": "6.1.0",
|
||||
"form-data": "^4.0.0",
|
||||
"sinon": "^14.0.2",
|
||||
"typescript": "^4.9.3"
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ const ev = new Xev();
|
|||
/**
|
||||
* Init process
|
||||
*/
|
||||
export default async function(): Promise<void> {
|
||||
export async function boot(): Promise<void> {
|
||||
process.title = `FoundKey (${cluster.isPrimary ? 'master' : 'worker'})`;
|
||||
|
||||
if (cluster.isPrimary || envOption.disableClustering) {
|
||||
|
|
|
@ -66,7 +66,7 @@ export async function masterMain(): Promise<void> {
|
|||
bootLogger.succ('FoundKey initialized');
|
||||
|
||||
if (!envOption.disableClustering) {
|
||||
await spawnWorkers(config.clusterLimit);
|
||||
await spawnWorkers(config.clusterLimits);
|
||||
}
|
||||
|
||||
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true);
|
||||
|
@ -139,16 +139,26 @@ async function connectDb(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
async function spawnWorkers(limit = 1): Promise<void> {
|
||||
const workers = Math.min(limit, os.cpus().length);
|
||||
bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`);
|
||||
await Promise.all([...Array(workers)].map(spawnWorker));
|
||||
async function spawnWorkers(clusterLimits: Required<Config['clusterLimits']>): Promise<void> {
|
||||
const modes = ['web', 'queue'];
|
||||
const cpus = os.cpus().length;
|
||||
for (const mode of modes.filter(mode => clusterLimits[mode] > cpus)) {
|
||||
bootLogger.warn(`configuration warning: cluster limit for ${mode} exceeds number of cores (${cpus})`);
|
||||
}
|
||||
|
||||
const total = modes.reduce((acc, mode) => acc + clusterLimits[mode], 0);
|
||||
const workers = new Array(total);
|
||||
workers.fill('web', 0, clusterLimits.web);
|
||||
workers.fill('queue', clusterLimits.web);
|
||||
|
||||
bootLogger.info(`Starting ${total} workers...`);
|
||||
await Promise.all(workers.map(mode => spawnWorker(mode)));
|
||||
bootLogger.succ('All workers started');
|
||||
}
|
||||
|
||||
function spawnWorker(): Promise<void> {
|
||||
function spawnWorker(mode: 'web' | 'queue'): Promise<void> {
|
||||
return new Promise(res => {
|
||||
const worker = cluster.fork();
|
||||
const worker = cluster.fork({ mode });
|
||||
worker.on('message', message => {
|
||||
if (message === 'listenFailed') {
|
||||
bootLogger.error('The server Listen failed due to the previous error.');
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import os from 'node:os';
|
||||
import cluster from 'node:cluster';
|
||||
import { initDb } from '@/db/postgre.js';
|
||||
|
||||
|
@ -7,11 +8,20 @@ import { initDb } from '@/db/postgre.js';
|
|||
export async function workerMain(): Promise<void> {
|
||||
await initDb();
|
||||
|
||||
// start server
|
||||
await import('../server/index.js').then(x => x.default());
|
||||
if (!process.env.mode || process.env.mode === 'web') {
|
||||
// start server
|
||||
await import('../server/index.js').then(x => x.default());
|
||||
}
|
||||
|
||||
// start job queue
|
||||
import('../queue/index.js').then(x => x.default());
|
||||
if (!process.env.mode || process.env.mode === 'queue') {
|
||||
// start job queue
|
||||
import('../queue/index.js').then(x => x.default());
|
||||
|
||||
if (process.env.mode === 'queue') {
|
||||
// if this is an exclusive queue worker, renice to have higher priority
|
||||
os.setPriority(os.constants.priority.PRIORITY_BELOW_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (cluster.isWorker) {
|
||||
// Send a 'ready' message to parent process
|
||||
|
|
|
@ -38,6 +38,12 @@ export default function load(): Config {
|
|||
|
||||
config.port = config.port || parseInt(process.env.PORT || '', 10);
|
||||
|
||||
config.images = Object.assign({
|
||||
info: '/twemoji/1f440.svg',
|
||||
notFound: '/twemoji/2049.svg',
|
||||
error: '/twemoji/1f480.svg',
|
||||
}, config.images ?? {});
|
||||
|
||||
if (!config.maxNoteTextLength) config.maxNoteTextLength = 3000;
|
||||
|
||||
mixin.version = meta.version;
|
||||
|
@ -54,6 +60,23 @@ export default function load(): Config {
|
|||
|
||||
if (!config.redis.prefix) config.redis.prefix = mixin.host;
|
||||
|
||||
if (!config.clusterLimits) {
|
||||
config.clusterLimits = {
|
||||
web: 1,
|
||||
queue: 1,
|
||||
};
|
||||
} else {
|
||||
config.clusterLimits = {
|
||||
web: 1,
|
||||
queue: 1,
|
||||
...config.clusterLimits,
|
||||
};
|
||||
|
||||
if (config.clusterLimits.web < 1 || config.clusterLimits.queue < 1) {
|
||||
throw new Error('invalid cluster limits');
|
||||
}
|
||||
}
|
||||
|
||||
return Object.assign(config, mixin);
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,10 @@ export type Source = {
|
|||
|
||||
accesslog?: string;
|
||||
|
||||
clusterLimit?: number;
|
||||
clusterLimits?: {
|
||||
web?: number;
|
||||
queue?: number;
|
||||
};
|
||||
|
||||
id: string;
|
||||
|
||||
|
@ -56,7 +59,7 @@ export type Source = {
|
|||
deliverJobMaxAttempts?: number;
|
||||
inboxJobMaxAttempts?: number;
|
||||
|
||||
syslog: {
|
||||
syslog?: {
|
||||
host: string;
|
||||
port: number;
|
||||
};
|
||||
|
@ -64,6 +67,12 @@ export type Source = {
|
|||
mediaProxy?: string;
|
||||
proxyRemoteFiles?: boolean;
|
||||
internalStoragePath?: string;
|
||||
|
||||
images?: {
|
||||
info?: string;
|
||||
notFound?: string;
|
||||
error?: string;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// https://github.com/typeorm/typeorm/issues/2400
|
||||
import pg from 'pg';
|
||||
import { SECOND } from '@/const.js';
|
||||
|
||||
pg.types.setTypeParser(20, Number);
|
||||
|
||||
|
@ -8,6 +7,7 @@ import { Logger, DataSource } from 'typeorm';
|
|||
import * as highlight from 'cli-highlight';
|
||||
import config from '@/config/index.js';
|
||||
|
||||
import { SECOND } from '@/const.js';
|
||||
import { User } from '@/models/entities/user.js';
|
||||
import { DriveFile } from '@/models/entities/drive-file.js';
|
||||
import { DriveFolder } from '@/models/entities/drive-folder.js';
|
||||
|
@ -78,33 +78,33 @@ import { redisClient } from './redis.js';
|
|||
const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false);
|
||||
|
||||
class MyCustomLogger implements Logger {
|
||||
private highlight(sql: string) {
|
||||
private highlight(sql: string): string {
|
||||
return highlight.highlight(sql, {
|
||||
language: 'sql', ignoreIllegals: true,
|
||||
});
|
||||
}
|
||||
|
||||
public logQuery(query: string, parameters?: any[]) {
|
||||
public logQuery(query: string): void {
|
||||
sqlLogger.info(this.highlight(query).substring(0, 100));
|
||||
}
|
||||
|
||||
public logQueryError(error: string, query: string, parameters?: any[]) {
|
||||
public logQueryError(error: string, query: string): void {
|
||||
sqlLogger.error(this.highlight(query));
|
||||
}
|
||||
|
||||
public logQuerySlow(time: number, query: string, parameters?: any[]) {
|
||||
public logQuerySlow(time: number, query: string): void {
|
||||
sqlLogger.warn(this.highlight(query));
|
||||
}
|
||||
|
||||
public logSchemaBuild(message: string) {
|
||||
public logSchemaBuild(message: string): void {
|
||||
sqlLogger.info(message);
|
||||
}
|
||||
|
||||
public log(message: string) {
|
||||
public log(message: string): void {
|
||||
sqlLogger.info(message);
|
||||
}
|
||||
|
||||
public logMigration(message: string) {
|
||||
public logMigration(message: string): void {
|
||||
sqlLogger.info(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'node:events';
|
||||
import boot from '@/boot/index.js';
|
||||
import { boot } from '@/boot/index.js';
|
||||
|
||||
Error.stackTraceLimit = Infinity;
|
||||
EventEmitter.defaultMaxListeners = 128;
|
||||
|
|
|
@ -7,7 +7,17 @@ const treeAdapter = parse5.defaultTreeAdapter;
|
|||
const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
|
||||
const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/;
|
||||
|
||||
export function fromHtml(html: string, hashtagNames?: string[], quoteUri?: string | null): string {
|
||||
function getAttr(node: TreeAdapter.Node, attr: string): string {
|
||||
return node.attrs.find(({ name }) => name === attr)?.value;
|
||||
}
|
||||
function attrHas(node: TreeAdapter.Node, attr: string, value: string): boolean {
|
||||
const attrValue = getAttr(node, attr);
|
||||
if (!attrValue) return false;
|
||||
|
||||
return new RegExp('\\b' + value + '\\b').test(attrValue);
|
||||
}
|
||||
|
||||
export function fromHtml(html: string, quoteUri?: string | null): string {
|
||||
const dom = parse5.parseFragment(
|
||||
// some AP servers like Pixelfed use br tags as well as newlines
|
||||
html.replace(/<br\s?\/?>\r?\n/gi, '\n'),
|
||||
|
@ -59,19 +69,18 @@ export function fromHtml(html: string, hashtagNames?: string[], quoteUri?: strin
|
|||
case 'a':
|
||||
{
|
||||
const txt = getText(node);
|
||||
const rel = node.attrs.find(x => x.name === 'rel');
|
||||
const href = node.attrs.find(x => x.name === 'href');
|
||||
const href = getAttr(node, 'href');
|
||||
|
||||
// hashtags
|
||||
if (hashtagNames && href && hashtagNames.map(x => x.toLowerCase()).includes(txt.toLowerCase())) {
|
||||
if (txt.startsWith('#') && href && (attrHas(node, 'rel', 'tag') || attrHas(node, 'class', 'hashtag'))) {
|
||||
text += txt;
|
||||
// mentions
|
||||
} else if (txt.startsWith('@') && !(rel && rel.value.match(/^me /))) {
|
||||
} else if (txt.startsWith('@') && !attrHas(node, 'rel', 'me')) {
|
||||
const part = txt.split('@');
|
||||
|
||||
if (part.length === 2 && href) {
|
||||
// restore the host name part
|
||||
const acct = `${txt}@${(new URL(href.value)).hostname}`;
|
||||
const acct = `${txt}@${(new URL(href)).hostname}`;
|
||||
text += acct;
|
||||
} else if (part.length === 3) {
|
||||
text += txt;
|
||||
|
@ -85,17 +94,17 @@ export function fromHtml(html: string, hashtagNames?: string[], quoteUri?: strin
|
|||
if (!href) {
|
||||
return txt;
|
||||
}
|
||||
if (!txt || txt === href.value) { // #6383: Missing text node
|
||||
if (href.value.match(urlRegexFull)) {
|
||||
return href.value;
|
||||
if (!txt || txt === href) { // #6383: Missing text node
|
||||
if (href.match(urlRegexFull)) {
|
||||
return href;
|
||||
} else {
|
||||
return `<${href.value}>`;
|
||||
return `<${href}>`;
|
||||
}
|
||||
}
|
||||
if (href.value.match(urlRegex) && !href.value.match(urlRegexFull)) {
|
||||
return `[${txt}](<${href.value}>)`; // #6846
|
||||
if (href.match(urlRegex) && !href.match(urlRegexFull)) {
|
||||
return `[${txt}](<${href}>)`; // #6846
|
||||
} else {
|
||||
return `[${txt}](${href.value})`;
|
||||
return `[${txt}](${href})`;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -204,8 +213,7 @@ export function fromHtml(html: string, hashtagNames?: string[], quoteUri?: strin
|
|||
|
||||
case 'span':
|
||||
{
|
||||
const nodeClass = node.attrs.find(({ name }) => name === 'class')?.value;
|
||||
if (/\bquote-inline\b/.test(nodeClass) && quoteUri && getText(node).trim() === `RE: ${quoteUri}`) {
|
||||
if (attrHas(node, 'class', 'quote-inline') && quoteUri && getText(node).trim() === `RE: ${quoteUri}`) {
|
||||
// embedded quote thingy for backwards compatibility, don't show it
|
||||
} else {
|
||||
appendChildren(node.childNodes);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export class Cache<T> {
|
||||
public cache: Map<string | null, { date: number; value: T; }>;
|
||||
public cache: Map<string, { date: number; value: T; }>;
|
||||
private lifetime: number;
|
||||
public fetcher: (key: string | null) => Promise<T | undefined>;
|
||||
public fetcher: (key: string) => Promise<T | undefined>;
|
||||
|
||||
constructor(lifetime: number, fetcher: Cache<T>['fetcher']) {
|
||||
this.cache = new Map();
|
||||
|
@ -9,14 +9,14 @@ export class Cache<T> {
|
|||
this.fetcher = fetcher;
|
||||
}
|
||||
|
||||
public set(key: string | null, value: T): void {
|
||||
public set(key: string, value: T): void {
|
||||
this.cache.set(key, {
|
||||
date: Date.now(),
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
public get(key: string | null): T | undefined {
|
||||
public get(key: string): T | undefined {
|
||||
const cached = this.cache.get(key);
|
||||
if (cached == null) return undefined;
|
||||
|
||||
|
@ -29,7 +29,7 @@ export class Cache<T> {
|
|||
return cached.value;
|
||||
}
|
||||
|
||||
public delete(key: string | null): void {
|
||||
public delete(key: string): void {
|
||||
this.cache.delete(key);
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ export class Cache<T> {
|
|||
* run to get the value. If the fetcher returns undefined, it is
|
||||
* returned but not cached.
|
||||
*/
|
||||
public async fetch(key: string | null): Promise<T | undefined> {
|
||||
public async fetch(key: string): Promise<T | undefined> {
|
||||
const cached = this.get(key);
|
||||
if (cached !== undefined) {
|
||||
return cached;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import * as crypto from 'node:crypto';
|
||||
|
||||
const L_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||
const LU_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
const LU_CHARS = L_CHARS + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
export function secureRndstr(length = 32, useLU = true): string {
|
||||
const chars = useLU ? LU_CHARS : L_CHARS;
|
||||
export function secureRndstrCustom(length = 32, chars: string): string {
|
||||
const chars_len = chars.length;
|
||||
|
||||
let str = '';
|
||||
|
@ -19,3 +18,8 @@ export function secureRndstr(length = 32, useLU = true): string {
|
|||
|
||||
return str;
|
||||
}
|
||||
|
||||
export function secureRndstr(length = 32, useLU = true): string {
|
||||
const chars = useLU ? LU_CHARS : L_CHARS;
|
||||
return secureRndstrCustom(length, chars);
|
||||
}
|
||||
|
|
16
packages/backend/src/misc/should-block-instance.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||
import { Instance } from '@/models/entities/instance.js';
|
||||
import { Meta } from '@/models/entities/meta.js';
|
||||
|
||||
/**
|
||||
* Returns whether a specific host (punycoded) should be blocked.
|
||||
*
|
||||
* @param host punycoded instance host
|
||||
* @param meta a Promise contatining the information from the meta table (optional)
|
||||
* @returns whether the given host should be blocked
|
||||
*/
|
||||
|
||||
export async function shouldBlockInstance(host: Instance['host'], meta: Promise<Meta> = fetchMeta()): Promise<boolean> {
|
||||
const { blockedHosts } = await meta;
|
||||
return blockedHosts.some(blockedHost => host === blockedHost || host.endsWith('.' + blockedHost));
|
||||
}
|
|
@ -2,39 +2,12 @@ import { db } from '@/db/postgre.js';
|
|||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||
import { Instance } from '@/models/entities/instance.js';
|
||||
import { DAY } from '@/const.js';
|
||||
import { Meta } from '@/models/entities/meta.js';
|
||||
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||
|
||||
// Threshold from last contact after which an instance will be considered
|
||||
// "dead" and should no longer get activities delivered to it.
|
||||
const deadThreshold = 7 * DAY;
|
||||
|
||||
/**
|
||||
* Returns whether a given host matches a wildcard pattern.
|
||||
* @param host punycoded instance host
|
||||
* @param pattern wildcard pattern containing a punycoded instance host
|
||||
* @returns whether the post matches the pattern
|
||||
*/
|
||||
function matchHost(host: Instance['host'], pattern: string): boolean {
|
||||
// Escape all of the regex special characters. Pattern from:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
||||
const escape = (str: string): string => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const re = new RegExp('^' + pattern.split('*').map(escape).join('.*') + '$');
|
||||
|
||||
return re.test(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a specific host (punycoded) should be blocked.
|
||||
*
|
||||
* @param host punycoded instance host
|
||||
* @param meta a Promise contatining the information from the meta table (oprional)
|
||||
* @returns whether the given host should be blocked
|
||||
*/
|
||||
export async function shouldBlockInstance(host: string, meta: Promise<Meta> = fetchMeta()): Promise<boolean> {
|
||||
const { blockedHosts } = await meta;
|
||||
return blockedHosts.some(blockedHost => matchHost(host, blockedHost));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of hosts which should be skipped.
|
||||
*
|
||||
|
@ -62,7 +35,7 @@ export async function skippedInstances(hosts: Array<Instance['host']>): Promise<
|
|||
hosts.filter(host => !skipped.includes(host) && !host.includes(',')).join(','),
|
||||
],
|
||||
)
|
||||
.then(res => res.map(row => row.host)),
|
||||
.then((res: Instance[]) => res.map(row => row.host)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ export class AbuseUserReport {
|
|||
@Column(id())
|
||||
public targetUserId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -27,7 +27,7 @@ export class AbuseUserReport {
|
|||
@Column(id())
|
||||
public reporterId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -39,7 +39,7 @@ export class AbuseUserReport {
|
|||
})
|
||||
public assigneeId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -41,7 +41,7 @@ export class AccessToken {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -53,7 +53,7 @@ export class AccessToken {
|
|||
})
|
||||
public appId: App['id'] | null;
|
||||
|
||||
@ManyToOne(type => App, {
|
||||
@ManyToOne(() => App, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -18,7 +18,7 @@ export class AnnouncementRead {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -28,7 +28,7 @@ export class AnnouncementRead {
|
|||
@Column(id())
|
||||
public announcementId: Announcement['id'];
|
||||
|
||||
@ManyToOne(type => Announcement, {
|
||||
@ManyToOne(() => Announcement, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class AntennaNote {
|
|||
})
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -29,7 +29,7 @@ export class AntennaNote {
|
|||
})
|
||||
public antennaId: Antenna['id'];
|
||||
|
||||
@ManyToOne(type => Antenna, {
|
||||
@ManyToOne(() => Antenna, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Antenna {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -42,7 +42,7 @@ export class Antenna {
|
|||
})
|
||||
public userListId: UserList['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserList, {
|
||||
@ManyToOne(() => UserList, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -54,7 +54,7 @@ export class Antenna {
|
|||
})
|
||||
public userGroupJoiningId: UserGroupJoining['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserGroupJoining, {
|
||||
@ManyToOne(() => UserGroupJoining, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class App {
|
|||
})
|
||||
public userId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'SET NULL',
|
||||
nullable: true,
|
||||
})
|
||||
|
|
|
@ -11,7 +11,7 @@ export class AttestationChallenge {
|
|||
@PrimaryColumn(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { id } from '../id.js';
|
||||
import { User } from './user.js';
|
||||
import { AccessToken } from './access-token.js';
|
||||
import { App } from './app.js';
|
||||
|
||||
@Entity()
|
||||
|
@ -23,21 +23,27 @@ export class AuthSession {
|
|||
...id(),
|
||||
nullable: true,
|
||||
})
|
||||
public userId: User['id'] | null;
|
||||
public accessTokenId: AccessToken['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => AccessToken, {
|
||||
onDelete: 'CASCADE',
|
||||
nullable: true,
|
||||
})
|
||||
@JoinColumn()
|
||||
public user: User | null;
|
||||
public accessToken: AccessToken | null;
|
||||
|
||||
@Column(id())
|
||||
public appId: App['id'];
|
||||
|
||||
@ManyToOne(type => App, {
|
||||
@ManyToOne(() => App, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
public app: App | null;
|
||||
|
||||
@Column('text', {
|
||||
nullable: true,
|
||||
comment: 'PKCE code_challenge value, if provided (OAuth only)',
|
||||
})
|
||||
pkceChallenge: string | null;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Blocking {
|
|||
})
|
||||
public blockeeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -34,7 +34,7 @@ export class Blocking {
|
|||
})
|
||||
public blockerId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -22,7 +22,7 @@ export class ChannelFollowing {
|
|||
})
|
||||
public followeeId: Channel['id'];
|
||||
|
||||
@ManyToOne(type => Channel, {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -35,7 +35,7 @@ export class ChannelFollowing {
|
|||
})
|
||||
public followerId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -18,7 +18,7 @@ export class ChannelNotePining {
|
|||
@Column(id())
|
||||
public channelId: Channel['id'];
|
||||
|
||||
@ManyToOne(type => Channel, {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -27,7 +27,7 @@ export class ChannelNotePining {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -28,7 +28,7 @@ export class Channel {
|
|||
})
|
||||
public userId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -53,7 +53,7 @@ export class Channel {
|
|||
})
|
||||
public bannerId: DriveFile['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFile, {
|
||||
@ManyToOne(() => DriveFile, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class ClipNote {
|
|||
})
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -29,7 +29,7 @@ export class ClipNote {
|
|||
})
|
||||
public clipId: Clip['id'];
|
||||
|
||||
@ManyToOne(type => Clip, {
|
||||
@ManyToOne(() => Clip, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -19,7 +19,7 @@ export class Clip {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -23,7 +23,7 @@ export class DriveFile {
|
|||
})
|
||||
public userId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'RESTRICT',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -144,7 +144,7 @@ export class DriveFile {
|
|||
})
|
||||
public folderId: DriveFolder['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFolder, {
|
||||
@ManyToOne(() => DriveFolder, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -27,7 +27,7 @@ export class DriveFolder {
|
|||
})
|
||||
public userId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -41,7 +41,7 @@ export class DriveFolder {
|
|||
})
|
||||
public parentId: DriveFolder['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFolder, {
|
||||
@ManyToOne(() => DriveFolder, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -20,7 +20,7 @@ export class FollowRequest {
|
|||
})
|
||||
public followeeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -33,7 +33,7 @@ export class FollowRequest {
|
|||
})
|
||||
public followerId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Following {
|
|||
})
|
||||
public followeeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -34,7 +34,7 @@ export class Following {
|
|||
})
|
||||
public followerId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class GalleryLike {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -25,7 +25,7 @@ export class GalleryLike {
|
|||
@Column(id())
|
||||
public postId: GalleryPost['id'];
|
||||
|
||||
@ManyToOne(type => GalleryPost, {
|
||||
@ManyToOne(() => GalleryPost, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -37,7 +37,7 @@ export class GalleryPost {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -22,7 +22,7 @@ export class MessagingMessage {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -35,7 +35,7 @@ export class MessagingMessage {
|
|||
})
|
||||
public recipientId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -48,7 +48,7 @@ export class MessagingMessage {
|
|||
})
|
||||
public groupId: UserGroup['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserGroup, {
|
||||
@ManyToOne(() => UserGroup, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -81,7 +81,7 @@ export class MessagingMessage {
|
|||
})
|
||||
public fileId: DriveFile['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFile, {
|
||||
@ManyToOne(() => DriveFile, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -134,7 +134,7 @@ export class Meta {
|
|||
})
|
||||
public proxyAccountId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -246,57 +246,6 @@ export class Meta {
|
|||
})
|
||||
public swPrivateKey: string;
|
||||
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
})
|
||||
public enableTwitterIntegration: boolean;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
nullable: true,
|
||||
})
|
||||
public twitterConsumerKey: string | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
nullable: true,
|
||||
})
|
||||
public twitterConsumerSecret: string | null;
|
||||
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
})
|
||||
public enableGithubIntegration: boolean;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
nullable: true,
|
||||
})
|
||||
public githubClientId: string | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
nullable: true,
|
||||
})
|
||||
public githubClientSecret: string | null;
|
||||
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
})
|
||||
public enableDiscordIntegration: boolean;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
nullable: true,
|
||||
})
|
||||
public discordClientId: string | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
nullable: true,
|
||||
})
|
||||
public discordClientSecret: string | null;
|
||||
|
||||
@Column('enum', {
|
||||
enum: TranslationService,
|
||||
nullable: true,
|
||||
|
|
|
@ -16,7 +16,7 @@ export class ModerationLog {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -17,7 +17,7 @@ export class MutedNote {
|
|||
})
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -30,7 +30,7 @@ export class MutedNote {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -27,7 +27,7 @@ export class Muting {
|
|||
})
|
||||
public muteeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -40,7 +40,7 @@ export class Muting {
|
|||
})
|
||||
public muterId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -18,7 +18,7 @@ export class NoteFavorite {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -27,7 +27,7 @@ export class NoteFavorite {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -19,7 +19,7 @@ export class NoteReaction {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -29,7 +29,7 @@ export class NoteReaction {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -20,7 +20,7 @@ export class NoteThreadMuting {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -14,7 +14,7 @@ export class NoteUnread {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -24,7 +24,7 @@ export class NoteUnread {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -22,7 +22,7 @@ export class NoteWatching {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -35,7 +35,7 @@ export class NoteWatching {
|
|||
})
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -27,7 +27,7 @@ export class Note {
|
|||
})
|
||||
public replyId: Note['id'] | null;
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -41,7 +41,7 @@ export class Note {
|
|||
})
|
||||
public renoteId: Note['id'] | null;
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -75,7 +75,7 @@ export class Note {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -179,7 +179,7 @@ export class Note {
|
|||
})
|
||||
public channelId: Channel['id'] | null;
|
||||
|
||||
@ManyToOne(type => Channel, {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -28,7 +28,7 @@ export class Notification {
|
|||
})
|
||||
public notifieeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -45,7 +45,7 @@ export class Notification {
|
|||
})
|
||||
public notifierId: User['id'] | null;
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -89,7 +89,7 @@ export class Notification {
|
|||
})
|
||||
public noteId: Note['id'] | null;
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -101,7 +101,7 @@ export class Notification {
|
|||
})
|
||||
public followRequestId: FollowRequest['id'] | null;
|
||||
|
||||
@ManyToOne(type => FollowRequest, {
|
||||
@ManyToOne(() => FollowRequest, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -113,7 +113,7 @@ export class Notification {
|
|||
})
|
||||
public userGroupInvitationId: UserGroupInvitation['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserGroupInvitation, {
|
||||
@ManyToOne(() => UserGroupInvitation, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -165,7 +165,7 @@ export class Notification {
|
|||
})
|
||||
public appAccessTokenId: AccessToken['id'] | null;
|
||||
|
||||
@ManyToOne(type => AccessToken, {
|
||||
@ManyToOne(() => AccessToken, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class PageLike {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -25,7 +25,7 @@ export class PageLike {
|
|||
@Column(id())
|
||||
public pageId: Page['id'];
|
||||
|
||||
@ManyToOne(type => Page, {
|
||||
@ManyToOne(() => Page, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -57,7 +57,7 @@ export class Page {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -69,7 +69,7 @@ export class Page {
|
|||
})
|
||||
public eyeCatchingImageId: DriveFile['id'] | null;
|
||||
|
||||
@ManyToOne(type => DriveFile, {
|
||||
@ManyToOne(() => DriveFile, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -22,7 +22,7 @@ export class PasswordResetRequest {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -19,7 +19,7 @@ export class PollVote {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -29,7 +29,7 @@ export class PollVote {
|
|||
@Column(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@ManyToOne(type => Note, {
|
||||
@ManyToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -9,7 +9,7 @@ export class Poll {
|
|||
@PrimaryColumn(id())
|
||||
public noteId: Note['id'];
|
||||
|
||||
@OneToOne(type => Note, {
|
||||
@OneToOne(() => Note, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -25,7 +25,7 @@ export class RegistryItem {
|
|||
})
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -21,7 +21,7 @@ export class RenoteMuting {
|
|||
})
|
||||
public muteeId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
@ -34,7 +34,7 @@ export class RenoteMuting {
|
|||
})
|
||||
public muterId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|
|
@ -16,7 +16,7 @@ export class Signin {
|
|||
@Column(id())
|
||||
public userId: User['id'];
|
||||
|
||||
@ManyToOne(type => User, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn()
|
||||
|
|