diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..5d4564c68 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,5 @@ +'โš™๏ธServer': +- packages/backend/**/* + +'๐Ÿ–ฅ๏ธClient': +- packages/client/**/* diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000..057208eda --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,14 @@ +name: "Pull Request Labeler" +on: +- pull_request_target + +jobs: + triage: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d6ec7bbf..163bd21d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,14 @@ You should also include the user name that made the change. - enhance: API: notifications/readใฏ้…ๅˆ—ใงใ‚‚ๅ—ใ‘ไป˜ใ‘ใ‚‹ใ‚ˆใ†ใซ #7667 @tamaina - enhance: ใƒ—ใƒƒใ‚ทใƒฅ้€š็Ÿฅใ‚’่ค‡ๆ•ฐใ‚ขใ‚ซใ‚ฆใƒณใƒˆๅฏพๅฟœใซ #7667 @tamaina - enhance: ใƒ—ใƒƒใ‚ทใƒฅ้€š็Ÿฅใซใ‚ฏใƒชใƒƒใ‚ฏใ‚„actionใ‚’่จญๅฎš #7667 @tamaina +- replaced webpack with Vite @tamaina +- update dependencies @syuilo +- enhance: display URL of QR code for TOTP registration @syuilo +- make CAPTCHA required for signin to improve security @syuilo +- The theme color is now better validated. @Johann150 + Your own theme color may be unset if it was in an invalid format. + Admins should check their instance settings if in doubt. +- Perform port diagnosis at startup only when Listen fails @mei23 ### Bugfixes - Client: fix settings page @tamaina @@ -25,6 +33,14 @@ You should also include the user name that made the change. - Server: await promises when following or unfollowing users @Johann150 - Client: fix abuse reports page to be able to show all reports @Johann150 - Federation: Add rel attribute to host-meta @mei23 +- Client: fix profile picture height in mentions @tamaina +- MFM: more animated functions support `speed` parameter @futchitwo +- Federation: Fix quote renotes containing no text being federated correctly @Johann150 +- Server: fix missing foreign key for reports leading to reports page being unusable @Johann150 +- Server: fix internal in-memory caching @Johann150 +- Server: use correct order of attachments on notes @Johann150 +- Server: prevent crash when processing certain PNGs @syuilo +- Server: Fix unable to generate video thumbnails @mei23 ## 12.110.1 (2022/04/23) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cafca625d..62a7dd9ff 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,7 @@ Before creating an issue, please check the following: - Do not use Issues to ask questions or troubleshooting. - Issues should only be used to feature requests, suggestions, and bug tracking. - Please ask questions or troubleshooting in the [Misskey Forum](https://forum.misskey.io/) or [Discord](https://discord.gg/Wp8gVStHW3). +- Do not close issues that are about to be resolved. It should remain open until a commit that actually resolves it is merged. ## Before implementation When you want to add a feature or fix a bug, **first have the design and policy reviewed in an Issue** (if it is not there, please make one). Without this step, there is a high possibility that the PR will not be merged even if it is implemented. diff --git a/Dockerfile b/Dockerfile index 174e2e9bc..33d5faad1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:18.0.0-alpine3.15 AS base -ENV NODE_ENV=production +ARG NODE_ENV=production WORKDIR /misskey @@ -31,5 +31,6 @@ COPY --from=builder /misskey/packages/backend/built ./packages/backend/built COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node_modules COPY . ./ +ENV NODE_ENV=production CMD ["npm", "run", "migrateandstart"] diff --git a/README.md b/README.md index 19e953aee..9d7c59633 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,32 @@ -[![Misskey](https://github.com/misskey-dev/assets/blob/main/banner.png?raw=true)](https://join.misskey.page/) -
- -**๐ŸŒŽ A forever evolving, interplanetary microblogging platform. ๐Ÿš€** - -**Misskey** is a distributed microblogging platform with advanced features such as Reactions and a highly customizable UI. - -[Learn more](https://misskey-hub.net/) - + + Misskey logo + + +**๐ŸŒŽ **[Misskey](https://misskey-hub.net/)** is an open source, decentralized social media platform that's free forever! ๐Ÿš€** + --- + + + find an instance + + + create an instance -[โœจ Find an instance](https://misskey-hub.net/instances.html) -โ€ข -[๐Ÿ“ฆ Create your own instance](https://misskey-hub.net/docs/install.html) -โ€ข -[๐Ÿ› ๏ธ Contribute](./CONTRIBUTING.md) -โ€ข -[๐Ÿš€ Join the community](https://discord.gg/Wp8gVStHW3) + + become a contributor + + + join the community + + + + become a patron + + --- -Become a Patron! -
@@ -30,17 +35,16 @@ ## โœจ Features - **ActivityPub support**\ - It is possible to interact with other software. +Not on Misskey? No problem! Not only can Misskey instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed! - **Reactions**\ - You can add "reactions" to each post, making it easy for you to express your feelings. +You can add emoji reactions to any post! No longer are you bound by a like button, show everyone exactly how you feel with the tap of a button. - **Drive**\ - An interface to manage uploaded files such as images, videos, sounds, etc. - You can also organize your favorite content into folders, making it easy to share again. +With Misskey's built in drive, you get cloud storage right in your social media, where you can upload any files, make folders, and find media from posts you've made! - **Rich Web UI**\ - Misskey has a rich WebUI by default. - It is highly customizable by flexibly changing the layout and installing various widgets and themes. - Furthermore, plug-ins can be created using AiScript, a original programming language. -- and more... + Misskey has a rich and easy to use Web UI! + It is highly customizable, from changing the layout and adding widgets to making custom themes. + Furthermore, plugins can be created using AiScript, an original programming language. +- And much more...
diff --git a/assets/title_float.svg b/assets/title_float.svg new file mode 100644 index 000000000..43205ac1c Binary files /dev/null and b/assets/title_float.svg differ diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 000000000..8f31cf7fb --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: misskey +version: 0.0.0 diff --git a/chart/files/default.yml b/chart/files/default.yml new file mode 100644 index 000000000..a9ef22f42 --- /dev/null +++ b/chart/files/default.yml @@ -0,0 +1,165 @@ +#โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +# Misskey configuration +#โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + +# โ”Œโ”€โ”€โ”€โ”€โ”€โ” +#โ”€โ”€โ”€โ”˜ URL โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +# Final accessible URL seen by a user. +# url: https://example.tld/ + +# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE +# URL SETTINGS AFTER THAT! + +# โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +#โ”€โ”€โ”€โ”˜ Port and TLS settings โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +# +# Misskey supports two deployment options for public. +# + +# Option 1: With Reverse Proxy +# +# +----- https://example.tld/ ------------+ +# +------+ |+-------------+ +----------------+| +# | User | ---> || Proxy (443) | ---> | Misskey (3000) || +# +------+ |+-------------+ +----------------+| +# +---------------------------------------+ +# +# You need to setup reverse proxy. (eg. nginx) +# You do not define 'https' section. + +# Option 2: Standalone +# +# +- https://example.tld/ -+ +# +------+ | +---------------+ | +# | User | ---> | | Misskey (443) | | +# +------+ | +---------------+ | +# +------------------------+ +# +# You need to run Misskey as root. +# You need to set Certificate in 'https' section. + +# To use option 1, uncomment below line. +port: 3000 # A port that your Misskey server should listen. + +# To use option 2, uncomment below lines. +#port: 443 + +#https: +# # path for certification +# key: /etc/letsencrypt/live/example.tld/privkey.pem +# cert: /etc/letsencrypt/live/example.tld/fullchain.pem + +# โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +#โ”€โ”€โ”€โ”˜ PostgreSQL configuration โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +db: + host: localhost + port: 5432 + + # Database name + db: misskey + + # Auth + user: example-misskey-user + pass: example-misskey-pass + + # Whether disable Caching queries + #disableCache: true + + # Extra Connection options + #extra: + # ssl: true + +# โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +#โ”€โ”€โ”€โ”˜ Redis configuration โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +redis: + host: localhost + port: 6379 + #pass: example-pass + #prefix: example-prefix + #db: 1 + +# โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +#โ”€โ”€โ”€โ”˜ Elasticsearch configuration โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +#elasticsearch: +# host: localhost +# port: 9200 +# ssl: false +# user: +# pass: + +# โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +#โ”€โ”€โ”€โ”˜ ID generation โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +# You can select the ID generation method. +# You don't usually need to change this setting, but you can +# change it according to your preferences. + +# Available methods: +# aid ... Short, Millisecond accuracy +# meid ... Similar to ObjectID, Millisecond accuracy +# ulid ... Millisecond accuracy +# objectid ... This is left for backward compatibility + +# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE +# ID SETTINGS AFTER THAT! + +id: "aid" +# โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +#โ”€โ”€โ”€โ”˜ Other configuration โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +# Whether disable HSTS +#disableHsts: true + +# Number of worker processes +#clusterLimit: 1 + +# Job concurrency per worker +# deliverJobConcurrency: 128 +# inboxJobConcurrency: 16 + +# Job rate limiter +# deliverJobPerSec: 128 +# inboxJobPerSec: 16 + +# Job attempts +# deliverJobMaxAttempts: 12 +# inboxJobMaxAttempts: 8 + +# IP address family used for outgoing request (ipv4, ipv6 or dual) +#outgoingAddressFamily: ipv4 + +# Syslog option +#syslog: +# host: localhost +# port: 514 + +# Proxy for HTTP/HTTPS +#proxy: http://127.0.0.1:3128 + +#proxyBypassHosts: [ +# 'example.com', +# '192.0.2.8' +#] + +# Proxy for SMTP/SMTPS +#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT +#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4 +#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5 + +# Media Proxy +#mediaProxy: https://example.com/proxy + +# Sign to ActivityPub GET request (default: false) +#signToActivityPubGet: true + +#allowedPrivateNetworks: [ +# '127.0.0.1/32' +#] + +# Upload or download file size limits (bytes) +#maxFileSize: 262144000 diff --git a/chart/templates/ConfigMap.yml b/chart/templates/ConfigMap.yml new file mode 100644 index 000000000..37c25e086 --- /dev/null +++ b/chart/templates/ConfigMap.yml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "misskey.fullname" . }}-configuration +data: + default.yml: |- + {{ .Files.Get "files/default.yml"|nindent 4 }} + url: {{ .Values.url }} diff --git a/chart/templates/Deployment.yml b/chart/templates/Deployment.yml new file mode 100644 index 000000000..d16aece91 --- /dev/null +++ b/chart/templates/Deployment.yml @@ -0,0 +1,47 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "misskey.fullname" . }} + labels: + {{- include "misskey.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "misskey.selectorLabels" . | nindent 6 }} + replicas: 1 + template: + metadata: + labels: + {{- include "misskey.selectorLabels" . | nindent 8 }} + spec: + containers: + - name: misskey + image: {{ .Values.image }} + env: + - name: NODE_ENV + value: {{ .Values.environment }} + volumeMounts: + - name: {{ include "misskey.fullname" . }}-configuration + mountPath: /misskey/.config + readOnly: true + ports: + - containerPort: 3000 + - name: postgres + image: postgres:14-alpine + env: + - name: POSTGRES_USER + value: "example-misskey-user" + - name: POSTGRES_PASSWORD + value: "example-misskey-pass" + - name: POSTGRES_DB + value: "misskey" + ports: + - containerPort: 5432 + - name: redis + image: redis:alpine + ports: + - containerPort: 6379 + volumes: + - name: {{ include "misskey.fullname" . }}-configuration + configMap: + name: {{ include "misskey.fullname" . }}-configuration diff --git a/chart/templates/Service.yml b/chart/templates/Service.yml new file mode 100644 index 000000000..320958129 --- /dev/null +++ b/chart/templates/Service.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "misskey.fullname" . }} + annotations: + dev.okteto.com/auto-ingress: "true" +spec: + type: ClusterIP + ports: + - port: 3000 + protocol: TCP + name: http + selector: + {{- include "misskey.selectorLabels" . | nindent 4 }} diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl new file mode 100644 index 000000000..a5a2499f3 --- /dev/null +++ b/chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "misskey.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "misskey.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "misskey.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "misskey.labels" -}} +helm.sh/chart: {{ include "misskey.chart" . }} +{{ include "misskey.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "misskey.selectorLabels" -}} +app.kubernetes.io/name: {{ include "misskey.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "misskey.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "misskey.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/chart/values.yml b/chart/values.yml new file mode 100644 index 000000000..a7031538a --- /dev/null +++ b/chart/values.yml @@ -0,0 +1,3 @@ +url: https://example.tld/ +image: okteto.dev/misskey +environment: production diff --git a/docker-compose.yml b/docker-compose.yml index e1d51668a..0bf17a555 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: - redis # - es ports: - - "127.0.0.1:3000:3000" + - "3000:3000" networks: - internal_network - external_network diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index c52762bf3..f64246d15 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -425,7 +425,7 @@ quoteQuestion: "ๅผ•็”จใจใ—ใฆๆทปไป˜ใ—ใพใ™ใ‹๏ผŸ" noMessagesYet: "ใพใ ใƒใƒฃใƒƒใƒˆใฏใ‚ใ‚Šใพใ›ใ‚“" newMessageExists: "ๆ–ฐใ—ใ„ใƒกใƒƒใ‚ปใƒผใ‚ธใŒใ‚ใ‚Šใพใ™" onlyOneFileCanBeAttached: "ใƒกใƒƒใ‚ปใƒผใ‚ธใซๆทปไป˜ใงใใ‚‹ใƒ•ใ‚กใ‚คใƒซใฏใฒใจใคใงใ™" -signinRequired: "ใƒญใ‚ฐใ‚คใƒณใ—ใฆใใ ใ•ใ„" +signinRequired: "็ถš่กŒใ™ใ‚‹ๅ‰ใซใ€ใ‚ตใ‚คใƒณใ‚ขใƒƒใƒ—ใพใŸใฏใ‚ตใ‚คใƒณใ‚คใƒณใŒๅฟ…่ฆใงใ™" invitations: "ๆ‹›ๅพ…" invitationCode: "ๆ‹›ๅพ…ใ‚ณใƒผใƒ‰" checking: "็ขบ่ชใ—ใฆใ„ใพใ™" diff --git a/okteto.yml b/okteto.yml new file mode 100644 index 000000000..e2996fbbc --- /dev/null +++ b/okteto.yml @@ -0,0 +1,6 @@ +build: + misskey: + args: + - NODE_ENV=development +deploy: + - helm upgrade --install misskey chart --set image=${OKTETO_BUILD_MISSKEY_IMAGE} --set url="https://misskey-$(kubectl config view --minify -o jsonpath='{..namespace}').cloud.okteto.net" --set environment=development diff --git a/packages/backend/migration/1652859567549-uniform-themecolor.js b/packages/backend/migration/1652859567549-uniform-themecolor.js new file mode 100644 index 000000000..8da1fd7fb --- /dev/null +++ b/packages/backend/migration/1652859567549-uniform-themecolor.js @@ -0,0 +1,36 @@ +import tinycolor from 'tinycolor2'; + +export class uniformThemecolor1652859567549 { + name = 'uniformThemecolor1652859567549' + + async up(queryRunner) { + const formatColor = (color) => { + let tc = new tinycolor(color); + if (tc.isValid()) { + return tc.toHexString(); + } else { + return null; + } + }; + + await queryRunner.query('SELECT "id", "themeColor" FROM "instance" WHERE "themeColor" IS NOT NULL') + .then(instances => Promise.all(instances.map(instance => { + // update theme color to uniform format, e.g. #00ff00 + // invalid theme colors get set to null + return queryRunner.query('UPDATE "instance" SET "themeColor" = $1 WHERE "id" = $2', [formatColor(instance.themeColor), instance.id]); + }))); + + // also fix own theme color + await queryRunner.query('SELECT "themeColor" FROM "meta" WHERE "themeColor" IS NOT NULL LIMIT 1') + .then(metas => { + if (metas.length > 0) { + return queryRunner.query('UPDATE "meta" SET "themeColor" = $1', [formatColor(metas[0].themeColor)]); + } + }); + } + + async down(queryRunner) { + // The original representation is not stored, so migrating back is not possible. + // The new format also works in older versions so this is not a problem. + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 646758895..ab0e9fbc1 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -77,7 +77,6 @@ "os-utils": "0.0.14", "parse5": "6.0.1", "pg": "8.7.3", - "portscanner": "2.2.0", "private-ip": "2.3.3", "probe-image-size": "7.2.3", "promise-limit": "2.7.0", @@ -97,7 +96,7 @@ "s-age": "1.1.2", "sanitize-html": "2.7.0", "semver": "7.3.7", - "sharp": "0.30.4", + "sharp": "0.29.3", "speakeasy": "2.0.0", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", @@ -151,7 +150,6 @@ "@types/nodemailer": "6.4.4", "@types/oauth": "0.9.1", "@types/parse5": "6.0.3", - "@types/portscanner": "2.1.1", "@types/pug": "2.0.6", "@types/punycode": "2.1.0", "@types/qrcode": "1.4.2", diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 09d20f936..bf5196048 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -5,7 +5,6 @@ import * as os from 'node:os'; import cluster from 'node:cluster'; import chalk from 'chalk'; import chalkTemplate from 'chalk-template'; -import * as portscanner from 'portscanner'; import semver from 'semver'; import Logger from '@/services/logger.js'; @@ -48,11 +47,6 @@ function greet() { bootLogger.info(`Misskey v${meta.version}`, null, true); } -function isRoot() { - // maybe process.getuid will be undefined under not POSIX environment (e.g. Windows) - return process.getuid != null && process.getuid() === 0; -} - /** * Init master process */ @@ -67,7 +61,6 @@ export async function masterMain() { showNodejsVersion(); config = loadConfigBoot(); await connectDb(); - await validatePort(config); } catch (e) { bootLogger.error('Fatal error occurred during initialization', null, true); process.exit(1); @@ -97,8 +90,6 @@ function showEnvironment(): void { logger.warn('The environment is not in production mode.'); logger.warn('DO NOT USE FOR PRODUCTION PURPOSE!', null, true); } - - logger.info(`You ${isRoot() ? '' : 'do not '}have root privileges`); } function showNodejsVersion(): void { @@ -152,29 +143,6 @@ async function connectDb(): Promise { } } -async function validatePort(config: Config): Promise { - const isWellKnownPort = (port: number) => port < 1024; - - async function isPortAvailable(port: number): Promise { - return await portscanner.checkPortStatus(port, '127.0.0.1') === 'closed'; - } - - if (config.port == null || Number.isNaN(config.port)) { - bootLogger.error('The port is not configured. Please configure port.', null, true); - process.exit(1); - } - - if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) { - bootLogger.error('You need root privileges to listen on well-known port on Linux', null, true); - process.exit(1); - } - - if (!await isPortAvailable(config.port)) { - bootLogger.error(`Port ${config.port} is already in use`, null, true); - process.exit(1); - } -} - async function spawnWorkers(limit: number = 1) { const workers = Math.min(limit, os.cpus().length); bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`); @@ -186,6 +154,10 @@ function spawnWorker(): Promise { return new Promise(res => { const worker = cluster.fork(); worker.on('message', message => { + if (message === 'listenFailed') { + bootLogger.error(`The server Listen failed due to the previous error.`); + process.exit(1); + } if (message !== 'ready') return; res(); }); diff --git a/packages/backend/src/remote/activitypub/kernel/announce/note.ts b/packages/backend/src/remote/activitypub/kernel/announce/note.ts index 680749f4d..052751c65 100644 --- a/packages/backend/src/remote/activitypub/kernel/announce/note.ts +++ b/packages/backend/src/remote/activitypub/kernel/announce/note.ts @@ -9,6 +9,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { getApLock } from '@/misc/app-lock.js'; import { parseAudience } from '../../audience.js'; import { StatusError } from '@/misc/fetch.js'; +import { Notes } from '@/models/index.js'; const logger = apLogger; @@ -52,6 +53,8 @@ export default async function(resolver: Resolver, actor: CacheableRemoteUser, ac throw e; } + if (!await Notes.isVisibleForMe(renote, actor)) return 'skip: invalid actor for this activity'; + logger.info(`Creating the (Re)Note: ${uri}`); const activityAudience = await parseAudience(actor, activity.to, activity.cc); diff --git a/packages/backend/src/remote/activitypub/kernel/delete/index.ts b/packages/backend/src/remote/activitypub/kernel/delete/index.ts index 4c06a9de0..c7064f553 100644 --- a/packages/backend/src/remote/activitypub/kernel/delete/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/delete/index.ts @@ -13,37 +13,37 @@ export default async (actor: CacheableRemoteUser, activity: IDelete): Promise { userId: user.id, fileIds, }) - .orderBy('array_position(ARRAY[:...fileIds], "id")') + .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') .setParameters({ fileIds }) .getMany(); } diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts index 7b66657ad..0024b8ce3 100644 --- a/packages/backend/src/server/api/private/signin.ts +++ b/packages/backend/src/server/api/private/signin.ts @@ -1,20 +1,25 @@ +import { randomBytes } from 'node:crypto'; import Koa from 'koa'; import bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; -import signin from '../common/signin.js'; +import { IsNull } from 'typeorm'; import config from '@/config/index.js'; import { Users, Signins, UserProfiles, UserSecurityKeys, AttestationChallenges } from '@/models/index.js'; import { ILocalUser } from '@/models/entities/user.js'; import { genId } from '@/misc/gen-id.js'; +import { fetchMeta } from '@/misc/fetch-meta.js'; +import { verifyHcaptcha, verifyRecaptcha } from '@/misc/captcha.js'; import { verifyLogin, hash } from '../2fa.js'; -import { randomBytes } from 'node:crypto'; -import { IsNull } from 'typeorm'; +import signin from '../common/signin.js'; export default async (ctx: Koa.Context) => { ctx.set('Access-Control-Allow-Origin', config.url); ctx.set('Access-Control-Allow-Credentials', 'true'); const body = ctx.request.body as any; + + const instance = await fetchMeta(true); + const username = body['username']; const password = body['password']; const token = body['token']; @@ -79,6 +84,18 @@ export default async (ctx: Koa.Context) => { } if (!profile.twoFactorEnabled) { + if (instance.enableHcaptcha && instance.hcaptchaSecretKey) { + await verifyHcaptcha(instance.hcaptchaSecretKey, body['hcaptcha-response']).catch(e => { + ctx.throw(400, e); + }); + } + + if (instance.enableRecaptcha && instance.recaptchaSecretKey) { + await verifyRecaptcha(instance.recaptchaSecretKey, body['g-recaptcha-response']).catch(e => { + ctx.throw(400, e); + }); + } + if (same) { signin(ctx, user); return; @@ -155,7 +172,7 @@ export default async (ctx: Koa.Context) => { body.credentialId .replace(/-/g, '+') .replace(/_/g, '/'), - 'base64' + 'base64', ).toString('hex'), }); diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index c1a2a6dff..cd061da72 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -2,6 +2,7 @@ * Core Server */ +import cluster from 'node:cluster'; import * as fs from 'node:fs'; import * as http from 'node:http'; import Koa from 'koa'; @@ -142,5 +143,26 @@ export default () => new Promise(resolve => { initializeStreamingServer(server); + server.on('error', e => { + switch ((e as any).code) { + case 'EACCES': + serverLogger.error(`You do not have permission to listen on port ${config.port}.`); + break; + case 'EADDRINUSE': + serverLogger.error(`Port ${config.port} is already in use by another process.`); + break; + default: + serverLogger.error(e); + break; + } + + if (cluster.isWorker) { + process.send!('listenFailed'); + } else { + // disableClustering + process.exit(1); + } + }); + server.listen(config.port, resolve); }); diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css index 9c4cd4b9b..d59f00fe1 100644 --- a/packages/backend/src/server/web/style.css +++ b/packages/backend/src/server/web/style.css @@ -39,28 +39,24 @@ html { width: 28px; height: 28px; transform: translateY(70px); + color: var(--accent); } - -#splashSpinner:before, -#splashSpinner:after { - content: " "; - display: block; - box-sizing: border-box; - width: 28px; - height: 28px; - border-radius: 50%; - border: solid 4px; -} - -#splashSpinner:before { - border-color: currentColor; - opacity: 0.3; -} - -#splashSpinner:after { +#splashSpinner > .spinner { position: absolute; top: 0; - border-color: currentColor transparent transparent transparent; + left: 0; + width: 28px; + height: 28px; + fill-rule: evenodd; + clip-rule: evenodd; + stroke-linecap: round; + stroke-linejoin: round; + stroke-miterlimit: 1.5; +} +#splashSpinner > .spinner.bg { + opacity: 0.275; +} +#splashSpinner > .spinner.fg { animation: splashSpinner 0.5s linear infinite; } diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index d79354d11..a488e5117 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -65,4 +65,14 @@ html div#splash img#splashIcon(src= icon || '/static-assets/splash.png') div#splashSpinner + + + + + + + + + + block content diff --git a/packages/backend/src/services/drive/generate-video-thumbnail.ts b/packages/backend/src/services/drive/generate-video-thumbnail.ts index da93bc97c..ef75a9f58 100644 --- a/packages/backend/src/services/drive/generate-video-thumbnail.ts +++ b/packages/backend/src/services/drive/generate-video-thumbnail.ts @@ -1,7 +1,7 @@ import * as fs from 'node:fs'; import * as tmp from 'tmp'; import { IImage, convertToJpeg } from './image-processor.js'; -import * as FFmpeg from 'fluent-ffmpeg'; +import FFmpeg from 'fluent-ffmpeg'; export async function GenerateVideoThumbnail(path: string): Promise { const [outDir, cleanup] = await new Promise<[string, any]>((res, rej) => { diff --git a/packages/backend/src/services/fetch-instance-metadata.ts b/packages/backend/src/services/fetch-instance-metadata.ts index d5294c5fe..029c388dc 100644 --- a/packages/backend/src/services/fetch-instance-metadata.ts +++ b/packages/backend/src/services/fetch-instance-metadata.ts @@ -1,5 +1,6 @@ import { DOMWindow, JSDOM } from 'jsdom'; import fetch from 'node-fetch'; +import tinycolor from 'tinycolor2'; import { getJson, getHtml, getAgentByUrl } from '@/misc/fetch.js'; import { Instance } from '@/models/entities/instance.js'; import { Instances } from '@/models/index.js'; @@ -208,16 +209,11 @@ async function fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | nul } async function getThemeColor(doc: DOMWindow['document'] | null, manifest: Record | null): Promise { - if (doc) { - const themeColor = doc.querySelector('meta[name="theme-color"]')?.getAttribute('content'); + const themeColor = doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') || manifest?.theme_color; - if (themeColor) { - return themeColor; - } - } - - if (manifest) { - return manifest.theme_color; + if (themeColor) { + const color = new tinycolor(themeColor); + if (color.isValid()) return color.toHexString(); } return null; diff --git a/packages/backend/src/services/note/reaction/create.ts b/packages/backend/src/services/note/reaction/create.ts index 5a0948bca..5cb7ebdcd 100644 --- a/packages/backend/src/services/note/reaction/create.ts +++ b/packages/backend/src/services/note/reaction/create.ts @@ -27,6 +27,11 @@ export default async (user: { id: User['id']; host: User['host']; }, note: Note, } } + // check visibility + if (!await Notes.isVisibleForMe(note, user)) { + throw new IdentifiableError('68e9d2d1-48bf-42c2-b90a-b20e09fd3d48', 'Note not accessible for you.'); + } + // TODO: cache reaction = await toDbReaction(reaction, user.host); diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 0a495b339..e900746be 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -1,14 +1,15 @@ import * as fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; +import * as childProcess from 'child_process'; +import * as http from 'node:http'; +import { SIGKILL } from 'constants'; import * as WebSocket from 'ws'; import * as misskey from 'misskey-js'; import fetch from 'node-fetch'; import FormData from 'form-data'; -import * as childProcess from 'child_process'; -import * as http from 'node:http'; +import { DataSource } from 'typeorm'; import loadConfig from '../src/config/load.js'; -import { SIGKILL } from 'constants'; import { entities } from '../src/db/postgre.js'; const _filename = fileURLToPath(import.meta.url); @@ -27,29 +28,29 @@ export const async = (fn: Function) => (done: Function) => { export const request = async (endpoint: string, params: any, me?: any): Promise<{ body: any, status: number }> => { const auth = me ? { - i: me.token + i: me.token, } : {}; const res = await fetch(`http://localhost:${port}/api${endpoint}`, { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, - body: JSON.stringify(Object.assign(auth, params)) + body: JSON.stringify(Object.assign(auth, params)), }); const status = res.status; const body = res.status !== 204 ? await res.json().catch() : null; return { - body, status + body, status, }; }; export const signup = async (params?: any): Promise => { const q = Object.assign({ username: 'test', - password: 'test' + password: 'test', }, params); const res = await request('/signup', q); @@ -59,7 +60,7 @@ export const signup = async (params?: any): Promise => { export const post = async (user: any, params?: misskey.Endpoints['notes/create']['req']): Promise => { const q = Object.assign({ - text: 'test' + text: 'test', }, params); const res = await request('/notes/create', q, user); @@ -70,26 +71,26 @@ export const post = async (user: any, params?: misskey.Endpoints['notes/create'] export const react = async (user: any, note: any, reaction: string): Promise => { await request('/notes/reactions/create', { noteId: note.id, - reaction: reaction + reaction: reaction, }, user); }; export const uploadFile = (user: any, path?: string): Promise => { - const formData = new FormData(); - formData.append('i', user.token); - formData.append('file', fs.createReadStream(path || _dirname + '/resources/Lenna.png')); + const formData = new FormData(); + formData.append('i', user.token); + formData.append('file', fs.createReadStream(path || _dirname + '/resources/Lenna.png')); - return fetch(`http://localhost:${port}/api/drive/files/create`, { - method: 'post', - body: formData, - timeout: 30 * 1000, - }).then(res => { - if (!res.ok) { - throw `${res.status} ${res.statusText}`; - } else { - return res.json(); - } - }); + return fetch(`http://localhost:${port}/api/drive/files/create`, { + method: 'post', + body: formData, + timeout: 30 * 1000, + }).then(res => { + if (!res.ok) { + throw `${res.status} ${res.statusText}`; + } else { + return res.json(); + } + }); }; export function connectStream(user: any, channel: string, listener: (message: Record) => any, params?: any): Promise { @@ -112,8 +113,8 @@ export function connectStream(user: any, channel: string, listener: (message: Re channel: channel, id: 'a', pong: true, - params: params - } + params: params, + }, })); }); }); @@ -124,8 +125,8 @@ export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status? return await new Promise((resolve, reject) => { const req = http.request(`http://localhost:${port}${path}`, { headers: { - Accept: accept - } + Accept: accept, + }, }, res => { if (res.statusCode! >= 400) { reject(res); @@ -146,7 +147,7 @@ export function launchServer(callbackSpawnedProcess: (p: childProcess.ChildProce return (done: (err?: Error) => any) => { const p = childProcess.spawn('node', [_dirname + '/../index.js'], { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], - env: { NODE_ENV: 'test', PATH: process.env.PATH } + env: { NODE_ENV: 'test', PATH: process.env.PATH }, }); callbackSpawnedProcess(p); p.on('message', message => { @@ -158,12 +159,7 @@ export function launchServer(callbackSpawnedProcess: (p: childProcess.ChildProce export async function initTestDb(justBorrow = false, initEntities?: any[]) { if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test'; - try { - const conn = await getConnection(); - await conn.close(); - } catch (e) {} - - return await createConnection({ + return new DataSource({ type: 'postgres', host: config.db.host, port: config.db.port, @@ -172,7 +168,7 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) { database: config.db.db, synchronize: true && !justBorrow, dropSchema: true && !justBorrow, - entities: initEntities || entities + entities: initEntities || entities, }); } @@ -185,7 +181,7 @@ export function startServer(timeout = 30 * 1000): Promise rej(e)); diff --git a/packages/backend/yarn.lock b/packages/backend/yarn.lock index 45aa8136b..9c40715e1 100644 --- a/packages/backend/yarn.lock +++ b/packages/backend/yarn.lock @@ -705,11 +705,6 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== -"@types/portscanner@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/portscanner/-/portscanner-2.1.1.tgz#89d5094e16f3d941f20f3889dfa5d3a164b3dd3b" - integrity sha512-1NsVIbgBKvrqxwtMN0V6CLji1ERwKSI/RWz0J3y++CzSwYNGBStCfpIFgxV3ZwxsDR5PoZqoUWhwraDm+Ztn0Q== - "@types/pug@2.0.6": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.6.tgz#f830323c88172e66826d0bde413498b61054b5a6" @@ -1249,19 +1244,7 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= -async@>=0.2.9: - version "3.2.0" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" - integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== - -async@^2.6.0: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - -async@^3.2.3: +async@>=0.2.9, async@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== @@ -1872,7 +1855,7 @@ color-support@^1.1.2: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -color@^4.2.3: +color@^4.0.1: version "4.2.3" resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== @@ -2279,16 +2262,16 @@ detect-file@^1.0.0: resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + detect-libc@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.0.tgz#c528bc09bc6d1aa30149228240917c225448f204" integrity sha512-S55LzUl8HUav8l9E2PBTlC5PAJrHK7tkM+XXFGD+fbsbkTzhCpG6K05LxJcUOEWzMa4v6ptcMZ9s3fOdJDu0Zw== -detect-libc@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" - integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== - detect-node@2.1.0, detect-node@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" @@ -3861,13 +3844,6 @@ is-negative-zero@^2.0.1: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== -is-number-like@^1.0.3: - version "1.0.8" - resolved "https://registry.yarnpkg.com/is-number-like/-/is-number-like-1.0.8.tgz#2e129620b50891042e44e9bbbb30593e75cfbbe3" - integrity sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA== - dependencies: - lodash.isfinite "^3.3.2" - is-number-object@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" @@ -4498,11 +4474,6 @@ lodash.isequal@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.isfinite@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3" - integrity sha1-+4m2WpqAKBgz8LdHizpRBPiY67M= - lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" @@ -4548,7 +4519,7 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21: +lodash@^4.17.11, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4985,7 +4956,7 @@ node-addon-api@^1.2.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== -node-addon-api@^4.3.0: +node-addon-api@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== @@ -5541,14 +5512,6 @@ pngjs@^5.0.0: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw== -portscanner@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/portscanner/-/portscanner-2.2.0.tgz#6059189b3efa0965c9d96a56b958eb9508411cf1" - integrity sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw== - dependencies: - async "^2.6.0" - is-number-like "^1.0.3" - postcss@^8.3.11: version "8.3.11" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858" @@ -5580,10 +5543,10 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -prebuild-install@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870" - integrity sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg== +prebuild-install@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.0.tgz#991b6ac16c81591ba40a6d5de93fb33673ac1370" + integrity sha512-CNcMgI1xBypOyGqjp3wOc8AAo1nMhZS3Cwd3iHIxOdAUbb+YxdNuM4Z5iIrZ8RLvOsf3F3bl7b7xGq6DjQoNYA== dependencies: detect-libc "^2.0.0" expand-template "^2.0.3" @@ -6227,7 +6190,7 @@ seedrandom@3.0.5: resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== -semver@7.3.7, semver@^7.3.7: +semver@7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== @@ -6288,17 +6251,17 @@ sha.js@^2.4.11: inherits "^2.0.1" safe-buffer "^5.0.1" -sharp@0.30.4: - version "0.30.4" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.30.4.tgz#73d9daa63bbc20da189c9328d75d5d395fc8fb73" - integrity sha512-3Onig53Y6lji4NIZo69s14mERXXY/GV++6CzOYx/Rd8bnTwbhFbL09WZd7Ag/CCnA0WxFID8tkY0QReyfL6v0Q== +sharp@0.29.3: + version "0.29.3" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.3.tgz#0da183d626094c974516a48fab9b3e4ba92eb5c2" + integrity sha512-fKWUuOw77E4nhpyzCCJR1ayrttHoFHBT2U/kR/qEMRhvPEcluG4BKj324+SCO1e84+knXHwhJ1HHJGnUt4ElGA== dependencies: - color "^4.2.3" - detect-libc "^2.0.1" - node-addon-api "^4.3.0" - prebuild-install "^7.0.1" - semver "^7.3.7" - simple-get "^4.0.1" + color "^4.0.1" + detect-libc "^1.0.3" + node-addon-api "^4.2.0" + prebuild-install "^7.0.0" + semver "^7.3.5" + simple-get "^4.0.0" tar-fs "^2.1.1" tunnel-agent "^0.6.0" @@ -6343,7 +6306,7 @@ simple-concat@^1.0.0: resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== -simple-get@^4.0.0, simple-get@^4.0.1: +simple-get@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== diff --git a/packages/client/package.json b/packages/client/package.json index 9a99728d0..22e1de657 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -42,7 +42,6 @@ "nested-property": "4.0.0", "parse5": "6.0.1", "photoswipe": "5.2.7", - "portscanner": "2.2.0", "prismjs": "1.28.0", "private-ip": "2.3.3", "promise-limit": "2.7.0", @@ -75,6 +74,13 @@ "vuedraggable": "4.0.1", "wavesurfer.js": "6.0.1", "websocket": "1.0.34", + "@vitejs/plugin-vue": "2.3.3", + "@vue/compiler-sfc": "3.2.33", + "@rollup/plugin-alias": "3.1.9", + "@rollup/plugin-json": "4.1.0", + "rollup": "2.73.0", + "typescript": "4.6.4", + "vite": "2.9.9", "ws": "8.6.0" }, "devDependencies": { @@ -99,14 +105,6 @@ "@types/ws": "8.5.3", "@typescript-eslint/eslint-plugin": "5.23.0", "@typescript-eslint/parser": "5.23.0", - "@vitejs/plugin-vue": "2.3.3", - "@vue/compiler-sfc": "3.2.33", - "@rollup/plugin-alias": "3.1.9", - "@rollup/plugin-json": "4.1.0", - "rollup": "2.73.0", - "typescript": "4.6.4", - - "vite": "2.9.9", "eslint": "8.15.0", "eslint-plugin-vue": "8.7.1", "cross-env": "7.0.3", diff --git a/packages/client/src/components/global/loading.vue b/packages/client/src/components/global/loading.vue index 43ea1395e..fa2ce1800 100644 --- a/packages/client/src/components/global/loading.vue +++ b/packages/client/src/components/global/loading.vue @@ -1,6 +1,17 @@ @@ -19,7 +30,7 @@ const props = withDefaults(defineProps<{