summaryrefslogtreecommitdiff
path: root/packages/backend/src/server
diff options
context:
space:
mode:
authortamaina <tamaina@hotmail.co.jp>2022-05-28 01:31:23 +0900
committertamaina <tamaina@hotmail.co.jp>2022-05-28 01:31:23 +0900
commitfa99d9c6fee3a7d6f72e254e0aa55972cd6538fb (patch)
tree43d941bbacc9cfaa911fc785fce0b34af4ef6fcc /packages/backend/src/server
parentMerge branch 'develop' into pizzax-indexeddb (diff)
parentfix(docs): correct information for drive upload (#8736) (diff)
downloadsharkey-fa99d9c6fee3a7d6f72e254e0aa55972cd6538fb.tar.gz
sharkey-fa99d9c6fee3a7d6f72e254e0aa55972cd6538fb.tar.bz2
sharkey-fa99d9c6fee3a7d6f72e254e0aa55972cd6538fb.zip
Merge branch 'develop' into pizzax-indexeddb
Diffstat (limited to 'packages/backend/src/server')
-rw-r--r--packages/backend/src/server/activitypub.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/2fa/register.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/create.ts12
-rw-r--r--packages/backend/src/server/api/endpoints/users/search.ts9
-rw-r--r--packages/backend/src/server/api/openapi/gen-spec.ts16
-rw-r--r--packages/backend/src/server/api/private/signin.ts25
-rw-r--r--packages/backend/src/server/file/send-drive-file.ts9
-rw-r--r--packages/backend/src/server/index.ts26
-rw-r--r--packages/backend/src/server/web/boot.js14
-rw-r--r--packages/backend/src/server/web/index.ts4
-rw-r--r--packages/backend/src/server/web/style.css30
-rw-r--r--packages/backend/src/server/web/views/base.pug14
-rw-r--r--packages/backend/src/server/well-known.ts1
14 files changed, 121 insertions, 51 deletions
diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts
index 133dd36066..a48c2d4122 100644
--- a/packages/backend/src/server/activitypub.ts
+++ b/packages/backend/src/server/activitypub.ts
@@ -1,6 +1,6 @@
import Router from '@koa/router';
import json from 'koa-json-body';
-import httpSignature from 'http-signature';
+import httpSignature from '@peertube/http-signature';
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderNote from '@/remote/activitypub/renderer/note.js';
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index b23ee9e3df..09e43301b7 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -27,7 +27,7 @@ export const paramDef = {
blockedHosts: { type: 'array', nullable: true, items: {
type: 'string',
} },
- themeColor: { type: 'string', nullable: true },
+ themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' },
mascotImageUrl: { type: 'string', nullable: true },
bannerUrl: { type: 'string', nullable: true },
errorImageUrl: { type: 'string', nullable: true },
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
index d5e1b19e54..33f5717728 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
@@ -2,8 +2,8 @@ import bcrypt from 'bcryptjs';
import * as speakeasy from 'speakeasy';
import * as QRCode from 'qrcode';
import config from '@/config/index.js';
-import define from '../../../define.js';
import { UserProfiles } from '@/models/index.js';
+import define from '../../../define.js';
export const meta = {
requireCredential: true,
@@ -40,15 +40,17 @@ export default define(meta, paramDef, async (ps, user) => {
});
// Get the data URL of the authenticator URL
- const dataUrl = await QRCode.toDataURL(speakeasy.otpauthURL({
+ const url = speakeasy.otpauthURL({
secret: secret.base32,
encoding: 'base32',
label: user.username,
issuer: config.host,
- }));
+ });
+ const dataUrl = await QRCode.toDataURL(url);
return {
qr: dataUrl,
+ url,
secret: secret.base32,
label: user.username,
issuer: config.host,
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 40a3ba73ca..955f53bbc1 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -172,10 +172,14 @@ export default define(meta, paramDef, async (ps, user) => {
let files: DriveFile[] = [];
const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null;
if (fileIds != null) {
- files = await DriveFiles.findBy({
- userId: user.id,
- id: In(fileIds),
- });
+ files = await DriveFiles.createQueryBuilder('file')
+ .where('file.userId = :userId AND file.id IN (:...fileIds)', {
+ userId: user.id,
+ fileIds,
+ })
+ .orderBy('array_position(ARRAY[:...fileIds], "id"::text)')
+ .setParameters({ fileIds })
+ .getMany();
}
let renote: Note | null = null;
diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts
index a72a58a843..f93d4f718b 100644
--- a/packages/backend/src/server/api/endpoints/users/search.ts
+++ b/packages/backend/src/server/api/endpoints/users/search.ts
@@ -61,7 +61,14 @@ export default define(meta, paramDef, async (ps, me) => {
.getMany();
} else {
const nameQuery = Users.createQueryBuilder('user')
- .where('user.name ILIKE :query', { query: '%' + ps.query + '%' })
+ .where(new Brackets(qb => {
+ qb.where('user.name ILIKE :query', { query: '%' + ps.query + '%' });
+
+ // Also search username if it qualifies as username
+ if (Users.validateLocalUsername(ps.query)) {
+ qb.orWhere('user.usernameLower LIKE :username', { username: '%' + ps.query.toLowerCase() + '%' });
+ }
+ }))
.andWhere(new Brackets(qb => { qb
.where('user.updatedAt IS NULL')
.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts
index c6e557aefb..3929fff3f7 100644
--- a/packages/backend/src/server/api/openapi/gen-spec.ts
+++ b/packages/backend/src/server/api/openapi/gen-spec.ts
@@ -59,6 +59,18 @@ export function genOpenapiSpec(lang = 'ja-JP') {
desc += ` / **Permission**: *${kind}*`;
}
+ const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json';
+ const schema = endpoint.params;
+
+ if (endpoint.meta.requireFile) {
+ schema.properties.file = {
+ type: 'string',
+ format: 'binary',
+ description: 'The file contents.',
+ };
+ schema.required.push('file');
+ }
+
const info = {
operationId: endpoint.name,
summary: endpoint.name,
@@ -78,8 +90,8 @@ export function genOpenapiSpec(lang = 'ja-JP') {
requestBody: {
required: true,
content: {
- 'application/json': {
- schema: endpoint.params,
+ [requestType]: {
+ schema,
},
},
},
diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts
index 7b66657ad8..0024b8ce3e 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/file/send-drive-file.ts b/packages/backend/src/server/file/send-drive-file.ts
index 027d078ce1..c34e043145 100644
--- a/packages/backend/src/server/file/send-drive-file.ts
+++ b/packages/backend/src/server/file/send-drive-file.ts
@@ -4,11 +4,11 @@ import { dirname } from 'node:path';
import Koa from 'koa';
import send from 'koa-send';
import rename from 'rename';
-import * as tmp from 'tmp';
import { serverLogger } from '../index.js';
import { contentDisposition } from '@/misc/content-disposition.js';
import { DriveFiles } from '@/models/index.js';
import { InternalStorage } from '@/services/drive/internal-storage.js';
+import { createTemp } from '@/misc/create-temp.js';
import { downloadUrl } from '@/misc/download-url.js';
import { detectType } from '@/misc/get-file-info.js';
import { convertToWebp, convertToJpeg, convertToPng } from '@/services/drive/image-processor.js';
@@ -50,12 +50,7 @@ export default async function(ctx: Koa.Context) {
if (!file.storedInternal) {
if (file.isLink && file.uri) { // 期限切れリモートファイル
- const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
- tmp.file((e, path, fd, cleanup) => {
- if (e) return rej(e);
- res([path, cleanup]);
- });
- });
+ const [path, cleanup] = await createTemp();
try {
await downloadUrl(file.uri, path);
diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts
index c1a2a6dfff..f31de2b7f4 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';
@@ -88,10 +89,10 @@ router.get('/avatar/@:acct', async ctx => {
});
router.get('/identicon/:x', async ctx => {
- const [temp] = await createTemp();
+ const [temp, cleanup] = await createTemp();
await genIdenticon(ctx.params.x, fs.createWriteStream(temp));
ctx.set('Content-Type', 'image/png');
- ctx.body = fs.createReadStream(temp);
+ ctx.body = fs.createReadStream(temp).on('close', () => cleanup());
});
router.get('/verify-email/:code', async ctx => {
@@ -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/boot.js b/packages/backend/src/server/web/boot.js
index 751e8619bf..a9ee0df4f1 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -58,15 +58,11 @@
? `?salt=${localStorage.getItem('salt')}`
: '';
- const script = document.createElement('script');
- script.setAttribute('src', `/assets/app.${v}.js${salt}`);
- script.setAttribute('async', 'true');
- script.setAttribute('defer', 'true');
- script.addEventListener('error', async () => {
- await checkUpdate();
- renderError('APP_FETCH_FAILED');
- });
- document.head.appendChild(script);
+ import(`/assets/${CLIENT_ENTRY}${salt}`)
+ .catch(async () => {
+ await checkUpdate();
+ renderError('APP_FETCH_FAILED');
+ })
//#endregion
//#region Theme
diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts
index 061ea50609..9e31f2389e 100644
--- a/packages/backend/src/server/web/index.ts
+++ b/packages/backend/src/server/web/index.ts
@@ -4,6 +4,7 @@
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
+import { PathOrFileDescriptor, readFileSync } from 'node:fs';
import ms from 'ms';
import Koa from 'koa';
import Router from '@koa/router';
@@ -73,6 +74,9 @@ app.use(views(_dirname + '/views', {
extension: 'pug',
options: {
version: config.version,
+ clientEntry: () => process.env.NODE_ENV === 'production' ?
+ config.clientEntry :
+ JSON.parse(readFileSync(`${_dirname}/../../../../../built/_client_dist_/manifest.json`, 'utf-8'))['src/init.ts'].file.replace(/^_client_dist_\//, ''),
config,
},
}));
diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css
index 9c4cd4b9bf..d59f00fe16 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;
+#splashSpinner > .spinner {
+ position: absolute;
+ top: 0;
+ left: 0;
width: 28px;
height: 28px;
- border-radius: 50%;
- border: solid 4px;
+ fill-rule: evenodd;
+ clip-rule: evenodd;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-miterlimit: 1.5;
}
-
-#splashSpinner:before {
- border-color: currentColor;
- opacity: 0.3;
+#splashSpinner > .spinner.bg {
+ opacity: 0.275;
}
-
-#splashSpinner:after {
- position: absolute;
- top: 0;
- border-color: currentColor transparent transparent transparent;
+#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 1513208310..a488e51171 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -50,6 +50,10 @@ html
style
include ../style.css
+ script.
+ var VERSION = "#{version}";
+ var CLIENT_ENTRY = "#{clientEntry()}";
+
script
include ../boot.js
@@ -61,4 +65,14 @@ html
div#splash
img#splashIcon(src= icon || '/static-assets/splash.png')
div#splashSpinner
+ <svg class="spinner bg" viewBox="0 0 152 152" xmlns="http://www.w3.org/2000/svg">
+ <g transform="matrix(1,0,0,1,12,12)">
+ <circle cx="64" cy="64" r="64" style="fill:none;stroke:currentColor;stroke-width:24px;"/>
+ </g>
+ </svg>
+ <svg class="spinner fg" viewBox="0 0 152 152" xmlns="http://www.w3.org/2000/svg">
+ <g transform="matrix(1,0,0,1,12,12)">
+ <path d="M128,64C128,28.654 99.346,0 64,0C99.346,0 128,28.654 128,64Z" style="fill:none;stroke:currentColor;stroke-width:24px;"/>
+ </g>
+ </svg>
block content
diff --git a/packages/backend/src/server/well-known.ts b/packages/backend/src/server/well-known.ts
index 7530b4e0ba..1d094f2edd 100644
--- a/packages/backend/src/server/well-known.ts
+++ b/packages/backend/src/server/well-known.ts
@@ -41,6 +41,7 @@ router.options(allPath, async ctx => {
router.get('/.well-known/host-meta', async ctx => {
ctx.set('Content-Type', xrd);
ctx.body = XRD({ element: 'Link', attributes: {
+ rel: 'lrdd',
type: xrd,
template: `${config.url}${webFingerPath}?resource={uri}`,
} });