Merge pull request #118 from SirTenzin/perf/bun-runtime

perf: ️ introduce bun runtime in dockerfiles and package.json
This commit is contained in:
John Yeo
2025-07-22 09:58:43 +01:00
committed by GitHub
26 changed files with 157 additions and 15413 deletions

View File

@@ -95,7 +95,7 @@ services:
# Run localtunnel
localtunnel:
image: node:20-alpine
image: oven/bun:latest
build:
dockerfile: docker/dev.dockerfile
context: .

View File

@@ -32,14 +32,12 @@ services:
context: .
dockerfile: docker/prod.dockerfile
target: server-prod
volumes:
- ./server:/app/server
ports:
- "8080:8080"
restart: always
localtunnel:
image: node:20-alpine
image: oven/bun:latest
build:
dockerfile: docker/prod.dockerfile
context: .

View File

@@ -1,23 +1,19 @@
# Multi-stage Dockerfile for Autumn development
FROM node:22-alpine AS base
FROM oven/bun:latest AS base
WORKDIR /app
RUN npm install -g pnpm
# Skip Puppeteer Chromium download to speed up install
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_SKIP_DOWNLOAD=true
COPY package*.json ./
COPY pnpm-workspace.yaml ./
COPY pnpm-lock.yaml ./
COPY package.json ./
COPY bun.lock ./
COPY shared/package*.json ./shared/
COPY server/package*.json ./server/
COPY vite/package*.json ./vite/
RUN pnpm install --frozen-lockfile
RUN npm install -g nodemon tsx
RUN bun install
# Stage 1: /localtunnel
FROM base AS localtunnel
@@ -29,26 +25,28 @@ CMD ["sh", "localtunnel-start.sh"]
FROM base AS shared
COPY shared/ ./shared/
WORKDIR /app/shared
RUN pnpm run build
CMD ["pnpm", "run", "dev"]
RUN bun run build
CMD ["bun", "dev"]
# Stage 3: /vite
FROM base AS vite
COPY --from=shared /app/shared/dist ./shared/dist
WORKDIR /app/vite
COPY vite/ ./
EXPOSE 3000
CMD ["pnpm", "run", "dev"]
CMD ["bun", "dev"]
# Stage 4: /server
FROM base AS server
COPY --from=shared /app/shared/dist ./shared/dist
COPY server/ ./server/
WORKDIR /app/server
EXPOSE 8080
CMD ["pnpm", "run", "dev"]
CMD ["bun", "dev"]
# Stage 5: Workers
FROM base AS workers
COPY --from=shared /app/shared/dist ./shared/dist
COPY server/ ./server/
WORKDIR /app/server
# RUN pnpm install
CMD ["pnpm", "run", "workers"]
CMD ["bun", "workers:dev"]

View File

@@ -1,45 +1,52 @@
# ---- Base dependencies ----
FROM node:18-alpine AS base
FROM oven/bun:latest AS base
WORKDIR /app
RUN npm install -g pnpm serve typescript tsc tsc-alias tsx
RUN bun install -g serve typescript tsc tsc-alias tsx
COPY package*.json pnpm-workspace.yaml pnpm-lock.yaml ./
COPY package.json bun.lock ./
COPY shared/package*.json ./shared/
COPY server/package*.json ./server/
COPY vite/package*.json ./vite/
RUN pnpm install
RUN bun install
FROM base AS localtunnel
WORKDIR /app
COPY localtunnel-start.sh ./
CMD ["sh", "localtunnel-start.sh"]
# # ---- Build frontend (vite) ----
# ---- Build shared package ----
FROM base AS shared-build
COPY shared/ ./shared/
WORKDIR /app/shared
RUN bun run build
# ---- Build frontend (vite) ----
FROM base AS vite-build
COPY --from=shared-build /app/shared/dist ./shared/dist
COPY . .
WORKDIR /app
RUN pnpm run vite:build
RUN bun run build
# ---- Build backend (server) ----
FROM base AS server-build
COPY --from=shared-build /app/shared/dist ./shared/dist
COPY . .
WORKDIR /app
RUN pnpm run server:build
# RUN bun run server:build:bun
# ---- Production frontend image ----
FROM vite-build AS vite-prod
EXPOSE 3000
WORKDIR /app
CMD ["pnpm", "run", "vite:start"]
CMD ["bun", "start"]
FROM server-build AS server-prod
EXPOSE 8080
WORKDIR /app
CMD ["pnpm", "run", "server:start"]
WORKDIR /app/server
CMD ["bun", "start"]
FROM server-build AS workers-prod
EXPOSE 8080
WORKDIR /app
CMD ["pnpm", "run", "server:workers"]
WORKDIR /app/server
CMD ["bun", "workers"]

View File

@@ -14,8 +14,8 @@ if [ -z "$LOCALTUNNEL_RESERVED_KEY" ]; then
fi
echo "Installing localtunnel..."
echo "Reserved key: ${LOCALTUNNEL_RESERVED_KEY}"
npm install -g localtunnel
echo "Reserved key: ${LOCAL_TUNNEL_RESERVED_KEY}"
bun install -g localtunnel
echo "Server is ready! Starting localtunnel..."

View File

@@ -1,40 +1,56 @@
{
"name": "autumn",
"private": true,
"workspaces": [
"server",
"shared",
"vite"
],
"type": "module",
"scripts": {
"vite:build": "pnpm -F shared build && pnpm -F vite build",
"vite:start": "pnpm -F vite start",
"server:build": "pnpm -F shared build && pnpm -F server prod:build",
"server:start": "pnpm -F server prod:start",
"server:cron": "pnpm -F server cron:start",
"server:workers": "pnpm -F server workers:start",
"server:check": "NODE_ENV=production pnpm -F server check",
"dev": "concurrently \"cd server && npm run dev\" \"cd vite && npm run dev\" \"redis-server\"",
"dev:wsl": "concurrently \"cd server && npm run dev\" \"cd vite && npm run dev\" \"cd shared && npm run dev\"",
"setup": "node setup.js",
"setupci": "node setupci",
"db:push": " pnpm -F shared db:push",
"db:generate": "pnpm -F shared db:generate",
"db:migrate": " pnpm -F shared db:migrate",
"docker:up": "docker compose -f docker-compose.dev.yml up --build"
},
"dependencies": {
"@wooorm/starry-night": "^3.8.0",
"ag-charts-react": "^12.0.1",
"chalk": "^5.3.0",
"drizzle-kit": "^0.31.1",
"tailwind-scrollbar-hide": "^4.0.0"
},
"devDependencies": {
"@types/node": "^24.0.3",
"dotenv": "^16.5.0",
"inquirer": "^12.6.3",
"concurrently": "^9.1.2"
}
"name": "autumn",
"private": true,
"workspaces": [
"server",
"shared",
"vite"
],
"type": "module",
"scripts": {
"vite:build": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
"vite:start": "bun -F @autumn/vite start:bun",
"shared": "bun -F @autumn/shared build",
"server": "bun -F @autumn/shared build && bun -F @autumn/server start",
"workers": "bun -F @autumn/shared build && bun -F @autumn/server workers",
"cron": "bun -F @autumn/shared build && bun -F @autumn/server cron",
"check": "bun -F @autumn/shared build && bun -F @autumn/server check",
"server:cron": "pnpm -F server cron:start",
"server:check": "NODE_ENV=production pnpm -F server check",
"dev": "concurrently \"cd server && npm run dev\" \"cd vite && npm run dev\" \"redis-server\"",
"dev:wsl": "concurrently \"cd server && npm run dev\" \"cd vite && npm run dev\" \"cd shared && npm run dev\"",
"setup": "node setup.js",
"setupci": "node setupci",
"db:push": " pnpm -F shared db:push",
"db:generate": "pnpm -F shared db:generate",
"db:migrate": " pnpm -F shared db:migrate",
"docker:up": "docker compose -f docker-compose.dev.yml up --build",
"docker:up:unix": "docker compose -f docker-compose.unix.yml up --build",
"docker:up:ci": "docker compose -f docker-compose.ci.yml up --build",
"build:all": "pnpm -F shared build && pnpm -F server prod:build && pnpm -F vite build",
"vite:build:bun": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
"vite:start:bun": "bun -F @autumn/shared build && bun -F @autumn/vite start:bun",
"dev:bun": "concurrently \"cd server && bun run dev:bun\" \"cd vite && bun run dev:bun\"",
"build:all:bun": "bun run -F @autumn/shared build:bun && bun run -F @autumn/server prod:build:bun && bun run -F @autumn/vite build:bun"
},
"dependencies": {
"@wooorm/starry-night": "^3.8.0",
"ag-charts-react": "^12.0.1",
"chalk": "^5.3.0",
"drizzle-kit": "^0.31.1",
"tailwind-scrollbar-hide": "^4.0.0"
},
"devDependencies": {
"@types/node": "^24.0.3",
"dotenv": "^16.5.0",
"inquirer": "^12.6.3",
"concurrently": "^9.1.2"
}
}

15262
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,17 @@
# Print existing env:
if [ -f .env.prod ]; then
echo "Current env: local"
elif [ -f .env.local ]; then
echo "Current env: local"
else
echo "Current env: none"
fi
# If arg1 is prod:
if [ "$1" = "prod" ]; then
# If .env and .env.prod exists, then switch
if [ -f .env ] && [ -f .env.prod ]; then
mv .env .env.local
mv .env .env.loc
mv .env.prod .env
fi
fi
# If arg1 is local:
if [ "$1" = "local" ]; then
if [ -f .env ] && [ -f .env.local ]; then
if [ -f .env ] && [ -f .env.loc ]; then
cp .env .env.prod # Copy current .env to .env.prod first
cp .env.local .env # Copy .env.local to .env
rm .env.local # Remove the .env.local file
cp .env.loc .env # Copy .env.local to .env
rm .env.loc # Remove the .env.local file
fi
fi

View File

@@ -6,28 +6,13 @@
"type": "module",
"scripts": {
"email": "email dev -p 3001",
"dev": "cross-env NODE_ENV=development npx nodemon --no-deprecation --exec npx tsx src/index.ts --ignore scripts --ignore tests",
"dev:bun": "cross-env NODE_ENV=development bunx nodemon --no-deprecation --exec bun src/index.ts --ignore scripts --ignore tests",
"start": "npx tsx src/index.ts",
"workers": "npx tsx watch src/workers.ts",
"workers:start": "node --no-deprecation dist/src/workers.js",
"check": "tsx src/check.ts",
"build": "tsc -b tsconfig.build.json && cp -r src/external/clickhouse/queries dist/src/external/clickhouse/",
"prod": "tsc -b tsconfig.build.json && tsc-alias && node dist/src/index.js",
"prod:build": "tsc -b tsconfig.build.json && tsc-alias && cp -r src/external/clickhouse/queries dist/src/external/clickhouse/",
"prod:start": "node dist/src/index.js",
"cron": "tsx src/cron.ts",
"cron:start": "node --no-deprecation dist/src/cron.js",
"testa-parallel": "mocha 'tests/alex/00_setup.ts' && mocha --parallel --timeout 10000000 'tests/alex/**/*.ts' --ignore 'tests/alex/00_setup.ts'",
"testa-browser": "mocha 'tests/alex/00_setup.ts' && mocha --timeout 10000000 'tests/alex/**/*.ts' --ignore 'tests/alex/00_setup.ts'",
"testa": "mocha 'tests/alex/00_setup.ts' && mocha --timeout 10000000 'tests/alex/01_free.ts' --ignore 'tests/alex/00_setup.ts'",
"test": "mocha --timeout 10000000 'tests/**/*.ts' --ignore 'tests/alex/**/*.ts'",
"test-custom": "mocha --timeout 1000000 'tests/01_product.ts'",
"benchmark": "tsx benchmarks/index.ts",
"benchmark:customer": "tsx benchmarks/index.ts customer",
"benchmark:attach": "tsx benchmarks/index.ts attach",
"benchmark:verbose": "tsx benchmarks/index.ts --verbose",
"benchmark:export": "tsx benchmarks/index.ts --export"
"start": "bun src/index.ts",
"dev": "NODE_ENV=development bun --watch src/index.ts --ignore scripts --ignore tests",
"workers": "bun src/workers.ts",
"workers:dev": "bun --watch src/workers.ts",
"cron": "bun src/cron.ts",
"check": "bun src/check.ts",
"build": "bun build ./src/index.ts ./src/workers.ts ./src/cron.ts --outdir dist --target bun"
},
"mocha": {
"node-option": [

View File

@@ -23,7 +23,8 @@ elif [[ $filename == *"/tests/"* ]]; then
elif [[ $filename == *".sh"* ]]; then
$filename
else
NODE_ENV=development npx tsx $filename
# NODE_ENV=development npx tsx $filename
NODE_ENV=development bun $filename
fi

View File

@@ -1,4 +1,5 @@
import "dotenv/config";
import { config } from "dotenv";
config();
import { getAllFullCustomers } from "@/utils/scriptUtils/getAll/getAllAutumnCustomers.js";
import { initDrizzle } from "@/db/initDrizzle.js";
@@ -6,16 +7,12 @@ import {
AppEnv,
BillingType,
CusProductStatus,
Customer,
FullCusProduct,
FullCustomer,
} from "@autumn/shared";
import Stripe from "stripe";
import assert from "assert";
import {
cusProductsToCusEnts,
cusProductToPrices,
} from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js";
import { cusProductToPrices } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js";
import {
findStripeItemForPrice,
isLicenseItem,
@@ -36,10 +33,11 @@ import { isFreeProduct, isOneOff } from "@/internal/products/productUtils.js";
import { getRelatedCusPrice } from "./internal/customers/cusProducts/cusEnts/cusEntUtils.js";
const { db, client } = initDrizzle({ maxConnections: 5 });
let orgSlugs = process.env.ORG_SLUGS!.split(",");
const skipEmails = process.env.SKIP_EMAILS!.split(",");
orgSlugs = ["athenahq"];
// orgSlugs = ["athenahq"];
const getSingleCustomer = async ({
stripeCli,

View File

@@ -70,7 +70,11 @@ stripeWebhookRouter.post(
try {
const webhookSecret = getStripeWebhookSecret(org, env);
event = stripe.webhooks.constructEvent(request.body, sig, webhookSecret);
event = await stripe.webhooks.constructEventAsync(
request.body,
sig,
webhookSecret
);
} catch (err: any) {
response.status(400).send(`Webhook Error: ${err.message}`);
return;

View File

@@ -2,8 +2,6 @@ import { logger } from "@/external/logtail/logtailUtils.js";
import "dotenv/config";
export const checkEnvVars = () => {
console.log("Database URL", process.env.DATABASE_URL);
if (!process.env.DATABASE_URL) {
console.error(`DATABASE_URL is not set`);
process.exit(1);

View File

@@ -7,5 +7,3 @@ const init = async () => {
};
init();
//

View File

@@ -26,17 +26,21 @@ export const completeCheckoutForm = async (
overrideQuantity?: number,
promoCode?: string
) => {
const session = await client.sessions.create();
let browser;
const browser = await puppeteer.connect({
browserWSEndpoint: session!.wsEndpoint,
defaultViewport: null,
});
// const browser = await puppeteer.launch({
// headless: true,
// args: ["--no-sandbox", "--disable-setuid-sandbox"],
// });
if (process.env.NODE_ENV === "development") {
const session = await client.sessions.create();
browser = await puppeteer.connect({
browserWSEndpoint: session!.wsEndpoint,
defaultViewport: null,
});
} else {
browser = await puppeteer.launch({
headless: false,
executablePath: "/Applications/Chromium.app/Contents/MacOS/Chromium",
args: ["--no-sandbox", "--disable-setuid-sandbox"],
});
}
try {
const page = await browser.newPage();

View File

@@ -24,6 +24,6 @@
"@emails/*": ["emails/*"]
}
},
"include": ["src", "emails"],
"include": ["src", "emails", "src/external/clickhouse/queries"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -8,7 +8,6 @@ export * from "./models/authModels/membership.js";
// Gen Models
export * from "./models/genModels/genEnums.js";
export * from "./models/genModels/genModels.js";
// 1. Org Models
export * from "./models/orgModels/orgTable.js";

View File

@@ -1,4 +1,4 @@
import { Member, User } from "@autumn/shared";
import { Member, User } from "../../db/auth-schema.js";
export interface Membership {
user: User;

View File

@@ -1,6 +1,7 @@
import { pgTable, text, numeric, jsonb, boolean } from "drizzle-orm/pg-core";
import { ChatResultFeature, ProductV2 } from "@autumn/shared";
import { ChatResultFeature } from "../chatResultModels/chatResultFeature.js";
import { ProductV2 } from "../productV2Models/productV2Models.js";
import { collatePgColumn } from "../../db/utils.js";
export const chatResults = pgTable("chat_results", {

View File

@@ -1 +0,0 @@

View File

@@ -1,9 +1,5 @@
import {
ProductItemFeatureType,
ProductItemSchema,
ProductItemInterval,
Infinite,
} from "@autumn/shared";
import { ProductItemSchema } from "./productItemModels.js";
import { Infinite } from "../../productModels/productEnums.js";
import { z } from "zod";
export const FeatureItemSchema = ProductItemSchema.pick({

View File

@@ -1,8 +1,4 @@
import {
PriceTierSchema,
ProductItemInterval,
ProductItemSchema,
} from "@autumn/shared";
import { ProductItemSchema } from "./productItemModels.js";
import { z } from "zod";
export const FeaturePriceItemSchema = ProductItemSchema.pick({

View File

@@ -1,4 +1,4 @@
import { ProductItemSchema } from "@autumn/shared";
import { ProductItemSchema } from "./productItemModels.js";
import { z } from "zod";
export const PriceItemSchema = ProductItemSchema.pick({

View File

@@ -13,8 +13,9 @@
"author": "Recase Inc.",
"license": "Apache-2.0",
"scripts": {
"build": "tsc",
"dev": "npx nodemon --watch . --ext ts --ignore dist --exec 'pnpm run build'",
"build": "bun build ./index.ts --outdir dist --target bun",
"dev": "bunx nodemon --ext ts --ignore dist --exec \"bun run build\"",
"db:push": "cross-env NODE_OPTIONS=\"--import tsx\" pnpm exec drizzle-kit push --config drizzle.config.ts",
"db:generate": "cross-env NODE_OPTIONS=\"--import tsx\" pnpm exec drizzle-kit generate --config drizzle.config.ts",
"db:migrate": "cross-env NODE_OPTIONS=\"--import tsx\" pnpm exec drizzle-kit migrate --config drizzle.config.ts"

View File

@@ -5,10 +5,13 @@
"type": "module",
"scripts": {
"dev": "vite --port 3000 --host",
"build": "tsc -b && vite build",
"build": "tsc && vite build",
"start": "serve -s dist",
"lint": "eslint .",
"preview": "vite preview",
"start": "serve -s dist"
"dev:bun": "bunx --bun vite --port 3000 --host",
"build:bun": "tsc && bunx --bun vite build",
"start:bun": "bunx --bun serve -s dist"
},
"author": "Recase Inc.",
"license": "Apache-2.0",

View File

@@ -5,7 +5,7 @@
/*
---break--- */
@plugin "tailwindcss-animate";
@import 'tailwind-scrollbar-hide/v4';
@import "tailwind-scrollbar-hide/v4";
/*
---break--- */
@custom-variant dark (&:is(.dark *));
@@ -277,6 +277,19 @@ input[type="number"]::-webkit-inner-spin-button {
}
}
/* Force responsive utilities generation for Bun compatibility */
@media (min-width: 768px) {
.md\:flex {
display: flex;
}
.md\:hidden {
display: none !important;
}
.md\:block {
display: block;
}
}
button {
cursor: pointer;
}