Skip to content

Commit

Permalink
refactor: build once for all deployments
Browse files Browse the repository at this point in the history
  • Loading branch information
forehalo committed Oct 15, 2024
1 parent 72d52e4 commit fe52900
Show file tree
Hide file tree
Showing 47 changed files with 414 additions and 303 deletions.
98 changes: 0 additions & 98 deletions .github/workflows/build-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,83 +135,6 @@ jobs:
path: ./packages/frontend/apps/mobile/dist
if-no-files-found: error

build-web-selfhost:
name: Build @affine/web selfhost
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Core
run: yarn nx build @affine/web --skip-nx-cache
env:
BUILD_TYPE: ${{ github.event.inputs.flavor }}
PUBLIC_PATH: '/'
SELF_HOSTED: true
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Download selfhost fonts
run: node ./scripts/download-blocksuite-fonts.mjs
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
name: selfhost-web
path: ./packages/frontend/apps/web/dist
if-no-files-found: error

build-mobile-selfhost:
name: Build @affine/mobile selfhost
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Mobile
run: yarn nx build @affine/mobile --skip-nx-cache
env:
BUILD_TYPE: ${{ github.event.inputs.flavor }}
PUBLIC_PATH: '/mobile/'
SELF_HOSTED: true
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload mobile artifact
uses: actions/upload-artifact@v4
with:
name: selfhost-mobile
path: ./packages/frontend/apps/mobile/dist
if-no-files-found: error

build-admin-selfhost:
name: Build @affine/admin selfhost
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build admin
run: yarn nx build @affine/admin --skip-nx-cache
env:
BUILD_TYPE: ${{ github.event.inputs.flavor }}
PUBLIC_PATH: '/admin/'
SELF_HOSTED: true
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload admin artifact
uses: actions/upload-artifact@v4
with:
name: selfhost-admin
path: ./packages/frontend/admin/dist
if-no-files-found: error

build-server-native:
name: Build Server native - ${{ matrix.targets.name }}
runs-on: ubuntu-latest
Expand Down Expand Up @@ -256,9 +179,6 @@ jobs:
- build-web
- build-mobile
- build-admin
- build-web-selfhost
- build-mobile-selfhost
- build-admin-selfhost
- build-server-native
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -334,24 +254,6 @@ jobs:
name: admin
path: ./packages/frontend/admin/dist

- name: Download selfhost web artifact
uses: actions/download-artifact@v4
with:
name: selfhost-web
path: ./packages/frontend/apps/web/dist/selfhost

- name: Download selfhost mobile artifact
uses: actions/download-artifact@v4
with:
name: selfhost-mobile
path: ./packages/frontend/apps/mobile/dist/selfhost

- name: Download selfhost admin artifact
uses: actions/download-artifact@v4
with:
name: selfhost-admin
path: ./packages/frontend/admin/dist/selfhost

- name: Install Node.js dependencies
run: |
yarn config set --json supportedArchitectures.cpu '["x64", "arm64", "arm"]'
Expand Down
4 changes: 4 additions & 0 deletions packages/backend/server/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export async function createApp() {
logger: AFFiNE.affine.stable ? ['log'] : ['verbose'],
});

if (AFFiNE.server.path) {
app.setGlobalPrefix(AFFiNE.server.path);
}

app.use(serverTimingAndCache);

app.use(
Expand Down
33 changes: 25 additions & 8 deletions packages/backend/server/src/core/doc-renderer/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ interface RenderOptions {
}

interface HtmlAssets {
html: string;
css: string[];
js: string[];
publicPath: string;
Expand All @@ -27,7 +26,6 @@ interface HtmlAssets {
}

const defaultAssets: HtmlAssets = {
html: '',
css: [],
js: [],
publicPath: '/',
Expand Down Expand Up @@ -152,9 +150,15 @@ export class DocRendererController {
return null;
}

// @TODO(@forehalo): pre-compile html template to accelerate serializing
_render(opts: RenderOptions | null, assets: HtmlAssets): string {
if (!opts && assets.html) {
return assets.html;
// TODO(@forehalo): how can we enable the type reference to @affine/env
const env: Record<string, any> = {
publicPath: assets.publicPath,
};

if (this.config.isSelfhosted) {
env.isSelfHosted = true;
}

const title = opts?.title
Expand Down Expand Up @@ -182,7 +186,7 @@ export class DocRendererController {
<title>${title}</title>
<meta name="theme-color" content="#fafafa" />
<link rel="preconnect" href="${assets.publicPath}">
${assets.publicPath.startsWith('/') ? '' : `<link rel="preconnect" href="${assets.publicPath}">`}
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" sizes="192x192" href="/favicon-192.png" />
Expand All @@ -199,6 +203,10 @@ export class DocRendererController {
<meta property="og:title" content="${title}" />
<meta property="og:description" content="${summary}" />
<meta property="og:image" content="${image}" />
<meta name="renderer" content="ssr" />
${Object.entries(env)
.map(([key, val]) => `<meta name="env:${key}" content="${val}" />`)
.join('\n')}
${assets.css.map(url => `<link rel="stylesheet" href="${url}" />`).join('\n')}
</head>
<body>
Expand All @@ -214,11 +222,20 @@ export class DocRendererController {
*/
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');
const assets: HtmlAssets = JSON.parse(
readFileSync(manifestPath, 'utf-8')
);

const publicPath = this.config.isSelfhosted
? this.config.server.host + '/'
: assets.publicPath;

assets.publicPath = publicPath;
assets.js = assets.js.map(path => publicPath + path);
assets.css = assets.css.map(path => publicPath + path);

return assets;
} catch (e) {
if (this.config.node.prod) {
Expand Down
87 changes: 67 additions & 20 deletions packages/backend/server/src/core/selfhost/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { HttpAdapterHost } from '@nestjs/core';
import type { Application, Request, Response } from 'express';
import { static as serveStatic } from 'express';
import isMobile from 'is-mobile';

import { Config } from '../../fundamentals';
import { AuthModule } from '../auth';
Expand Down Expand Up @@ -58,60 +59,106 @@ export class SelfhostModule implements OnModuleInit {
) {}

onModuleInit() {
const staticPath = join(this.config.projectRoot, 'static');
// in command line mode
if (!this.adapterHost.httpAdapter) {
return;
}

const app = this.adapterHost.httpAdapter.getInstance<Application>();
// for example, '/affine' in host [//host.com/affine]
const basePath = this.config.server.path;
const staticPath = join(this.config.projectRoot, 'static');

// web => {
// affine: 'static/index.html',
// selfhost: 'static/selfhost.html'
// }
// admin => {
// affine: 'static/admin/index.html',
// selfhost: 'static/admin/selfhost.html'
// }
// mobile => {
// affine: 'static/mobile/index.html',
// selfhost: 'static/mobile/selfhost.html'
// }
// NOTE(@forehalo):
// the order following routes should be respected,
// otherwise the app won't work properly.

// START REGION: /admin
// do not allow '/index.html' url, redirect to '/'
app.get(basePath + '/admin/index.html', (_req, res) => {
res.redirect(basePath + '/admin');
return res.redirect(basePath + '/admin');
});

// selfhost static file location
// web => 'static/selfhost'
// admin => 'static/admin/selfhost'
// mobile => 'static/mobile/selfhost'
// serve all static files
app.use(
basePath + '/admin',
serveStatic(join(staticPath, 'admin', 'selfhost'), {
redirect: false,
index: false,
})
);
app.use(
basePath + '/mobile',
serveStatic(join(staticPath, 'mobile', 'selfhost'), {
basePath,
serveStatic(join(staticPath, 'admin'), {
redirect: false,
index: false,
fallthrough: true,
})
);

// fallback all unknown routes
app.get(
[basePath + '/admin', basePath + '/admin/*'],
this.check.use,
(_req, res) => {
res.sendFile(join(staticPath, 'admin', 'selfhost', 'index.html'));
res.sendFile(
join(
staticPath,
'admin',
this.config.isSelfhosted ? 'selfhost.html' : 'index.html'
)
);
}
);
// END REGION

// START REGION: /mobile
// serve all static files
app.use(
basePath,
serveStatic(join(staticPath, 'mobile'), {
redirect: false,
index: false,
fallthrough: true,
})
);
// END REGION

// START REGION: /
// do not allow '/index.html' url, redirect to '/'
app.get(basePath + '/index.html', (_req, res) => {
res.redirect(basePath);
return res.redirect(basePath);
});

// serve all static files
app.use(
basePath,
serveStatic(join(staticPath, 'selfhost'), {
serveStatic(staticPath, {
redirect: false,
index: false,
fallthrough: true,
})
);

app.get('*', this.check.use, (_req, res) => {
res.sendFile(join(staticPath, 'selfhost', 'index.html'));
// fallback all unknown routes
app.get([basePath, basePath + '/*'], this.check.use, (req, res) => {
const mobile = isMobile({
ua: req.headers['user-agent'] ?? undefined,
});

return res.sendFile(
join(
staticPath,
mobile ? 'mobile' : '',
this.config.isSelfhosted ? 'selfhost.html' : 'index.html'
)
);
});
// END REGION
}
}
2 changes: 1 addition & 1 deletion packages/backend/server/src/fundamentals/nestjs/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineStartupConfig, ModuleConfig } from '../../fundamentals/config';
export interface ServerStartupConfigurations {
/**
* Base url of AFFiNE server, used for generating external urls.
* default to be `[AFFiNE.protocol]://[AFFiNE.host][:AFFiNE.port]?[AFFiNE.path]` if not specified
* default to be `[AFFiNE.protocol]://[AFFiNE.host][:AFFiNE.port]/[AFFiNE.path]` if not specified
*/
externalUrl: string;
/**
Expand Down
Loading

0 comments on commit fe52900

Please sign in to comment.