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](https://misskey-hub.net/)** is an open source, decentralized social media platform that's free forever! ๐**
+
---
+
+
+
+
+
+
-[โจ 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)
+
+
+
+
+
+
+
+
+
+
+
---
-
-
@@ -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 @@