Compare commits

...

122 Commits

Author SHA1 Message Date
vib 2bfe7a15d5 Merge branch 'main' into snug.moe 2023-01-04 16:42:25 +02:00
Norm e58c940d6f
meta: Bump copyright year 2023-01-03 21:53:02 -05:00
Johann150 08af6fda37
fix some type errors 2023-01-03 22:18:01 +01:00
Johann150 0c8a3cfeec
server: fix lints 2023-01-03 03:51:38 +01:00
Johann150 8bc366fde0
server: fix comma-dangle lint 2023-01-03 02:47:58 +01:00
Johann150 417d252e9d
server: fix custom lint typeorm-prefer-count 2023-01-03 02:42:42 +01:00
Johann150 b54e07caec
enhance typeorm-prefer-count lint rule 2023-01-03 02:41:53 +01:00
Johann150 e0560dbe9e
client: dont display tooltip if software name unknown
Changelog: Fixed
2023-01-02 21:51:13 +01:00
Johann150 5b898c6c82
chore: update yarn files 2023-01-02 21:40:12 +01:00
Johann150 6010884e62
cleanup: translate japanese, use SECOND constant 2023-01-02 21:07:56 +01:00
Johann150 b423d23cf6
server: fix custom lint typeorm-prefer-count 2023-01-02 21:07:02 +01:00
Johann150 29714d1ae0
add custom eslint rule to prefer countBy over findBy 2023-01-02 20:58:33 +01:00
Johann150 7bf4d4426a
use count instead of find to check existence 2023-01-02 14:43:27 +01:00
Johann150 d28931bf00
server: remove dateUTC function 2023-01-02 12:45:30 +01:00
Johann150 2a46719f31
server: set file permissions after copy
This explicitly sets the file permissions to allow everyone to read files
since apparently multer sometimes doesn't set the permissions we expect.

Ref: FoundKeyGang/FoundKey#202
Changelog: Fixed
2023-01-02 12:44:09 +01:00
Johann150 7f564431be
server: fixup sql
Fixup to 0b7c9095bf.
2023-01-02 00:11:35 +01:00
Johann150 0fbd7fa492
client: fix 500 error in notifications
closes FoundKeyGang/FoundKey#73

Changelog: Fixed
2023-01-01 23:32:01 +01:00
Johann150 3aaa9facc6
translate japanese to english 2023-01-01 23:30:43 +01:00
Johann150 8f09b05e7c
chore: remove reversi database tables
Changelog: Fixed
2023-01-01 22:27:34 +01:00
Johann150 8b0b7ff525
server: change default value for api/admin/show-users origin param
Changed from "local" to "combined" to fix a bug when the hostname is set
but origin is not.

Changelog: Changed
2023-01-01 22:11:19 +01:00
Johann150 0b7c9095bf
server: don't return users twice in search 2023-01-01 21:22:53 +01:00
Johann150 338e898f56
client: disable unavailable visibilities 2023-01-01 21:02:55 +01:00
Puniko 3a9d283630
client: make mod patterns display when seeking on stopped track 2022-12-29 21:43:34 +01:00
Johann150 ed27f61a4d
client: add mod tracker
Squashed commit of the following:

commit 54f0b67b25bc6064b5c0ab3982e20943859aff76
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 21:27:15 2022 +0100

    use nextTick instead of setTimeout

commit 6998cae7e3a706b00c1b63320750dab279f13e3d
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 21:14:55 2022 +0100

    my absolute terrible fix to the unhide issue

commit 79f546d1509185c315a5db7e12985e01ed430b08
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 21:01:35 2022 +0100

    stop player on hide/unhide

commit 6b7f13e8ef48d74edb92441144051e4225b27f71
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 10:36:59 2022 +0100

    make webkit style range slider the same

commit 8a267c5cdc5c1e98d6dd1e038571d92d01985a98
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 01:16:18 2022 +0100

    restyling range inputs

commit c39e1671b2957326ff91746da44091d728f58e82
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 00:57:47 2022 +0100

    make module seekable

commit c1762f27ae2e4d342ede88018f777c75b8b31d4a
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 00:14:35 2022 +0100

    remove accesskey attribs

commit 08f75a01f1c2359799e4c2ec734a391d3abfc07c
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 00:12:23 2022 +0100

    v-else on play button

commit 9302a9faaa5a75c33314befb3f8e0f414e9d886b
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 00:08:19 2022 +0100

    replace filter with some

commit bffd15daedf55f7623d6ebd777a12384274b89d6
Author: Puniko <me@absturztaube.ch>
Date:   Wed Dec 28 09:13:20 2022 +0100

    add chiptune2 and libopenmpt into COPYING

commit 794298c21c6fe09909136d141fd2a6b83eb64a89
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 15:32:43 2022 +0100

    little cleanup

commit f383aec1cd5a9f8a44539d9f802dee2d1332f64c
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 15:23:25 2022 +0100

    repeat only once and proper handling of track ending

commit fdaa9614c993de306789ecafc38d781d350fe1ab
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 14:52:20 2022 +0100

    prevent losing connection when downloading module

commit 6c5723c795a3610558111f5d0cd6f39dcfd06965
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 14:45:59 2022 +0100

    colours!!! 🌈

commit dba4f0a4a909b956d928bfb7e4f0321291a096e0
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 13:01:06 2022 +0100

    replace  with i18n

commit 4234dfbdbc7f9b73fe07348af1fe2590db4e811d
Author: Puniko <me@absturztaube.ch>
Date:   Mon Dec 26 15:47:10 2022 +0100

    retab

commit 0cc1ea8c3ec14fde81936ec3c9eed4d06ad52d95
Author: Puniko <me@absturztaube.ch>
Date:   Mon Dec 26 15:19:28 2022 +0100

    include libopenmpt tracker to foundkey

commit c2437c696a5dabb8581fd81f7852cdd6091fe00f
Author: Puniko <me@absturztaube.ch>
Date:   Mon Dec 26 12:08:49 2022 +0100

    add libopenmpt

Reviewed-on: FoundKeyGang/FoundKey#306
Changelog: Added
2022-12-29 21:36:44 +01:00
Chloe Kudryavtsev ed9d4023d4 backend: add argon2 support
Passwords will be automatically re-hashed on sign-in.
All new password hashes will be argon2 by default.

This uses argon2id and is not configurable.
In the very unlikely case someone has more specific needs,
a fork is recommended.

ChangeLog: Added

Co-authored-by: Chloe Kudryavtsev <code@toast.bunkerlabs.net>
Reviewed-on: FoundKeyGang/FoundKey#308
2022-12-29 20:13:47 +00:00
Johann150 76a8e000a3
client: only catch erroneous key errors in i18n.ts 2022-12-27 21:47:34 +01:00
Johann150 a673647fba
server: remove avatarColor and bannerColor properties
According to comments next to those properties, they were kept for backward compatibility.
However they were always being set to null.

Changelog: Removed
2022-12-26 18:52:16 +01:00
Johann150 eea2eb4919
use Promise.all instead of separate promises 2022-12-25 19:04:00 +01:00
Johann150 114d416de0
server: refactor password hashing & comparison to module
For easier replacement should the hash algorithm ever be changed.
2022-12-25 19:03:51 +01:00
Johann150 c2372315f7
server: improve error messages
Refactor Error's to ApiError's.

Changelog: Changed
2022-12-25 16:07:48 +01:00
Norm 09bc3cf95a
activitypub: Do block checks more globally
Changelog: Fixed
Reviewed-on: FoundKeyGang/FoundKey#299
2022-12-24 18:40:44 -05:00
Johann150 de3cdb5833
activitypub: block check for resolving collections 2022-12-24 18:39:44 -05:00
Norm a732cdc1ad
activitypub: perform block check in performOneActivity 2022-12-24 18:39:44 -05:00
Norm a8f82050c8
activitypub: perform resolver block check on objects as well 2022-12-24 18:39:44 -05:00
Norm 8e12b9a33e
server: restore original comment for skippedInstances 2022-12-24 15:01:32 -05:00
Norm 6583d0c43d
server: pass in resolved meta table to shouldBlockInstance
This should make it more friendly to use in places where the meta table
has already been resolved for other reasons.
2022-12-24 14:56:48 -05:00
Johann150 c02a03168d
fixup tooltip: use this instead of self 2022-12-24 13:48:58 +01:00
Johann150 85419326f8
server: use prelude function instead of separate function 2022-12-23 13:55:15 +01:00
Johann150 eaa11647f0
server: rewrite drive usage queries in raw SQL 2022-12-23 13:54:12 +01:00
Johann150 61a2db49df
server: always use user id for calcDriveUsageOf 2022-12-23 13:38:29 +01:00
Johann150 79ddbafd0d
client: fix tooltips not closing
closes FoundKeyGang/FoundKey#120

Changelog: Fixed
2022-12-23 11:06:07 +01:00
Norm 0e1459e5cf Merge pull request 'server: refactor follow request functions to be named exports' (#296) from refactor/follow-requests into main
Reviewed-on: FoundKeyGang/FoundKey#296
2022-12-23 02:06:31 +00:00
Johann150 e8e82dac82
fixup: confusing commas 2022-12-23 02:30:57 +01:00
Norm 9690244848
server: add return type for all follow reject funcs 2022-12-22 17:52:30 -05:00
Norm 4db25e4b1f
server: add doc for cancelFollowRequest 2022-12-22 16:55:08 -05:00
Norm 549302e9c0
server: add doc for createFollowRequest 2022-12-22 16:55:07 -05:00
Norm a3354904af
server: use named export for createFollowRequest 2022-12-22 16:52:52 -05:00
Norm 28f65bebfc
server: use named export for cancelFollowRequest 2022-12-22 16:52:52 -05:00
Norm 2204adc657
server: use named export for acceptAllFollowRequests 2022-12-22 16:52:52 -05:00
Norm b11e4053db
server: use named export for acceptFollowRequest 2022-12-22 16:52:52 -05:00
Johann150 e2ef800708
server: dont use replace for file types
No point in using replace if we already know which character we want to replace.
2022-12-22 14:46:21 +01:00
Johann150 a7048f17f7
server: simplify duplicated code 2022-12-22 14:45:20 +01:00
Johann150 ddf3e2c3db
client: refactor tooltip directive
Using the beforeUnmount hook should hopefully improve issues with
tooltips being left behind.
2022-12-22 14:12:25 +01:00
Johann150 52afff800a
server: start adding /api/v2 routes
empty changelog commit

Changelog: Added
2022-12-22 11:03:38 +01:00
Johann150 33f0b24c56
server: add v2 routes to notes endpoints 2022-12-22 11:02:04 +01:00
Andy 7685b92511
improve fetching of endpoint arguments
including support for route parameters (e.g. '/v2/note/:noteId' giving us a 'noteId' value)

Co-authored-by: Johann150 <johann.galle@protonmail.com>
2022-12-22 11:02:04 +01:00
Andy 8276bd3bdc
generate OpenAPI spec for v2 endpoints 2022-12-22 11:02:04 +01:00
Andy aed2752470
server: make v2 meta endpoint support GET 2022-12-22 11:01:56 +01:00
Andy 4a3b91d658
server: add additional API v2 options to endpoints
* improve type definitions for v2 method
The method has to be lowercase because it is used as an index to get
the respective method of the router.

Co-authored-by: Johann150 <johann.galle@protonmail.com>
2022-12-22 11:00:46 +01:00
Johann150 9317d25078
server: expire notifications after 3 months
closes FoundKeyGang/FoundKey#292

Changelog: Added
2022-12-21 21:46:45 +01:00
Johann150 fc36bb8880
server: reduce code duplication in check-expired queue job 2022-12-21 21:46:27 +01:00
Johann150 711bb8be7d
fixup: add missing redirect argument 2022-12-21 21:23:23 +01:00
Johann150 275136cf8b
allow redirects in API ap/* endpoints 2022-12-21 20:45:55 +01:00
Johann150 aa33708b90
server: handle redirects in signed get
part of FoundKeyGang/FoundKey#288

Changelog: Fixed
2022-12-20 22:07:24 +01:00
Norm d75e295ee8
remove "your" from "read:reactions"
Makes it consistent wiht the rest of the _permissions strings not using
pronouns.
2022-12-20 00:10:17 -05:00
Norm 766ab1c4c4
docs: readd missing "read:reactions" string 2022-12-20 00:07:35 -05:00
Johann150 99c459a21a
server: better upload limit error
Ref: FoundKeyGang/FoundKey#293
2022-12-19 21:29:29 +01:00
Johann150 bd68096ea9
server: refactor API error 2022-12-19 21:24:39 +01:00
Johann150 c411669133
client: fix token-generate-window component 2022-12-18 20:42:05 +01:00
Johann150 639fa74d43
client: restyle app token view 2022-12-18 19:37:52 +01:00
Johann150 3bf7deb233
client: remove unused styling classes 2022-12-18 00:50:02 +01:00
Johann150 2520633210
client: move MFM animations to MFM component 2022-12-18 00:49:28 +01:00
Johann150 4574db523a
client: add default margin to FormSwitch and MkButton 2022-12-18 00:11:32 +01:00
Johann150 263fb94f3f
client: unify import names of form ui components 2022-12-18 00:04:53 +01:00
Johann150 0e3321c106
client: unify import names of MkButton component 2022-12-17 23:32:25 +01:00
Johann150 677ee537d1
client: remove unused component 2022-12-17 22:39:18 +01:00
Johann150 1e539e6af5
client: add missing space
see FoundKeyGang/FoundKey#291
2022-12-17 22:34:49 +01:00
Johann150 0e5f744560
client: 2fa is not its own settings page 2022-12-17 22:34:49 +01:00
Norm 8f782f8ce5 scripts: convert to ESM and deduplicate (#290)
This makes it a bit easier to add any new files that may need to be
cleaned up in the future.

Also allows us to use top-level await for the `yarn dev` task.

Co-authored-by: Francis Dinh <normandy@biribiri.dev>
Reviewed-on: FoundKeyGang/FoundKey#290
2022-12-16 17:15:25 +00:00
Johann150 6c7f1774e3
server: fix thread mutes not applying to renotes
Changelog: Fixed
2022-12-15 21:20:24 +01:00
Johann150 af43df15ca
reduce duplication in secureRndstr 2022-12-15 20:46:17 +01:00
Johann150 5f83383ab8
fix import error in tests 2022-12-15 20:45:55 +01:00
Johann150 8c759dde6c
server: fix error about duplicate resolve 2022-12-15 19:44:55 +01:00
Johann150 84d83d908a
client: add button to unrenote
Changelog: Added
2022-12-15 17:52:19 +01:00
Johann150 16d091497a
server: use extractDbHost instead of toPuny, translate comments
Also swapped logical or for nullish coalescing operator in some places.
2022-12-15 00:32:15 +01:00
Johann150 ef53ec276a
activitypub: simplify some URI/id related checks
followup on previous commit
2022-12-15 00:31:23 +01:00
Johann150 3582fd8260
activitypub: centrally check id matches URL in resolver
This makes some duplicated checks in models/note and models/person
unnecessary.
2022-12-15 00:29:39 +01:00
Johann150 6256ddbd30
client: remove unused variables 2022-12-14 22:09:29 +01:00
Johann150 00fcc238f7
client: remove broken instance ticker from landing page 2022-12-14 22:08:27 +01:00
Johann150 9f1670d5fd
server: fix default not found error image 2022-12-14 19:05:41 +01:00
Norm ff31b8b06d server: remove bios and cli
The BIOS and CLI functionality were mainly for debugging purposes.
If a user has to use those to resolve an issue with the server, that
really should be fixed at the source instead.

Closes: FoundKeyGang/FoundKey#283
Changelog: Removed
2022-12-14 17:59:25 +00:00
Johann150 e317a771b3
remove vscode config files 2022-12-14 18:52:36 +01:00
Johann150 398ee6435b
client: replace repo link with foundkey link 2022-12-14 18:21:24 +01:00
Johann150 ffff2ae5ef
server: fix missing import
closes FoundKeyGang/FoundKey#286
2022-12-14 18:08:44 +01:00
Johann150 ccc8bf0289
chore: fix more miscellaneous lints 2022-12-13 23:09:32 +01:00
Johann150 a231b36d59
chore: fix lint about unused variables in entities 2022-12-13 23:09:32 +01:00
Johann150 8e9c65fab0
chore: fix some import related lints 2022-12-13 23:09:31 +01:00
Norm 78a3051313
remove unneeded TODO 2022-12-13 16:46:29 -05:00
Norm 78717e85d3
server: change JSON.parse/stringify to structuredClone
structuredClone is more typesafe than using JSON.parse and
JSON.stringify.

Now that Node 18.x is the new baseline, this should be safe to use now.

See https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
for details.
2022-12-13 16:45:38 -05:00
Norm a9d3cae511
server: add return type to extractApMentions 2022-12-13 16:31:15 -05:00
Norm bd27b7ca3a
server: add typing for renderFollowRelay 2022-12-13 16:06:18 -05:00
Norm e28a9eb8e8
use tsc --noEmit for backend and client
See https://github.com/misskey-dev/misskey/pull/9316
2022-12-13 16:02:06 -05:00
Norm e5a4c5d2d0
chore: update @typescript-eslint packages 2022-12-13 15:57:26 -05:00
Norm 6bba55c196
sw: add TypeScript type checking
This implements the upstream changes from
https://github.com/misskey-dev/misskey/pull/9314 but updated to our
version of ESLint.

Also updates TypeScript to 4.9.4 for all packages.
2022-12-13 15:42:08 -05:00
Norm 1d469f3c34
fix import typo 2022-12-13 15:12:29 -05:00
Norm 3f0228e14c
server: use color-convert KEYWORD instead of extracting parameter type 2022-12-13 15:11:29 -05:00
Johann150 73f81177b4
foundkey-js: adjust type definition 2022-12-13 20:54:50 +01:00
Johann150 6a26da3516
client: use configurable images 2022-12-13 20:54:49 +01:00
Johann150 5ea744b1b2
server: use configurable images 2022-12-13 20:54:49 +01:00
Johann150 ae6ba05306
add config for error images
Changelog: Added
2022-12-13 20:54:49 +01:00
Sam Smucny 21069223e3
client: add tooltips to visibility icons
Changelog: Changed
2022-12-13 20:49:17 +01:00
Johann150 d4d1e03479
server: fix errors for replies and state when note doesnt exist 2022-12-13 20:35:46 +01:00
Norm 5513a3eb3a chore: update .gitattributes to reflect removed assets
Since there's no longer any .afdesign/.psd files or the like, it's not necessary to have those in .gitattributes any longer.
2022-12-11 23:16:05 +00:00
Johann150 d5dd7c1ef5
chore: remove more unused assets 2022-12-11 20:52:52 +01:00
Johann150 a80521b6a8
chore: remove unused assets 2022-12-11 20:44:38 +01:00
Norm 030394b30d
refactor: remove default export for boot 2022-12-11 14:42:55 -05:00
Johann150 768d9bbdfb
refactor: remove default export for perform 2022-12-11 18:23:19 +01:00
Johann150 3ef1a4b0f9
refactor: remove default export for Resolver 2022-12-11 18:23:07 +01:00
Johann150 ae59ce51b0
refactor: remove default export for DbResolver 2022-12-11 18:16:48 +01:00
Johann150 14a9b9bedd
refactor: remove default export for request 2022-12-11 18:16:45 +01:00
Johann150 985a13f47f
refactor: remove default export for DeliverManager 2022-12-11 17:56:25 +01:00
Norm 3e46433ede docker: Ignore .woodpecker 2022-12-11 01:35:33 +00:00
405 changed files with 2819 additions and 2007 deletions

View File

@ -133,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

View File

@ -1,6 +1,6 @@
.autogen
.vscode
.config
.woodpecker
Dockerfile
build/
built/

View File

@ -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
View File

@ -1,7 +1 @@
*.svg -diff -text
*.psd -diff -text
*.ai -diff -text
*.mqo -diff -text
*.glb -diff -text
*.blend -diff -text
*.afdesign -diff -text

5
.gitignore vendored
View File

@ -1,6 +1,6 @@
# Visual Studio Code
/.vscode
!/.vscode/extensions.json
/.vsls.json
# Intelij-IDEA
/.idea
@ -11,6 +11,9 @@
# nano
.swp
# vimlocal
.vimlocal
# Node.js
node_modules
report.*.json

View File

@ -1,8 +0,0 @@
{
"recommendations": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"Vue.volar",
"Vue.vscode-typescript-vue-plugin"
]
}

View File

@ -1,4 +0,0 @@
{
"$schema": "http://json.schemastore.org/vsls",
"gitignore": "exclude"
}

22
.woodpecker/lint-sw.yml Normal file
View 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

View File

@ -316,8 +316,8 @@ This does not apply when using the Composition API since reactivation is manual.
If you import json in TypeScript, the json file will be spit out together with the TypeScript file into the dist directory when compiling with tsc. This behavior may cause unintentional rewriting of files, so when importing json files, be sure to check whether the files are allowed to be rewritten or not. If you do not want the file to be rewritten, you should make sure that the file can be rewritten by importing the json file. If you do not want the file to be rewritten, use functions such as `fs.readFileSync` to read the file instead of importing it.
### Component style definitions do not have a `margin`
Setting the `margin` of a component may be confusing.
Instead, it should always be the user of a component that sets a `margin`.
~~Setting the `margin` of a component may be confusing. Instead, it should always be the user of a component that sets a `margin`.~~
This was a philosophy used previously. Hoever it now seems a better idea to add a default margin to the top level element of a component which can be easily overwritten on the usage of that component with a `style` attribute.
### Do not use the word "follow" in HTML class names
This has caused things to be blocked by an ad blocker in the past.

10
COPYING
View File

@ -1,6 +1,6 @@
Unless otherwise stated this repository is
Copyright © 2014-2022 syuilo and contributors
Copyright © 2022 FoundKey contributors
Copyright © 2022-2023 FoundKey contributors
And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE.
(You may be able to run `git shortlog -se` to see a full list of authors.)
@ -13,3 +13,11 @@ https://github.com/muan/emojilib/blob/master/LICENSE
RsaSignature2017 implementation by Transmute Industries Inc
License: MIT
https://github.com/transmute-industries/RsaSignature2017/blob/master/LICENSE
Chiptune2.js by Simon Gündling
License: MIT
https://github.com/deskjet/chiptune2.js#license
libopenmpt (as part of openmpt) by OpenMPT
License: BSD 3-Clause
https://github.com/OpenMPT/openmpt/blob/master/LICENSE

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -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
}))

View File

@ -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"
@ -1095,6 +1097,7 @@ _permissions:
"write:notes": "Create and delete notes"
"read:notifications": "Read notifications"
"write:notifications": "Mark notifications as read and create custom notifications"
"read:reactions": "View reactions"
"write:reactions": "Create and delete reactions"
"write:votes": "Vote in polls"
"read:pages": "List and read pages"

View File

@ -18,7 +18,7 @@
"migrateandstart": "yarn migrate && yarn start",
"gulp": "gulp build",
"watch": "yarn dev",
"dev": "node ./scripts/dev.js",
"dev": "node ./scripts/dev.mjs",
"lint": "yarn workspaces foreach run lint",
"cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts",
"cy:run": "cypress run",
@ -26,8 +26,8 @@
"mocha": "yarn workspace backend run mocha",
"test": "yarn mocha",
"format": "gulp format",
"clean": "node ./scripts/clean.js",
"clean-all": "node ./scripts/clean-all.js",
"clean": "node ./scripts/clean.mjs",
"clean-all": "node ./scripts/clean-all.mjs",
"cleanall": "yarn clean-all"
},
"resolutions": {
@ -35,6 +35,7 @@
"lodash": "^4.17.21"
},
"dependencies": {
"argon2": "^0.30.2",
"execa": "5.1.1",
"gulp": "4.0.2",
"gulp-cssnano": "2.1.3",
@ -46,11 +47,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"
}

View File

@ -6,7 +6,11 @@ module.exports = {
extends: [
'../shared/.eslintrc.js',
],
plugins: [
'foundkey-custom-rules',
],
rules: {
'foundkey-custom-rules/typeorm-prefer-count': 'error',
'import/order': ['warn', {
'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
'pathGroups': [

View File

@ -0,0 +1,21 @@
export class removeReversi1672607891750 {
name = 'removeReversi1672607891750';
async up(queryRunner) {
await queryRunner.query(`DROP TABLE "reversi_matching"`);
await queryRunner.query(`DROP TABLE "reversi_game"`);
}
async down(queryRunner) {
await queryRunner.query(`CREATE TABLE "reversi_game" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "startedAt" TIMESTAMP WITH TIME ZONE, "user1Id" character varying(32) NOT NULL, "user2Id" character varying(32) NOT NULL, "user1Accepted" boolean NOT NULL DEFAULT false, "user2Accepted" boolean NOT NULL DEFAULT false, "black" integer, "isStarted" boolean NOT NULL DEFAULT false, "isEnded" boolean NOT NULL DEFAULT false, "winnerId" character varying(32), "surrendered" character varying(32), "logs" jsonb NOT NULL DEFAULT '[]', "map" character varying(64) array NOT NULL, "bw" character varying(32) NOT NULL, "isLlotheo" boolean NOT NULL DEFAULT false, "canPutEverywhere" boolean NOT NULL DEFAULT false, "loopedBoard" boolean NOT NULL DEFAULT false, "form1" jsonb DEFAULT null, "form2" jsonb DEFAULT null, "crc32" character varying(32), CONSTRAINT "PK_76b30eeba71b1193ad7c5311c3f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt")`);
await queryRunner.query(`CREATE TABLE "reversi_matching" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "parentId" character varying(32) NOT NULL, "childId" character varying(32) NOT NULL, CONSTRAINT "PK_880bd0afbab232f21c8b9d146cf" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b604d92d6c7aec38627f6eaf16" ON "reversi_matching" ("createdAt")`);
await queryRunner.query(`CREATE INDEX "IDX_3b25402709dd9882048c2bbade" ON "reversi_matching" ("parentId")`);
await queryRunner.query(`CREATE INDEX "IDX_e247b23a3c9b45f89ec1299d06" ON "reversi_matching" ("childId")`);
await queryRunner.query(`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_f7467510c60a45ce5aca6292743" FOREIGN KEY ("user1Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_6649a4e8c5d5cf32fb03b5da9f6" FOREIGN KEY ("user2Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_3b25402709dd9882048c2bbade0" FOREIGN KEY ("parentId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_e247b23a3c9b45f89ec1299d066" FOREIGN KEY ("childId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}

View File

@ -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",
@ -166,14 +166,15 @@
"@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.29.0",
"eslint-plugin-foundkey-custom-rules": "file:../shared/custom-rules",
"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"
}
}

View File

@ -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) {

View File

@ -140,7 +140,7 @@ async function connectDb(): Promise<void> {
}
async function spawnWorkers(clusterLimits: Required<Config['clusterLimits']>): Promise<void> {
const modes = ['web', 'queue'];
const modes = ['web' as const, 'queue' as const];
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})`);

View File

@ -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;

View File

@ -67,6 +67,12 @@ export type Source = {
mediaProxy?: string;
proxyRemoteFiles?: boolean;
internalStoragePath?: string;
images?: {
info?: string;
notFound?: string;
error?: string;
};
};
/**

View File

@ -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);
}
}

View File

@ -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;

View File

@ -8,10 +8,7 @@ import { SECOND } from '@/const.js';
*/
const retryDelay = 100;
const lock: (key: string, timeout?: number) => Promise<() => void>
= redisClient
? promisify(redisLock(redisClient, retryDelay))
: async () => () => { };
const lock: (key: string, timeout?: number) => Promise<() => void> = promisify(redisLock(redisClient, retryDelay));
/**
* Get AP Object lock

View File

@ -22,7 +22,7 @@ export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'No
if (note.visibility === 'specified') return false;
// skip if the antenna creator is blocked by the note author
const blockings = await blockingCache.fetch(noteUser.id);
const blockings = (await blockingCache.fetch(noteUser.id)) ?? [];
if (blockings.some(blocking => blocking === antenna.userId)) return false;
if (note.visibility === 'followers') {

View File

@ -3,7 +3,7 @@ import { db } from '@/db/postgre.js';
import { Meta } from '@/models/entities/meta.js';
import { getFetchInstanceMetadataLock } from '@/misc/app-lock.js';
let cache: Meta;
let cache: Meta | undefined;
/**
* Performs the primitive database operation to set the server configuration
@ -57,5 +57,5 @@ export async function fetchMeta(noCache = false): Promise<Meta> {
await getMeta();
return cache;
return cache!;
}

View File

@ -35,7 +35,7 @@ export async function getHtml(url: string, accept = 'text/html, */*', timeout =
return await res.text();
}
export async function getResponse(args: { url: string, method: string, body?: string, headers: Record<string, string>, timeout?: number, size?: number }) {
export async function getResponse(args: { url: string, method: string, body?: string, headers: Record<string, string>, timeout?: number, size?: number, redirect: 'follow' | 'manual' | 'error' = 'follow' }) {
const timeout = args.timeout || 10 * SECOND;
const controller = new AbortController();
@ -47,8 +47,9 @@ export async function getResponse(args: { url: string, method: string, body?: st
method: args.method,
headers: args.headers,
body: args.body,
redirect: args.redirect,
timeout,
size: args.size || 10 * 1024 * 1024,
size: args.size || 10 * 1024 * 1024, // 10 MiB
agent: getAgentByUrl,
signal: controller.signal,
});

View File

@ -0,0 +1,17 @@
import bcrypt from 'bcryptjs';
import * as argon2 from 'argon2';
export async function hashPassword(password: string): Promise<string> {
return argon2.hash(password);
}
export async function comparePassword(password: string, hash: string): Promise<boolean> {
if (isOldAlgorithm(hash)) return bcrypt.compare(password, hash);
return argon2.verify(hash, password);
}
export function isOldAlgorithm(hash: string): boolean {
// bcrypt hashes start with $2[ab]$
return hash.startsWith('$2');
}

View File

@ -75,7 +75,7 @@ export async function toDbReaction(reaction?: string | null, idnReacterHost?: st
const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/);
if (custom) {
const name = custom[1];
const emoji = await Emojis.findOneBy({
const emoji = await Emojis.countBy({
host: reacterHost ?? IsNull(),
name,
});

View File

@ -1,5 +1,11 @@
import { Note } from '@/models/entities/note.js';
export function isPureRenote(note: Note): note is Note & { renoteId: string, text: null, fileIds: null | never[], hasPoll: false } {
return note.renoteId != null && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !note.hasPoll;
return note.renoteId != null
&& note.text == null
&& (
note.fileIds == null
|| note.fileIds.length === 0
)
&& !note.hasPoll;
}

View File

@ -1,7 +1,7 @@
import * as crypto from 'node:crypto';
const L_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz';
const LU_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const LU_CHARS = L_CHARS + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
export function secureRndstrCustom(length = 32, chars: string): string {
const chars_len = chars.length;

View File

@ -6,11 +6,10 @@ 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)
* @param meta a resolved Meta table
* @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;
export async function shouldBlockInstance(host: Instance['host'], meta?: Meta): Promise<boolean> {
const { blockedHosts } = meta ?? await fetchMeta();
return blockedHosts.some(blockedHost => host === blockedHost || host.endsWith('.' + blockedHost));
}

View File

@ -15,8 +15,8 @@ const deadThreshold = 7 * DAY;
* @returns array of punycoded instance hosts that should be skipped (subset of hosts parameter)
*/
export async function skippedInstances(hosts: Array<Instance['host']>): Promise<Array<Instance['host']>> {
// Resolve the boolean promises before filtering
const meta = fetchMeta();
// first check for blocked instances since that info may already be in memory
const meta = await fetchMeta();
const shouldSkip = await Promise.all(hosts.map(host => shouldBlockInstance(host, meta)));
const skipped = hosts.filter((_, i) => shouldSkip[i]);

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -32,12 +32,4 @@ export class Announcement {
length: 1024, nullable: true,
})
public imageUrl: string | null;
constructor(data: Partial<Announcement>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View File

@ -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()

View File

@ -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()

View File

@ -21,7 +21,7 @@ export class App {
})
public userId: User['id'] | null;
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'SET NULL',
nullable: true,
})

View File

@ -11,7 +11,7 @@ export class AttestationChallenge {
@PrimaryColumn(id())
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -35,12 +35,4 @@ export class AttestationChallenge {
default: false,
})
public registrationChallenge: boolean;
constructor(data: Partial<AttestationChallenge>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View File

@ -1,4 +1,4 @@
import { Entity, PrimaryColumn, Index, Column, ManyToOne, OneToOne, JoinColumn } from 'typeorm';
import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm';
import { id } from '../id.js';
import { AccessToken } from './access-token.js';
import { App } from './app.js';

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -19,7 +19,7 @@ export class Clip {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -37,7 +37,7 @@ export class GalleryPost {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -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()

View File

@ -134,7 +134,7 @@ export class Meta {
})
public proxyAccountId: User['id'] | null;
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'SET NULL',
})
@JoinColumn()

View File

@ -16,7 +16,7 @@ export class ModerationLog {
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -2,7 +2,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ
import { noteNotificationTypes } from 'foundkey-js';
import { id } from '../id.js';
import { User } from './user.js';
import { Note } from './note.js';
@Entity()
@Index(['userId', 'threadId'], { unique: true })
@ -20,7 +19,7 @@ export class NoteThreadMuting {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -22,7 +22,7 @@ export class PasswordResetRequest {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -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()

View File

@ -9,7 +9,7 @@ export class Poll {
@PrimaryColumn(id())
public noteId: Note['id'];
@OneToOne(type => Note, {
@OneToOne(() => Note, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -25,7 +25,7 @@ export class RegistryItem {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -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()

View File

@ -16,7 +16,7 @@ export class Signin {
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -14,7 +14,7 @@ export class SwSubscription {
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -21,7 +21,7 @@ export class UserGroupInvitation {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -34,7 +34,7 @@ export class UserGroupInvitation {
})
public userGroupId: UserGroup['id'];
@ManyToOne(type => UserGroup, {
@ManyToOne(() => UserGroup, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -21,7 +21,7 @@ export class UserGroupJoining {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -34,7 +34,7 @@ export class UserGroupJoining {
})
public userGroupId: UserGroup['id'];
@ManyToOne(type => UserGroup, {
@ManyToOne(() => UserGroup, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -25,7 +25,7 @@ export class UserGroup {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -7,7 +7,7 @@ export class UserKeypair {
@PrimaryColumn(id())
public userId: User['id'];
@OneToOne(type => User, {
@OneToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -21,7 +21,7 @@ export class UserListJoining {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -34,7 +34,7 @@ export class UserListJoining {
})
public userListId: UserList['id'];
@ManyToOne(type => UserList, {
@ManyToOne(() => UserList, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -19,7 +19,7 @@ export class UserList {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -18,7 +18,7 @@ export class UserNotePining {
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -27,7 +27,7 @@ export class UserNotePining {
@Column(id())
public noteId: Note['id'];
@ManyToOne(type => Note, {
@ManyToOne(() => Note, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -11,7 +11,7 @@ export class UserProfile {
@PrimaryColumn(id())
public userId: User['id'];
@OneToOne(type => User, {
@OneToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -161,7 +161,7 @@ export class UserProfile {
})
public pinnedPageId: Page['id'] | null;
@OneToOne(type => Page, {
@OneToOne(() => Page, {
onDelete: 'SET NULL',
})
@JoinColumn()

View File

@ -7,7 +7,7 @@ export class UserPublickey {
@PrimaryColumn(id())
public userId: User['id'];
@OneToOne(type => User, {
@OneToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -13,7 +13,7 @@ export class UserSecurityKey {
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -81,7 +81,7 @@ export class User {
})
public avatarId: DriveFile['id'] | null;
@OneToOne(type => DriveFile, {
@OneToOne(() => DriveFile, {
onDelete: 'SET NULL',
})
@JoinColumn()
@ -94,7 +94,7 @@ export class User {
})
public bannerId: DriveFile['id'] | null;
@OneToOne(type => DriveFile, {
@OneToOne(() => DriveFile, {
onDelete: 'SET NULL',
})
@JoinColumn()

View File

@ -21,7 +21,7 @@ export class Webhook {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View File

@ -27,9 +27,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
getPublicProperties(file: DriveFile): DriveFile['properties'] {
if (file.properties.orientation != null) {
// TODO
//const properties = structuredClone(file.properties);
const properties = JSON.parse(JSON.stringify(file.properties));
const properties = structuredClone(file.properties);
if (file.properties.orientation >= 5) {
[properties.width, properties.height] = [properties.height, properties.width];
}
@ -63,50 +61,24 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url);
},
async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> {
const id = typeof user === 'object' ? user.id : user;
const { sum } = await this
.createQueryBuilder('file')
.where('file.userId = :id', { id })
.andWhere('file.isLink = FALSE')
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) || 0;
calcDriveUsageOf(id: User['id']): Promise<number> {
return db.query('SELECT SUM(size) AS sum FROM drive_file WHERE "userId" = $1 AND NOT "isLink"', [id])
.then(res => res[0].sum as number ?? 0);
},
async calcDriveUsageOfHost(host: string): Promise<number> {
const { sum } = await this
.createQueryBuilder('file')
.where('file.userHost = :host', { host: toPuny(host) })
.andWhere('file.isLink = FALSE')
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) || 0;
calcDriveUsageOfHost(host: string): Promise<number> {
return db.query('SELECT SUM(size) AS sum FROM drive_file WHERE "userHost" = $1 AND NOT "isLink"', [toPuny(host)])
.then(res => res[0].sum as number ?? 0);
},
async calcDriveUsageOfLocal(): Promise<number> {
const { sum } = await this
.createQueryBuilder('file')
.where('file.userHost IS NULL')
.andWhere('file.isLink = FALSE')
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) || 0;
calcDriveUsageOfLocal(): Promise<number> {
return db.query('SELECT SUM(size) AS sum FROM drive_file WHERE "userHost" IS NULL AND NOT "isLink"')
.then(res => res[0].sum as number ?? 0);
},
async calcDriveUsageOfRemote(): Promise<number> {
const { sum } = await this
.createQueryBuilder('file')
.where('file.userHost IS NOT NULL')
.andWhere('file.isLink = FALSE')
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) || 0;
calcDriveUsageOfRemote(): Promise<number> {
return db.query('SELECT SUM(size) AS sum FROM drive_file WHERE "userHost" IS NOT NULL AND NOT "isLink"')
.then(res => res[0].sum as number ?? 0);
},
async pack(
@ -154,26 +126,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
const file = typeof src === 'object' ? src : await this.findOneBy({ id: src });
if (file == null) return null;
return await awaitAll<Packed<'DriveFile'>>({
id: file.id,
createdAt: file.createdAt.toISOString(),
name: file.name,
type: file.type,
md5: file.md5,
size: file.size,
isSensitive: file.isSensitive,
blurhash: file.blurhash,
properties: opts.self ? file.properties : this.getPublicProperties(file),
url: opts.self ? file.url : this.getPublicUrl(file, false),
thumbnailUrl: this.getPublicUrl(file, true),
comment: file.comment,
folderId: file.folderId,
folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, {
detail: true,
}) : null,
userId: opts.withUser ? file.userId : null,
user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null,
});
return await this.pack(file, opts);
},
async packMany(

View File

@ -307,7 +307,6 @@ export const UserRepository = db.getRepository(User).extend({
host: user.host,
avatarUrl: this.getAvatarUrlSync(user),
avatarBlurhash: user.avatar?.blurhash || null,
avatarColor: null, // 後方互換性のため
isAdmin: user.isAdmin || falsy,
isModerator: user.isModerator || falsy,
isBot: user.isBot || falsy,
@ -332,7 +331,6 @@ export const UserRepository = db.getRepository(User).extend({
lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null,
bannerUrl: user.banner ? DriveFiles.getPublicUrl(user.banner, false) : null,
bannerBlurhash: user.banner?.blurhash || null,
bannerColor: null, // 後方互換性のため
isLocked: user.isLocked,
isSilenced: user.isSilenced || falsy,
isSuspended: user.isSuspended || falsy,

Some files were not shown because too many files have changed in this diff Show More