diff --git a/.github/workflows/deploy_test_web.yaml b/.github/workflows/deploy_test_web.yaml
index 49a96458f9b14..f7d3339d77f41 100644
--- a/.github/workflows/deploy_test_web.yaml
+++ b/.github/workflows/deploy_test_web.yaml
@@ -62,7 +62,7 @@ jobs:
with:
SSH_PRIVATE_KEY: ${{ env.SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i"
- SOURCE: "frontend/appflowy_web_app/dist frontend/appflowy_web_app/Dockerfile frontend/appflowy_web_app/nginx.conf frontend/appflowy_web_app/.env nginx-signed.crt nginx-signed.key"
+ SOURCE: "frontend/appflowy_web_app/dist frontend/appflowy_web_app/server.cjs frontend/appflowy_web_app/start.sh frontend/appflowy_web_app/Dockerfile frontend/appflowy_web_app/nginx.conf frontend/appflowy_web_app/.env nginx-signed.crt nginx-signed.key"
REMOTE_HOST: ${{ env.REMOTE_HOST }}
REMOTE_USER: ${{ env.REMOTE_USER }}
EXCLUDE: "frontend/appflowy_web_app/dist/, frontend/appflowy_web_app/node_modules/"
diff --git a/frontend/appflowy_web_app/Dockerfile b/frontend/appflowy_web_app/Dockerfile
index e7094ffe145e6..97e3a85559177 100644
--- a/frontend/appflowy_web_app/Dockerfile
+++ b/frontend/appflowy_web_app/Dockerfile
@@ -1,8 +1,14 @@
-FROM node:latest
+FROM oven/bun:latest
+
+WORKDIR /app
RUN apt-get update && \
apt-get install -y nginx
+RUN bun install cheerio pino axios pino-pretty
+
+COPY . .
+
RUN addgroup --system nginx && \
adduser --system --no-create-home --disabled-login --ingroup nginx nginx
@@ -18,6 +24,11 @@ COPY nginx-signed.key /etc/ssl/private/nginx-signed.key
RUN chown -R nginx:nginx /etc/ssl/certs/nginx-signed.crt /etc/ssl/private/nginx-signed.key
+COPY start.sh /app/start.sh
+
+RUN chmod +x /app/start.sh
+
+
EXPOSE 80 443
-CMD ["nginx", "-g", "daemon off;"]
+CMD ["/app/start.sh"]
diff --git a/frontend/appflowy_web_app/index.html b/frontend/appflowy_web_app/index.html
index 3d8bc89cd60b0..a71f082fc36b9 100644
--- a/frontend/appflowy_web_app/index.html
+++ b/frontend/appflowy_web_app/index.html
@@ -4,8 +4,28 @@
-
AppFlowy
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/appflowy_web_app/nginx.conf b/frontend/appflowy_web_app/nginx.conf
index 56529cca1aecf..729255a7786ed 100644
--- a/frontend/appflowy_web_app/nginx.conf
+++ b/frontend/appflowy_web_app/nginx.conf
@@ -30,6 +30,10 @@ http {
gzip_http_version 1.0;
+ gzip_comp_level 5;
+
+ gzip_vary on;
+
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript application/wasm;
# Existing server block for HTTP
@@ -61,15 +65,22 @@ http {
ssl_prefer_server_ciphers on;
location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- try_files $uri $uri/ /index.html;
+ proxy_pass http://localhost:3000;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection 'upgrade';
+ proxy_set_header Host $host;
+ proxy_cache_bypass $http_upgrade;
}
location /static/ {
root /usr/share/nginx/html;
expires 30d;
access_log off;
+ location ~* \.wasm$ {
+ types { application/wasm wasm; }
+ default_type application/wasm;
+ }
}
location /appflowy.svg {
diff --git a/frontend/appflowy_web_app/pnpm-lock.yaml b/frontend/appflowy_web_app/pnpm-lock.yaml
index b3e7c8496aaa4..37a34b9470ce9 100644
--- a/frontend/appflowy_web_app/pnpm-lock.yaml
+++ b/frontend/appflowy_web_app/pnpm-lock.yaml
@@ -8862,7 +8862,7 @@ packages:
/randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
dependencies:
- safe-buffer: 5.1.2
+ safe-buffer: 5.2.1
dev: true
/react-beautiful-dnd@13.1.1(react-dom@18.2.0)(react@18.2.0):
@@ -9604,6 +9604,10 @@ packages:
/safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+ /safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+ dev: true
+
/safe-regex-test@1.0.3:
resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
engines: {node: '>= 0.4'}
diff --git a/frontend/appflowy_web_app/server.cjs b/frontend/appflowy_web_app/server.cjs
new file mode 100644
index 0000000000000..e13c337faa394
--- /dev/null
+++ b/frontend/appflowy_web_app/server.cjs
@@ -0,0 +1,114 @@
+const path = require('path');
+const fs = require('fs');
+const pino = require('pino');
+const cheerio = require('cheerio');
+const axios = require('axios');
+
+const distDir = path.join(__dirname, 'dist');
+const indexPath = path.join(distDir, 'index.html');
+
+const setOrUpdateMetaTag = ($, selector, attribute, content) => {
+ if ($(selector).length === 0) {
+ $('head').append(``);
+ } else {
+ $(selector).attr('content', content);
+ }
+};
+// Create a new logger instance
+const logger = pino({
+ transport: {
+ target: 'pino-pretty',
+ level: 'info',
+ options: {
+ colorize: true,
+ translateTime: 'SYS:standard',
+ destination: `${__dirname}/pino-logger.log`,
+ },
+ },
+});
+
+const logRequestTimer = (req) => {
+ const start = Date.now();
+ const pathname = new URL(req.url).pathname;
+ logger.info(`Incoming request: ${pathname}`);
+ return () => {
+ const duration = Date.now() - start;
+ logger.info(`Request for ${pathname} took ${duration}ms`);
+ };
+};
+
+const fetchMetaData = async (url) => {
+ try {
+ const response = await axios.get(url);
+ return response.data;
+ } catch (error) {
+ logger.error('Error fetching meta data', error);
+ return null;
+ }
+};
+
+const createServer = async (req) => {
+ const timer = logRequestTimer(req);
+
+ if (req.method === 'GET') {
+ const pageId = req.url.split('/').pop();
+ let htmlData = fs.readFileSync(indexPath, 'utf8');
+ const $ = cheerio.load(htmlData);
+ if (!pageId) {
+ timer();
+ return new Response($.html(), {
+ headers: { 'Content-Type': 'text/html' },
+ });
+ }
+
+ const description = 'Write, share, comment, react, and publish docs quickly and securely on AppFlowy.';
+ let title = 'AppFlowy';
+ const url = 'https://appflowy.com';
+ let image = 'https://d3uafhn8yrvdfn.cloudfront.net/website/production/_next/static/media/og-image.e347bfb5.png';
+ // Inject meta data into the HTML to support SEO and social sharing
+ // if (metaData) {
+ // title = metaData.title;
+ // image = metaData.image;
+ // }
+
+ $('title').text(title);
+ setOrUpdateMetaTag($, 'meta[name="description"]', 'name', description);
+ setOrUpdateMetaTag($, 'meta[property="og:title"]', 'property', title);
+ setOrUpdateMetaTag($, 'meta[property="og:description"]', 'property', description);
+ setOrUpdateMetaTag($, 'meta[property="og:image"]', 'property', image);
+ setOrUpdateMetaTag($, 'meta[property="og:url"]', 'property', url);
+ setOrUpdateMetaTag($, 'meta[property="og:type"]', 'property', 'article');
+ setOrUpdateMetaTag($, 'meta[name="twitter:card"]', 'name', 'summary_large_image');
+ setOrUpdateMetaTag($, 'meta[name="twitter:title"]', 'name', title);
+ setOrUpdateMetaTag($, 'meta[name="twitter:description"]', 'name', description);
+ setOrUpdateMetaTag($, 'meta[name="twitter:image"]', 'name', image);
+
+ timer();
+ return new Response($.html(), {
+ headers: { 'Content-Type': 'text/html' },
+ });
+ } else {
+ timer();
+ logger.error({ message: 'Method not allowed', method: req.method });
+ return new Response('Method not allowed', { status: 405 });
+ }
+};
+
+const start = () => {
+ try {
+ Bun.serve({
+ port: 3000,
+ fetch: createServer,
+ error: (err) => {
+ logger.error(`Internal Server Error: ${err}`);
+ return new Response('Internal Server Error', { status: 500 });
+ },
+ });
+ logger.info(`Server is running on port 3000`);
+ } catch (err) {
+ logger.error(err);
+ process.exit(1);
+ }
+};
+
+start();
diff --git a/frontend/appflowy_web_app/src/components/editor/CollaborativeEditor.tsx b/frontend/appflowy_web_app/src/components/editor/CollaborativeEditor.tsx
index 8820296780a10..02a27f17ed0d0 100644
--- a/frontend/appflowy_web_app/src/components/editor/CollaborativeEditor.tsx
+++ b/frontend/appflowy_web_app/src/components/editor/CollaborativeEditor.tsx
@@ -30,6 +30,7 @@ function CollaborativeEditor({ doc }: { doc: Y.Doc }) {
useEffect(() => {
if (!editor) return;
+
editor.connect();
setIsConnected(true);
diff --git a/frontend/appflowy_web_app/start.sh b/frontend/appflowy_web_app/start.sh
new file mode 100644
index 0000000000000..b4691baa1ae79
--- /dev/null
+++ b/frontend/appflowy_web_app/start.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Start the frontend server
+bun run server.cjs &
+
+# Start the nginx server
+service nginx start
+
+tail -f /dev/null
+