fix(server): renderer path (#8305)
This commit is contained in:
18
.github/helm/affine/templates/ingress.yaml
vendored
18
.github/helm/affine/templates/ingress.yaml
vendored
@@ -60,15 +60,8 @@ spec:
|
|||||||
name: affine-graphql
|
name: affine-graphql
|
||||||
port:
|
port:
|
||||||
number: {{ .Values.graphql.service.port }}
|
number: {{ .Values.graphql.service.port }}
|
||||||
- path: /workspace/([^/]+)/(home|all|search|collection|tag|trash)
|
- path: /workspace
|
||||||
pathType: ImplementationSpecific
|
pathType: Prefix
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: affine-web
|
|
||||||
port:
|
|
||||||
number: {{ .Values.web.service.port }}
|
|
||||||
- path: /workspace/([^/]+)/.+
|
|
||||||
pathType: ImplementationSpecific
|
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: affine-renderer
|
name: affine-renderer
|
||||||
@@ -81,11 +74,4 @@ spec:
|
|||||||
name: affine-web
|
name: affine-web
|
||||||
port:
|
port:
|
||||||
number: {{ .Values.web.service.port }}
|
number: {{ .Values.web.service.port }}
|
||||||
- path: /js/worker.(.+).js
|
|
||||||
pathType: ImplementationSpecific
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: affine-web
|
|
||||||
port:
|
|
||||||
number: {{ .Values.web.service.port }}
|
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
|
|
||||||
import { Controller, Get, Logger, Param, Req, Res } from '@nestjs/common';
|
import { Controller, Get, Logger, Req, Res } from '@nestjs/common';
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
import isMobile from 'is-mobile';
|
import isMobile from 'is-mobile';
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ interface RenderOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface HtmlAssets {
|
interface HtmlAssets {
|
||||||
|
html: string;
|
||||||
css: string[];
|
css: string[];
|
||||||
js: string[];
|
js: string[];
|
||||||
publicPath: string;
|
publicPath: string;
|
||||||
@@ -26,6 +27,7 @@ interface HtmlAssets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const defaultAssets: HtmlAssets = {
|
const defaultAssets: HtmlAssets = {
|
||||||
|
html: '',
|
||||||
css: [],
|
css: [],
|
||||||
js: [],
|
js: [],
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
@@ -33,6 +35,16 @@ const defaultAssets: HtmlAssets = {
|
|||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(@forehalo): reuse routes with frontend
|
||||||
|
const staticPaths = new Set([
|
||||||
|
'all',
|
||||||
|
'home',
|
||||||
|
'search',
|
||||||
|
'collection',
|
||||||
|
'tag',
|
||||||
|
'trash',
|
||||||
|
]);
|
||||||
|
|
||||||
@Controller('/workspace')
|
@Controller('/workspace')
|
||||||
export class DocRendererController {
|
export class DocRendererController {
|
||||||
private readonly logger = new Logger(DocRendererController.name);
|
private readonly logger = new Logger(DocRendererController.name);
|
||||||
@@ -45,36 +57,23 @@ export class DocRendererController {
|
|||||||
private readonly config: Config,
|
private readonly config: Config,
|
||||||
private readonly url: URLHelper
|
private readonly url: URLHelper
|
||||||
) {
|
) {
|
||||||
try {
|
this.webAssets = this.readHtmlAssets(
|
||||||
const webConfigMapsPath = join(
|
join(
|
||||||
this.config.projectRoot,
|
this.config.projectRoot,
|
||||||
this.config.isSelfhosted ? 'static/selfhost' : 'static',
|
this.config.isSelfhosted ? 'static/selfhost' : 'static'
|
||||||
'assets-manifest.json'
|
)
|
||||||
);
|
);
|
||||||
const mobileConfigMapsPath = join(
|
this.mobileAssets = this.readHtmlAssets(
|
||||||
|
join(
|
||||||
this.config.projectRoot,
|
this.config.projectRoot,
|
||||||
this.config.isSelfhosted ? 'static/mobile/selfhost' : 'static/mobile',
|
this.config.isSelfhosted ? 'static/mobile/selfhost' : 'static/mobile'
|
||||||
'assets-manifest.json'
|
)
|
||||||
);
|
);
|
||||||
this.webAssets = JSON.parse(readFileSync(webConfigMapsPath, 'utf-8'));
|
|
||||||
this.mobileAssets = JSON.parse(
|
|
||||||
readFileSync(mobileConfigMapsPath, 'utf-8')
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
if (this.config.node.prod) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Public()
|
@Public()
|
||||||
@Get('/:workspaceId/:docId')
|
@Get('/*')
|
||||||
async render(
|
async render(@Req() req: Request, @Res() res: Response) {
|
||||||
@Req() req: Request,
|
|
||||||
@Res() res: Response,
|
|
||||||
@Param('workspaceId') workspaceId: string,
|
|
||||||
@Param('docId') docId: string
|
|
||||||
) {
|
|
||||||
const assets: HtmlAssets =
|
const assets: HtmlAssets =
|
||||||
this.config.affine.canary &&
|
this.config.affine.canary &&
|
||||||
isMobile({
|
isMobile({
|
||||||
@@ -84,14 +83,20 @@ export class DocRendererController {
|
|||||||
: this.webAssets;
|
: this.webAssets;
|
||||||
|
|
||||||
let opts: RenderOptions | null = null;
|
let opts: RenderOptions | null = null;
|
||||||
try {
|
// /workspace/:workspaceId/{:docId | staticPaths}
|
||||||
opts =
|
const [, , workspaceId, subPath, ...restPaths] = req.path.split('/');
|
||||||
workspaceId === docId
|
|
||||||
? await this.getWorkspaceContent(workspaceId)
|
// /:workspaceId/:docId
|
||||||
: await this.getPageContent(workspaceId, docId);
|
if (workspaceId && !staticPaths.has(subPath) && restPaths.length === 0) {
|
||||||
metrics.doc.counter('render').add(1);
|
try {
|
||||||
} catch (e) {
|
opts =
|
||||||
this.logger.error('failed to render page', e);
|
workspaceId === subPath
|
||||||
|
? await this.getWorkspaceContent(workspaceId)
|
||||||
|
: await this.getPageContent(workspaceId, subPath);
|
||||||
|
metrics.doc.counter('render').add(1);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error('failed to render page', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'text/html');
|
res.setHeader('Content-Type', 'text/html');
|
||||||
@@ -148,6 +153,10 @@ export class DocRendererController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_render(opts: RenderOptions | null, assets: HtmlAssets): string {
|
_render(opts: RenderOptions | null, assets: HtmlAssets): string {
|
||||||
|
if (!opts && assets.html) {
|
||||||
|
return assets.html;
|
||||||
|
}
|
||||||
|
|
||||||
const title = opts?.title
|
const title = opts?.title
|
||||||
? htmlSanitize(`${opts.title} | AFFiNE`)
|
? htmlSanitize(`${opts.title} | AFFiNE`)
|
||||||
: 'AFFiNE';
|
: 'AFFiNE';
|
||||||
@@ -199,4 +208,24 @@ export class DocRendererController {
|
|||||||
</html>
|
</html>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should only be called at startup time
|
||||||
|
*/
|
||||||
|
private readHtmlAssets(path: string): HtmlAssets {
|
||||||
|
const manifestPath = join(path, 'assets-manifest.json');
|
||||||
|
const htmlPath = join(path, 'index.html');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const assets = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
||||||
|
assets.html = readFileSync(htmlPath, 'utf-8');
|
||||||
|
return assets;
|
||||||
|
} catch (e) {
|
||||||
|
if (this.config.node.prod) {
|
||||||
|
throw e;
|
||||||
|
} else {
|
||||||
|
return defaultAssets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user