mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
23c67bcdba
* feat: support a event for getting encoded collab of document * feat: support publish view and unpublish views * feat: publish page to the web * chore: refacotor share bloc * feat: call the publish event * feat: support publish view and unpublish views * feat: integrate publish api * feat: integrate unpublish api * feat: fetch the publish info to show the publish status * feat: support publish interfaces * fix: lint error * fix: modified web server * fix: some style * fix: some style * fix: some style * fix: some style * fix: some style * fix: some style * fix: some style * fix: some style * fix: some style * fix: update codes * fix: update codes * fix: update codes * fix: update codes * fix: update codes * chore: refactor publish bloc * fix: some style * fix: some style * fix: some style * fix: some style * fix: some style * fix: some style * fix: the name is too long to publish * chore: change color * fix: some style * fix: some style * feat: refacotor share menu UI * fix: some style * fix: lint * fix: some style * feat: refacotor export-as * fix: some style * chore: refactor share menu colors * fix: rust ci * fix: some style * fix: some style * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: bugs * fix: rerelease * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: deploy * fix: og image * fix: support copy button * fix: support copy button * fix: support copy button * chore: add a params * feat: use default publish name * chore: update copy * feat: show a confirm deletion dialog if the deleted page contains published page * feat: add copy toast in publish tab * fix: to 404 fix: to 404 fix: to 404 fix: the error to 404 * feat: unpublish the page auto when moving it to another space * feat: improve confirm deletion dialog * feat: show unpublish error * chore: use beta.appflowy.com * feat: disable publish in non-apppflowy-cloud user mode * fix: modified bullted icon style * fix: the dark mode color * fix: save the dark mode in local storage * fix: text color * chore: make bash script more portable (#5679) * fix: title longer * chore: move the files and modified the en * chore: update deploy.sh * chore: modified Dockerfile * chore: modified server.cjs to server.js * chore: modifed server.js to server.ts * chore: replace publish url * chore: remove todo list hover * chore: show confirm dialog before deleting page * fix: unpublish the pages before deleting * fix: table cell bg color * fix: callout icon * fix: list number * fix: emoji * fix: number icon * fix: callout icon position * fix: add margin bottom * fix: code block * fix: support scroll for breadcrumbs * fix: the breadcrumb doesn't update after moving page * fix: 0705 issues * fix: update publish status afer deleting page * chore: add hover effect for visit site button * fix: remove puiblish url text field enable border color * chore: update delete page copy * chore: enable debug category * fix: only render sidebar if the spaces are ready * fix: the breadcrumb doesn't update after moving page * fix: auto code * fix: add emoji * fix: add emoji * fix: favicon * fix: cypress test * fix: remove deploy ci * fix: default url * chore: revert launch.json * fix: docker ci * fix: change favicon * fix: flutter integration test * feat: add hover effect to share menu * chore: add a checkmark if the page has been published * chore: revert space deletion --------- Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io> Co-authored-by: Zack <speed2exe@live.com.sg>
194 lines
5.7 KiB
TypeScript
194 lines
5.7 KiB
TypeScript
import path from 'path';
|
|
import * as fs from 'fs';
|
|
import pino from 'pino';
|
|
import { type CheerioAPI, load } from 'cheerio';
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-expect-error
|
|
import { fetch } from 'bun';
|
|
|
|
const distDir = path.join(__dirname, 'dist');
|
|
const indexPath = path.join(distDir, 'index.html');
|
|
const baseURL = process.env.AF_BASE_URL as string;
|
|
const defaultSite = 'https://appflowy.io';
|
|
|
|
const setOrUpdateMetaTag = ($: CheerioAPI, selector: string, attribute: string, content: string) => {
|
|
if ($(selector).length === 0) {
|
|
$('head').append(`<meta ${attribute}="${selector.match(/\[(.*?)\]/)?.[1]}" content="${content}">`);
|
|
} else {
|
|
$(selector).attr('content', content);
|
|
}
|
|
};
|
|
|
|
const logger = pino({
|
|
transport: {
|
|
target: 'pino-pretty',
|
|
options: {
|
|
colorize: true,
|
|
translateTime: 'SYS:standard',
|
|
destination: `${__dirname}/pino-logger.log`,
|
|
},
|
|
},
|
|
level: 'info',
|
|
});
|
|
|
|
const logRequestTimer = (req: Request) => {
|
|
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: string) => {
|
|
logger.info(`Fetching meta data from ${url}`);
|
|
try {
|
|
const response = await fetch(url, {
|
|
verbose: true,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
} catch (error) {
|
|
logger.error(`Error fetching meta data ${error}`);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const createServer = async (req: Request) => {
|
|
const timer = logRequestTimer(req);
|
|
const reqUrl = new URL(req.url);
|
|
const hostname = req.headers.get('host');
|
|
|
|
logger.info(`Request URL: ${hostname}${reqUrl.pathname}`);
|
|
|
|
const [namespace, publishName] = reqUrl.pathname.slice(1).split('/');
|
|
|
|
logger.info(`Namespace: ${namespace}, Publish Name: ${publishName}`);
|
|
|
|
if (req.method === 'GET') {
|
|
if (namespace === '' || !publishName) {
|
|
timer();
|
|
return new Response(null, {
|
|
status: 302,
|
|
headers: {
|
|
Location: defaultSite,
|
|
},
|
|
});
|
|
}
|
|
|
|
let metaData;
|
|
|
|
try {
|
|
metaData = await fetchMetaData(`${baseURL}/api/workspace/published/${namespace}/${publishName}`);
|
|
} catch (error) {
|
|
logger.error(`Error fetching meta data: ${error}`);
|
|
}
|
|
|
|
const htmlData = fs.readFileSync(indexPath, 'utf8');
|
|
const $ = load(htmlData);
|
|
|
|
const description = 'Write, share, and publish docs quickly on AppFlowy.\nGet started for free.';
|
|
let title = 'AppFlowy';
|
|
const url = `https://${hostname}${reqUrl.pathname}`;
|
|
let image = '/og-image.png';
|
|
let favicon = '/appflowy.svg';
|
|
|
|
try {
|
|
if (metaData && metaData.view) {
|
|
const view = metaData.view;
|
|
const emoji = view.icon.value;
|
|
const titleList = [];
|
|
|
|
if (emoji) {
|
|
const emojiCode = emoji.codePointAt(0).toString(16); // Convert emoji to hex code
|
|
const baseUrl = 'https://raw.githubusercontent.com/googlefonts/noto-emoji/main/svg/emoji_u';
|
|
|
|
favicon = `${baseUrl}${emojiCode}.svg`;
|
|
}
|
|
|
|
if (view.name) {
|
|
titleList.push(view.name);
|
|
titleList.push('|');
|
|
}
|
|
|
|
titleList.push('AppFlowy');
|
|
title = titleList.join(' ');
|
|
|
|
try {
|
|
const cover = view.extra ? JSON.parse(view.extra)?.cover : null;
|
|
|
|
if (cover) {
|
|
if (['unsplash', 'custom'].includes(cover.type)) {
|
|
image = cover.value;
|
|
} else if (cover.type === 'built_in') {
|
|
image = `/covers/m_cover_image_${cover.value}.png`;
|
|
}
|
|
}
|
|
} catch (_) {
|
|
// Do nothing
|
|
}
|
|
}
|
|
} catch (error) {
|
|
logger.error(`Error injecting meta data: ${error}`);
|
|
}
|
|
|
|
$('title').text(title);
|
|
$('link[rel="icon"]').attr('href', favicon);
|
|
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:site_name"]', 'property', 'AppFlowy');
|
|
setOrUpdateMetaTag($, 'meta[property="og:type"]', 'property', 'website');
|
|
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);
|
|
setOrUpdateMetaTag($, 'meta[name="twitter:site"]', 'name', '@appflowy');
|
|
|
|
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 });
|
|
}
|
|
};
|
|
|
|
declare const Bun: {
|
|
serve: (options: { port: number; fetch: typeof createServer; error: (err: Error) => Response }) => void;
|
|
};
|
|
|
|
const start = () => {
|
|
try {
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
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');
|
|
logger.info(`Base URL: ${baseURL}`);
|
|
} catch (err) {
|
|
logger.error(err);
|
|
process.exit(1);
|
|
}
|
|
};
|
|
|
|
start();
|
|
|
|
export {};
|