mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-24 04:00:59 +01:00
monograph: use bun in prod
This commit is contained in:
1
apps/monograph/.gitignore
vendored
1
apps/monograph/.gitignore
vendored
@@ -6,3 +6,4 @@ node_modules
|
||||
.dev.vars
|
||||
|
||||
.wrangler
|
||||
/output
|
||||
@@ -1,20 +1,14 @@
|
||||
|
||||
FROM --platform=$BUILDPLATFORM node:20-alpine
|
||||
FROM --platform=$BUILDPLATFORM oven/bun:1.1.36-alpine
|
||||
|
||||
RUN mkdir -p /home/node/app && chown -R node:node /home/node/app
|
||||
RUN mkdir -p /home/bun/app && chown -R bun:bun /home/bun/app
|
||||
|
||||
WORKDIR /home/node/app
|
||||
WORKDIR /home/bun/app
|
||||
|
||||
USER node
|
||||
USER bun
|
||||
|
||||
COPY --chown=node:node build ./build
|
||||
COPY --chown=bun:bun output .
|
||||
|
||||
RUN mv build/package.json .
|
||||
RUN bun install
|
||||
|
||||
RUN npm install
|
||||
|
||||
RUN npm install --include=optional sharp
|
||||
|
||||
RUN ls
|
||||
|
||||
CMD [ "npm", "run", "start" ]
|
||||
CMD [ "bun", "run", "start" ]
|
||||
@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import type { MetaFunction, LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { Cipher } from "@notesnook/crypto";
|
||||
import { convert } from "html-to-text";
|
||||
import { Flex, Text } from "@theme-ui/components";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { MonographPage } from "../components/monographpost";
|
||||
@@ -89,7 +88,7 @@ export async function loader({ params }: LoaderFunctionArgs) {
|
||||
metadata
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// console.error(e);
|
||||
return {
|
||||
monograph: null,
|
||||
metadata: {
|
||||
@@ -135,15 +134,6 @@ export default function MonographPost() {
|
||||
);
|
||||
}
|
||||
|
||||
const extractParagraph = (html: string) => {
|
||||
if (!html) return "";
|
||||
return convert(html, {
|
||||
wordwrap: false,
|
||||
preserveNewlines: false,
|
||||
decodeEntities: true
|
||||
});
|
||||
};
|
||||
|
||||
type Metadata = {
|
||||
title: string;
|
||||
fullDescription: string;
|
||||
@@ -151,6 +141,21 @@ type Metadata = {
|
||||
datePublished: string;
|
||||
};
|
||||
|
||||
function extractFirstWords(html: string, numWords = 30): string {
|
||||
// Strip HTML tags and normalize whitespace
|
||||
const plainText = html
|
||||
.replace(/<[^>]*>/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
|
||||
// Split into words and take first N
|
||||
const words = plainText.split(" ").slice(0, numWords);
|
||||
|
||||
// Add ellipsis if text was truncated
|
||||
const excerpt = words.join(" ");
|
||||
return words.length < plainText.split(" ").length ? excerpt + "..." : excerpt;
|
||||
}
|
||||
|
||||
function trimDescription(
|
||||
str: string,
|
||||
length: number,
|
||||
@@ -175,12 +180,12 @@ function addPeriod(str: string) {
|
||||
return str + "...";
|
||||
}
|
||||
|
||||
function getMonographMetadata(monograph?: Monograph): Metadata {
|
||||
function getMonographMetadata(monograph: Monograph): Metadata {
|
||||
const title = monograph?.title || "Not found";
|
||||
const text = monograph?.encryptedContent
|
||||
? "This monograph is encrypted. Enter password to view contents."
|
||||
: monograph?.content
|
||||
? extractParagraph(monograph?.content.data)
|
||||
? extractFirstWords(monograph?.content.data, 100)
|
||||
: "";
|
||||
const shortDescription = trimDescription(text, 150, true);
|
||||
const fullDescription = trimDescription(text, 300, true);
|
||||
|
||||
@@ -74,7 +74,7 @@ export async function isSpam(monograph: Monograph) {
|
||||
}
|
||||
return isSpam;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// console.error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export async function read<T>(key: string, fallback: T): Promise<T> {
|
||||
try {
|
||||
return (JSON.parse(await readFile(key, "utf-8")) as T) || fallback;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// console.error(e);
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ const cache: Record<
|
||||
export async function read<T>(key: string, fallback: T) {
|
||||
const cached = cache[key];
|
||||
if (cached && cached.ttl > Date.now() - cached.cachedAt) {
|
||||
return cached.value;
|
||||
return cached.value as T;
|
||||
}
|
||||
const value = (await provider).read<T>(key, fallback);
|
||||
cache[key] = {
|
||||
ttl: 5 * 60 * 1000,
|
||||
ttl: 60 * 60 * 1000,
|
||||
value,
|
||||
cachedAt: Date.now()
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ export async function read<T>(key: string, fallback: T): Promise<T> {
|
||||
key
|
||||
});
|
||||
if (typeof response === "object" && !response.success) {
|
||||
console.error("failed:", response.errors);
|
||||
// console.error("failed:", response.errors);
|
||||
return fallback;
|
||||
}
|
||||
return (
|
||||
@@ -48,7 +48,7 @@ export async function read<T>(key: string, fallback: T): Promise<T> {
|
||||
fallback
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// console.error(e);
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
1122
apps/monograph/package-lock.json
generated
1122
apps/monograph/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
"build": "remix vite:build",
|
||||
"dev": "remix vite:dev",
|
||||
"typecheck": "tsc",
|
||||
"start": "remix-serve ./build/server/index.js"
|
||||
"start": "bun server.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/cache": "11.11.0",
|
||||
@@ -49,7 +49,7 @@
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^2.12.1",
|
||||
"@remix-run/serve": "^2.12.1",
|
||||
"@types/bun": "^1.1.10",
|
||||
"@types/bun": "^1.1.13",
|
||||
"@types/html-to-text": "^9.0.4",
|
||||
"@types/react": "^18.3.9",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
@@ -58,7 +58,7 @@
|
||||
"postcss": "^8.4.47",
|
||||
"vite": "^5.4.8",
|
||||
"vite-plugin-arraybuffer": "^0.0.8",
|
||||
"vite-plugin-wasm": "^3.3.0",
|
||||
"vite-plugin-static-copy": "^2.1.0",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"wrangler": "3.78.11"
|
||||
},
|
||||
|
||||
48
apps/monograph/server.ts
Normal file
48
apps/monograph/server.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// This server file is used to serve the Remix app in production using Bun.
|
||||
// run it like so: npm run build; cd output; bun install; bun run start
|
||||
// Running it directly will give an error.
|
||||
import type { ServerBuild } from "@remix-run/server-runtime";
|
||||
import { createRequestHandler } from "@remix-run/server-runtime";
|
||||
import { resolve } from "node:path";
|
||||
// @ts-expect-error server is not built yet
|
||||
import * as build from "./build/server/index";
|
||||
import { type Serve } from "bun";
|
||||
|
||||
const remix = createRequestHandler(
|
||||
build as unknown as ServerBuild,
|
||||
Bun.env.NODE_ENV
|
||||
);
|
||||
process.env.PORT = process.env.PORT || "3000";
|
||||
|
||||
export default {
|
||||
port: process.env.PORT,
|
||||
async fetch(request) {
|
||||
// First we need to send handle static files
|
||||
const { pathname } = new URL(request.url);
|
||||
const file = Bun.file(
|
||||
resolve(__dirname, "./build/client/", `.${pathname}`)
|
||||
);
|
||||
if (await file.exists()) return new Response(file);
|
||||
// Only if a file doesn't exists we send the request to the Remix request handler
|
||||
return remix(request);
|
||||
}
|
||||
} satisfies Serve;
|
||||
@@ -9,7 +9,7 @@
|
||||
],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||
"types": ["vite/client", "vite-plugin-arraybuffer/types"],
|
||||
"types": ["vite/client", "vite-plugin-arraybuffer/types", "bun"],
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
@@ -20,11 +20,11 @@ import { vitePlugin as remix } from "@remix-run/dev";
|
||||
import { defineConfig } from "vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
import arraybuffer from "vite-plugin-arraybuffer";
|
||||
import wasm from "vite-plugin-wasm";
|
||||
import ThemeDark from "@notesnook/theme/theme-engine/themes/default-dark.json" with { type: "json" };
|
||||
import ThemeDark from "@notesnook/theme/theme-engine/themes/default-dark.json" with {type:"json"};
|
||||
import type { Plugin, ResolvedConfig } from "vite";
|
||||
import { writeFile } from "fs/promises";
|
||||
import path from "path";
|
||||
import { viteStaticCopy } from "vite-plugin-static-copy";
|
||||
import * as pkg from "./package.json";
|
||||
|
||||
const DEDUPE = [
|
||||
@@ -44,18 +44,18 @@ const DEFAULT_THEME_KEY =
|
||||
export default defineConfig(({ isSsrBuild }) => ({
|
||||
plugins: [
|
||||
writePlugin({
|
||||
"package.json": JSON.stringify({
|
||||
"../package.json": JSON.stringify({
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
type: "module",
|
||||
scripts: { start: pkg.scripts.start },
|
||||
dependencies: {
|
||||
"@remix-run/serve": pkg.devDependencies["@remix-run/serve"],
|
||||
"sharp": pkg.dependencies.sharp
|
||||
}
|
||||
"@napi-rs/canvas": pkg.dependencies["@napi-rs/canvas"]
|
||||
},
|
||||
})
|
||||
}),
|
||||
remix({
|
||||
buildDirectory: "output/build",
|
||||
future: {
|
||||
v3_fetcherPersist: true,
|
||||
v3_relativeSplatPath: true,
|
||||
@@ -64,7 +64,12 @@ export default defineConfig(({ isSsrBuild }) => ({
|
||||
}),
|
||||
tsconfigPaths(),
|
||||
arraybuffer(),
|
||||
wasm()
|
||||
isSsrBuild ? viteStaticCopy({
|
||||
targets: [
|
||||
{ src: "./server.ts", dest: "../../" },
|
||||
{ src: "./app/assets", dest: "../" }
|
||||
]
|
||||
}) : undefined
|
||||
],
|
||||
worker: {
|
||||
format: "es",
|
||||
@@ -75,15 +80,15 @@ export default defineConfig(({ isSsrBuild }) => ({
|
||||
}
|
||||
},
|
||||
ssr: {
|
||||
...(process.env.NODE_ENV === "development" ? {} : { noExternal: true, external: ["sharp"] }),
|
||||
...(process.env.NODE_ENV === "development"
|
||||
? {}
|
||||
: { noExternal: true, external: ["@napi-rs/canvas"] }),
|
||||
target: "node"
|
||||
},
|
||||
build: {
|
||||
target: isSsrBuild ? "node20" : undefined,
|
||||
rollupOptions: {
|
||||
external: [
|
||||
"sharp"
|
||||
]
|
||||
external: ["@napi-rs/canvas"]
|
||||
}
|
||||
},
|
||||
define: {
|
||||
|
||||
Reference in New Issue
Block a user