mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-16 06:15:30 +02:00
enhance(backend): bundle backend using Rolldown (#17068)
* enhance(backend): bundle backend using rolldown
* fix
* fix [ci skip]
* remove unused build script
* fix
* enhance: 起動からlistenまでかかる時間を減らす (MisskeyIO#1410)
* ✌️
* fix
* update rolldown
* fix(backend): extract static error classes to avoid rolldown design:paramtypes omission
* update rolldown
* Revert "fix(backend): extract static error classes to avoid rolldown design:paramtypes omission"
This reverts commit e2243c9dc3.
* fix
* perf: avoid generating sourcemap in production
* fix
* fix
* fix
* fix paths
* fix
* fix
* fix
* fix
* fix
* enhance: バックエンドの開発サーバー制御をrolldown側で行うように
* remove nodemon
* Update Changelog
* tweak config
* fix
* fix
* fix
* clean up
---------
Co-authored-by: あわわわとーにゅ <17376330+u1-liquid@users.noreply.github.com>
Co-authored-by: bab <mashirohira@gmail.com>
This commit is contained in:
@@ -4,8 +4,7 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { resolve } from 'node:path';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { DriveFilesRepository } from '@/models/_.js';
|
||||
@@ -25,11 +24,6 @@ import { FileServerFileResolver } from './file/FileServerFileResolver.js';
|
||||
import { FileServerProxyHandler } from './file/FileServerProxyHandler.js';
|
||||
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
const assets = `${_dirname}/../../server/file/assets/`;
|
||||
|
||||
@Injectable()
|
||||
export class FileServerService {
|
||||
private logger: Logger;
|
||||
@@ -37,6 +31,8 @@ export class FileServerService {
|
||||
private proxyHandler: FileServerProxyHandler;
|
||||
private fileResolver: FileServerFileResolver;
|
||||
|
||||
private readonly assets: string;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
@@ -52,6 +48,7 @@ export class FileServerService {
|
||||
private loggerService: LoggerService,
|
||||
) {
|
||||
this.logger = this.loggerService.getLogger('server', 'gray');
|
||||
this.assets = resolve(this.config.rootDir, 'packages/backend/src/server/file/assets');
|
||||
this.fileResolver = new FileServerFileResolver(
|
||||
this.driveFilesRepository,
|
||||
this.fileInfoService,
|
||||
@@ -61,13 +58,13 @@ export class FileServerService {
|
||||
this.driveHandler = new FileServerDriveHandler(
|
||||
this.config,
|
||||
this.fileResolver,
|
||||
assets,
|
||||
this.assets,
|
||||
this.videoProcessingService,
|
||||
);
|
||||
this.proxyHandler = new FileServerProxyHandler(
|
||||
this.config,
|
||||
this.fileResolver,
|
||||
assets,
|
||||
this.assets,
|
||||
this.imageProcessingService,
|
||||
);
|
||||
|
||||
@@ -87,7 +84,7 @@ export class FileServerService {
|
||||
fastify.register((fastify, options, done) => {
|
||||
fastify.addHook('onRequest', handleRequestRedirectToOmitSearch);
|
||||
fastify.get('/files/app-default.jpg', (request, reply) => {
|
||||
const file = fs.createReadStream(`${_dirname}/assets/dummy.png`);
|
||||
const file = fs.createReadStream(`${this.assets}/dummy.png`);
|
||||
reply.header('Content-Type', 'image/jpeg');
|
||||
reply.header('Cache-Control', 'max-age=31536000, immutable');
|
||||
return reply.send(file);
|
||||
@@ -121,7 +118,7 @@ export class FileServerService {
|
||||
reply.header('Cache-Control', 'max-age=300');
|
||||
|
||||
if (request.query && 'fallback' in request.query) {
|
||||
return reply.sendFile('/dummy.png', assets);
|
||||
return reply.sendFile('/dummy.png', this.assets);
|
||||
}
|
||||
|
||||
if (err instanceof StatusError && (err.statusCode === 302 || err.isClientError)) {
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
*/
|
||||
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import * as fs from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import ms from 'ms';
|
||||
import sharp from 'sharp';
|
||||
@@ -67,35 +65,17 @@ import { ErrorPage } from './views/error.js';
|
||||
|
||||
import type { FastifyError, FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
let rootDir = _dirname;
|
||||
// 見つかるまで上に遡る
|
||||
while (!fs.existsSync(resolve(rootDir, 'packages'))) {
|
||||
const parentDir = dirname(rootDir);
|
||||
if (parentDir === rootDir) {
|
||||
throw new Error('Cannot find root directory');
|
||||
}
|
||||
rootDir = parentDir;
|
||||
}
|
||||
|
||||
const backendRootDir = resolve(rootDir, 'packages/backend');
|
||||
const frontendRootDir = resolve(rootDir, 'packages/frontend');
|
||||
|
||||
const staticAssets = resolve(backendRootDir, 'assets');
|
||||
const clientAssets = resolve(frontendRootDir, 'assets');
|
||||
const assets = resolve(rootDir, 'built/_frontend_dist_');
|
||||
const swAssets = resolve(rootDir, 'built/_sw_dist_');
|
||||
const fluentEmojisDir = resolve(rootDir, 'fluent-emojis/dist');
|
||||
const twemojiDir = resolve(backendRootDir, 'node_modules/@discordapp/twemoji/dist/svg');
|
||||
const frontendViteOut = resolve(rootDir, 'built/_frontend_vite_');
|
||||
const frontendEmbedViteOut = resolve(rootDir, 'built/_frontend_embed_vite_');
|
||||
const tarball = resolve(rootDir, 'built/tarball');
|
||||
|
||||
@Injectable()
|
||||
export class ClientServerService {
|
||||
private logger: Logger;
|
||||
private readonly staticAssets: string;
|
||||
private readonly clientAssets: string;
|
||||
private readonly assets: string;
|
||||
private readonly swAssets: string;
|
||||
private readonly fluentEmojisDir: string;
|
||||
private readonly twemojiDir: string;
|
||||
private readonly frontendViteOut: string;
|
||||
private readonly frontendEmbedViteOut: string;
|
||||
private readonly tarball: string;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
@@ -149,6 +129,17 @@ export class ClientServerService {
|
||||
private clientLoggerService: ClientLoggerService,
|
||||
) {
|
||||
//this.createServer = this.createServer.bind(this);
|
||||
const backendRootdir = resolve(this.config.rootDir, 'packages/backend');
|
||||
const frontendRootdir = resolve(this.config.rootDir, 'packages/frontend');
|
||||
this.staticAssets = resolve(backendRootdir, 'assets');
|
||||
this.clientAssets = resolve(frontendRootdir, 'assets');
|
||||
this.assets = resolve(this.config.rootDir, 'built/_frontend_dist_');
|
||||
this.swAssets = resolve(this.config.rootDir, 'built/_sw_dist_');
|
||||
this.fluentEmojisDir = resolve(this.config.rootDir, 'fluent-emojis/dist');
|
||||
this.twemojiDir = resolve(backendRootdir, 'node_modules/@discordapp/twemoji/dist/svg');
|
||||
this.frontendViteOut = resolve(this.config.rootDir, 'built/_frontend_vite_');
|
||||
this.frontendEmbedViteOut = resolve(this.config.rootDir, 'built/_frontend_embed_vite_');
|
||||
this.tarball = resolve(this.config.rootDir, 'built/tarball');
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -223,17 +214,17 @@ export class ClientServerService {
|
||||
|
||||
//#region vite assets
|
||||
if (this.config.frontendEmbedManifestExists) {
|
||||
console.log(`[ClientServerService] Using built frontend vite assets. ${frontendViteOut}`);
|
||||
console.log(`[ClientServerService] Using built frontend vite assets. ${this.frontendViteOut}`);
|
||||
fastify.register((fastify, options, done) => {
|
||||
fastify.register(fastifyStatic, {
|
||||
root: frontendViteOut,
|
||||
root: this.frontendViteOut,
|
||||
prefix: '/vite/',
|
||||
maxAge: ms('30 days'),
|
||||
immutable: true,
|
||||
decorateReply: false,
|
||||
});
|
||||
fastify.register(fastifyStatic, {
|
||||
root: frontendEmbedViteOut,
|
||||
root: this.frontendEmbedViteOut,
|
||||
prefix: '/embed_vite/',
|
||||
maxAge: ms('30 days'),
|
||||
immutable: true,
|
||||
@@ -265,21 +256,21 @@ export class ClientServerService {
|
||||
//#region static assets
|
||||
|
||||
fastify.register(fastifyStatic, {
|
||||
root: staticAssets,
|
||||
root: this.staticAssets,
|
||||
prefix: '/static-assets/',
|
||||
maxAge: ms('7 days'),
|
||||
decorateReply: false,
|
||||
});
|
||||
|
||||
fastify.register(fastifyStatic, {
|
||||
root: clientAssets,
|
||||
root: this.clientAssets,
|
||||
prefix: '/client-assets/',
|
||||
maxAge: ms('7 days'),
|
||||
decorateReply: false,
|
||||
});
|
||||
|
||||
fastify.register(fastifyStatic, {
|
||||
root: assets,
|
||||
root: this.assets,
|
||||
prefix: '/assets/',
|
||||
maxAge: ms('7 days'),
|
||||
decorateReply: false,
|
||||
@@ -287,7 +278,7 @@ export class ClientServerService {
|
||||
|
||||
fastify.register((fastify, options, done) => {
|
||||
fastify.register(fastifyStatic, {
|
||||
root: tarball,
|
||||
root: this.tarball,
|
||||
prefix: '/tarball/',
|
||||
maxAge: ms('30 days'),
|
||||
immutable: true,
|
||||
@@ -298,11 +289,11 @@ export class ClientServerService {
|
||||
});
|
||||
|
||||
fastify.get('/favicon.ico', async (request, reply) => {
|
||||
return reply.sendFile('/favicon.ico', staticAssets);
|
||||
return reply.sendFile('/favicon.ico', this.staticAssets);
|
||||
});
|
||||
|
||||
fastify.get('/apple-touch-icon.png', async (request, reply) => {
|
||||
return reply.sendFile('/apple-touch-icon.png', staticAssets);
|
||||
return reply.sendFile('/apple-touch-icon.png', this.staticAssets);
|
||||
});
|
||||
|
||||
fastify.get<{ Params: { path: string } }>('/fluent-emoji/:path(.*)', async (request, reply) => {
|
||||
@@ -315,7 +306,7 @@ export class ClientServerService {
|
||||
|
||||
reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\'');
|
||||
|
||||
return reply.sendFile(path, fluentEmojisDir, {
|
||||
return reply.sendFile(path, this.fluentEmojisDir, {
|
||||
maxAge: ms('30 days'),
|
||||
});
|
||||
});
|
||||
@@ -330,7 +321,7 @@ export class ClientServerService {
|
||||
|
||||
reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\'');
|
||||
|
||||
return reply.sendFile(path, twemojiDir, {
|
||||
return reply.sendFile(path, this.twemojiDir, {
|
||||
maxAge: ms('30 days'),
|
||||
});
|
||||
});
|
||||
@@ -344,7 +335,7 @@ export class ClientServerService {
|
||||
}
|
||||
|
||||
const mask = await sharp(
|
||||
`${twemojiDir}/${path.replace('.png', '')}.svg`,
|
||||
`${this.twemojiDir}/${path.replace('.png', '')}.svg`,
|
||||
{ density: 1000 },
|
||||
)
|
||||
.resize(488, 488)
|
||||
@@ -380,7 +371,7 @@ export class ClientServerService {
|
||||
|
||||
// ServiceWorker
|
||||
fastify.get('/sw.js', async (request, reply) => {
|
||||
return await reply.sendFile('/sw.js', swAssets, {
|
||||
return await reply.sendFile('/sw.js', this.swAssets, {
|
||||
maxAge: ms('10 minutes'),
|
||||
});
|
||||
});
|
||||
@@ -390,7 +381,7 @@ export class ClientServerService {
|
||||
|
||||
// Embed Javascript
|
||||
fastify.get('/embed.js', async (request, reply) => {
|
||||
return await reply.sendFile('/embed.js', staticAssets, {
|
||||
return await reply.sendFile('/embed.js', this.staticAssets, {
|
||||
maxAge: ms('1 day'),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { promises as fsp, existsSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { promises as fsp } from 'node:fs';
|
||||
import { languages } from 'i18n/const';
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
@@ -18,25 +17,11 @@ import type { Config } from '@/config.js';
|
||||
import type { MiMeta } from '@/models/Meta.js';
|
||||
import type { CommonData, ViteFiles } from './views/_.js';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
let rootDir = _dirname;
|
||||
// 見つかるまで上に遡る
|
||||
while (!existsSync(resolve(rootDir, 'packages'))) {
|
||||
const parentDir = dirname(rootDir);
|
||||
if (parentDir === rootDir) {
|
||||
throw new Error('Cannot find root directory');
|
||||
}
|
||||
rootDir = parentDir;
|
||||
}
|
||||
|
||||
const frontendViteBuilt = resolve(rootDir, 'built/_frontend_vite_');
|
||||
const frontendEmbedViteBuilt = resolve(rootDir, 'built/_frontend_embed_vite_');
|
||||
|
||||
@Injectable()
|
||||
export class HtmlTemplateService {
|
||||
private frontendAssetsFetched = false;
|
||||
private readonly frontendViteBuilt: string;
|
||||
private readonly frontendEmbedViteBuilt: string;
|
||||
public frontendViteFiles: ViteFiles | null = null;
|
||||
public frontendBootloaderJs: string | null = null;
|
||||
public frontendBootloaderCss: string | null = null;
|
||||
@@ -53,6 +38,8 @@ export class HtmlTemplateService {
|
||||
|
||||
private metaEntityService: MetaEntityService,
|
||||
) {
|
||||
this.frontendViteBuilt = resolve(this.config.rootDir, 'built/_frontend_vite_');
|
||||
this.frontendEmbedViteBuilt = resolve(this.config.rootDir, 'built/_frontend_embed_vite_');
|
||||
}
|
||||
|
||||
// 初期ロードで読み込むべきファイルのパスを収集する。
|
||||
@@ -118,22 +105,22 @@ export class HtmlTemplateService {
|
||||
embedBootJs,
|
||||
embedBootCss,
|
||||
] = await Promise.all([
|
||||
fsp.readFile(resolve(frontendViteBuilt, 'loader/boot.js'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(frontendViteBuilt, 'loader/style.css'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(frontendEmbedViteBuilt, 'loader/boot.js'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(frontendEmbedViteBuilt, 'loader/style.css'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(this.frontendViteBuilt, 'loader/boot.js'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(this.frontendViteBuilt, 'loader/style.css'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(this.frontendEmbedViteBuilt, 'loader/boot.js'), 'utf-8').catch(() => null),
|
||||
fsp.readFile(resolve(this.frontendEmbedViteBuilt, 'loader/style.css'), 'utf-8').catch(() => null),
|
||||
]);
|
||||
|
||||
let feViteManifest: Manifest | null = null;
|
||||
let embedFeViteManifest: Manifest | null = null;
|
||||
|
||||
if (this.config.frontendManifestExists) {
|
||||
const manifestContent = await fsp.readFile(resolve(frontendViteBuilt, 'manifest.json'), 'utf-8').catch(() => null);
|
||||
const manifestContent = await fsp.readFile(resolve(this.frontendViteBuilt, 'manifest.json'), 'utf-8').catch(() => null);
|
||||
feViteManifest = manifestContent ? JSON.parse(manifestContent) : null;
|
||||
}
|
||||
|
||||
if (this.config.frontendEmbedManifestExists) {
|
||||
const manifestContent = await fsp.readFile(resolve(frontendEmbedViteBuilt, 'manifest.json'), 'utf-8').catch(() => null);
|
||||
const manifestContent = await fsp.readFile(resolve(this.frontendEmbedViteBuilt, 'manifest.json'), 'utf-8').catch(() => null);
|
||||
embedFeViteManifest = manifestContent ? JSON.parse(manifestContent) : null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user