Compare commits
4 Commits
9d8c4055ca
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f646ee43ce | ||
|
|
cfb60d04ce | ||
|
|
f24a9ddbdf | ||
|
|
28093ccd22 |
131
Dockerfile
131
Dockerfile
@@ -1,103 +1,56 @@
|
|||||||
# ---- build stage ----
|
# syntax=docker/dockerfile:1
|
||||||
FROM node:22-bookworm-slim AS build
|
FROM node:22-bookworm-slim AS base
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# CI-friendly env
|
# Install pnpm
|
||||||
ENV HUSKY=0
|
|
||||||
ENV CI=true
|
|
||||||
|
|
||||||
# Use pnpm
|
|
||||||
RUN corepack enable && corepack prepare pnpm@9.15.9 --activate
|
RUN corepack enable && corepack prepare pnpm@9.15.9 --activate
|
||||||
|
|
||||||
# Ensure git is available for build and runtime scripts
|
# Install system dependencies
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends git \
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
git \
|
||||||
|
ca-certificates \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Accept (optional) build-time public URL for Remix/Vite (Coolify can pass it)
|
|
||||||
ARG VITE_PUBLIC_APP_URL
|
|
||||||
ENV VITE_PUBLIC_APP_URL=${VITE_PUBLIC_APP_URL}
|
|
||||||
|
|
||||||
# Install deps efficiently
|
|
||||||
COPY package.json pnpm-lock.yaml* ./
|
|
||||||
RUN pnpm fetch
|
|
||||||
|
|
||||||
# Copy source and build
|
|
||||||
COPY . .
|
|
||||||
# install with dev deps (needed to build)
|
|
||||||
RUN pnpm install --offline --frozen-lockfile
|
|
||||||
|
|
||||||
# Build the Remix app (SSR + client)
|
|
||||||
RUN NODE_OPTIONS=--max-old-space-size=4096 pnpm run build
|
|
||||||
|
|
||||||
# ---- production dependencies stage ----
|
|
||||||
FROM build AS prod-deps
|
|
||||||
|
|
||||||
# Keep only production deps for runtime
|
|
||||||
RUN pnpm prune --prod --ignore-scripts
|
|
||||||
|
|
||||||
|
|
||||||
# ---- production stage ----
|
|
||||||
FROM prod-deps AS bolt-ai-production
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Dependencies stage
|
||||||
|
FROM base AS deps
|
||||||
|
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
# Install ALL dependencies (needed for build)
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Build stage
|
||||||
|
FROM base AS build
|
||||||
|
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the app
|
||||||
|
RUN pnpm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM base AS production
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV PORT=5173
|
ENV PORT=8788
|
||||||
ENV HOST=0.0.0.0
|
|
||||||
|
|
||||||
# Non-sensitive build arguments
|
# Copy package files
|
||||||
ARG VITE_LOG_LEVEL=debug
|
COPY package.json pnpm-lock.yaml ./
|
||||||
ARG DEFAULT_NUM_CTX
|
|
||||||
|
|
||||||
# Set non-sensitive environment variables
|
# Install production dependencies
|
||||||
ENV WRANGLER_SEND_METRICS=false \
|
RUN pnpm install --prod --frozen-lockfile --ignore-scripts
|
||||||
VITE_LOG_LEVEL=${VITE_LOG_LEVEL} \
|
|
||||||
DEFAULT_NUM_CTX=${DEFAULT_NUM_CTX} \
|
|
||||||
RUNNING_IN_DOCKER=true
|
|
||||||
|
|
||||||
# Note: API keys should be provided at runtime via docker run -e or docker-compose
|
# Copy built app
|
||||||
# Example: docker run -e OPENAI_API_KEY=your_key_here ...
|
COPY --from=build /app/build ./build
|
||||||
|
COPY --from=build /app/server.mjs ./server.mjs
|
||||||
|
COPY --from=build /app/public ./public
|
||||||
|
|
||||||
# Install curl for healthchecks and copy bindings script
|
EXPOSE 8788
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends curl \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Copy built files and scripts
|
# Health check
|
||||||
COPY --from=prod-deps /app/build /app/build
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
|
||||||
COPY --from=prod-deps /app/node_modules /app/node_modules
|
CMD node -e "require('http').get('http://localhost:8788/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
||||||
COPY --from=prod-deps /app/package.json /app/package.json
|
|
||||||
COPY --from=prod-deps /app/bindings.sh /app/bindings.sh
|
|
||||||
|
|
||||||
# Pre-configure wrangler to disable metrics
|
CMD ["node", "server.mjs"]
|
||||||
RUN mkdir -p /root/.config/.wrangler && \
|
|
||||||
echo '{"enabled":false}' > /root/.config/.wrangler/metrics.json
|
|
||||||
|
|
||||||
# Make bindings script executable
|
|
||||||
RUN chmod +x /app/bindings.sh
|
|
||||||
|
|
||||||
EXPOSE 5173
|
|
||||||
|
|
||||||
# Healthcheck for deployment platforms
|
|
||||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=5 \
|
|
||||||
CMD curl -fsS http://localhost:5173/ || exit 1
|
|
||||||
|
|
||||||
# Start using dockerstart script with Wrangler
|
|
||||||
CMD ["pnpm", "run", "dockerstart"]
|
|
||||||
|
|
||||||
|
|
||||||
# ---- development stage ----
|
|
||||||
FROM build AS development
|
|
||||||
|
|
||||||
# Non-sensitive development arguments
|
|
||||||
ARG VITE_LOG_LEVEL=debug
|
|
||||||
ARG DEFAULT_NUM_CTX
|
|
||||||
|
|
||||||
# Set non-sensitive environment variables for development
|
|
||||||
ENV VITE_LOG_LEVEL=${VITE_LOG_LEVEL} \
|
|
||||||
DEFAULT_NUM_CTX=${DEFAULT_NUM_CTX} \
|
|
||||||
RUNNING_IN_DOCKER=true
|
|
||||||
|
|
||||||
# Note: API keys should be provided at runtime via docker run -e or docker-compose
|
|
||||||
# Example: docker run -e OPENAI_API_KEY=your_key_here ...
|
|
||||||
|
|
||||||
RUN mkdir -p /app/run
|
|
||||||
CMD ["pnpm", "run", "dev", "--host"]
|
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ const buttonVariants = cva(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export interface ButtonProps
|
export interface ButtonProps
|
||||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
||||||
VariantProps<typeof buttonVariants> {
|
|
||||||
_asChild?: boolean;
|
_asChild?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ export interface StickToBottomContext {
|
|||||||
const StickToBottomContext = createContext<StickToBottomContext | null>(null);
|
const StickToBottomContext = createContext<StickToBottomContext | null>(null);
|
||||||
|
|
||||||
export interface StickToBottomProps
|
export interface StickToBottomProps
|
||||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>,
|
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>, StickToBottomOptions {
|
||||||
StickToBottomOptions {
|
|
||||||
contextRef?: React.Ref<StickToBottomContext>;
|
contextRef?: React.Ref<StickToBottomContext>;
|
||||||
instance?: ReturnType<typeof useStickToBottom>;
|
instance?: ReturnType<typeof useStickToBottom>;
|
||||||
children: ((context: StickToBottomContext) => ReactNode) | ReactNode;
|
children: ((context: StickToBottomContext) => ReactNode) | ReactNode;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { json, type LoaderFunctionArgs } from '@remix-run/cloudflare';
|
export function loader() {
|
||||||
|
return new Response("OK", {
|
||||||
export const loader = async ({ request: _request }: LoaderFunctionArgs) => {
|
status: 200,
|
||||||
return json({
|
headers: {
|
||||||
status: 'healthy',
|
"Content-Type": "text/plain",
|
||||||
timestamp: new Date().toISOString(),
|
},
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|||||||
9
package-additions.json
Normal file
9
package-additions.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/express": "^2.13.1",
|
||||||
|
"@remix-run/node": "^2.13.1",
|
||||||
|
"express": "^4.19.2",
|
||||||
|
"compression": "^1.7.4",
|
||||||
|
"morgan": "^1.10.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
12
package.json
12
package.json
@@ -28,7 +28,6 @@
|
|||||||
"typecheck": "tsc",
|
"typecheck": "tsc",
|
||||||
"typegen": "wrangler types",
|
"typegen": "wrangler types",
|
||||||
"preview": "pnpm run build && pnpm run start",
|
"preview": "pnpm run build && pnpm run start",
|
||||||
"prepare": "husky",
|
|
||||||
"clean": "node scripts/clean.js",
|
"clean": "node scripts/clean.js",
|
||||||
"electron:dev": "node scripts/electron-dev.mjs",
|
"electron:dev": "node scripts/electron-dev.mjs",
|
||||||
"electron:dev:inspect": "NODE_ENV=development electron --inspect=9229 build/electron/main/index.mjs",
|
"electron:dev:inspect": "NODE_ENV=development electron --inspect=9229 build/electron/main/index.mjs",
|
||||||
@@ -40,7 +39,8 @@
|
|||||||
"electron:build:mac": "rm -rf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder --mac",
|
"electron:build:mac": "rm -rf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder --mac",
|
||||||
"electron:build:win": "rm -rf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder --win",
|
"electron:build:win": "rm -rf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder --win",
|
||||||
"electron:build:linux": "rm -rf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder --linux",
|
"electron:build:linux": "rm -rf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder --linux",
|
||||||
"electron:build:dist": "rm -rf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder --mwl"
|
"electron:build:dist": "rm -rf dist && pnpm electron:build:renderer && pnpm electron:build:deps && electron-builder --mwl",
|
||||||
|
"start:prod": "node server.mjs"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.18.0"
|
"node": ">=18.18.0"
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"@radix-ui/react-tooltip": "^1.1.4",
|
"@radix-ui/react-tooltip": "^1.1.4",
|
||||||
"@remix-run/cloudflare": "^2.15.2",
|
"@remix-run/cloudflare": "^2.15.2",
|
||||||
"@remix-run/cloudflare-pages": "^2.15.2",
|
"@remix-run/cloudflare-pages": "^2.15.2",
|
||||||
"@remix-run/node": "^2.15.2",
|
"@remix-run/node": "^2.13.1",
|
||||||
"@remix-run/react": "^2.15.2",
|
"@remix-run/react": "^2.15.2",
|
||||||
"@tanstack/react-virtual": "^3.13.0",
|
"@tanstack/react-virtual": "^3.13.0",
|
||||||
"@types/react-beautiful-dnd": "^13.1.8",
|
"@types/react-beautiful-dnd": "^13.1.8",
|
||||||
@@ -157,7 +157,11 @@
|
|||||||
"use-debounce": "^10.0.4",
|
"use-debounce": "^10.0.4",
|
||||||
"vite-plugin-node-polyfills": "^0.22.0",
|
"vite-plugin-node-polyfills": "^0.22.0",
|
||||||
"zod": "^3.24.1",
|
"zod": "^3.24.1",
|
||||||
"zustand": "^5.0.3"
|
"zustand": "^5.0.3",
|
||||||
|
"@remix-run/express": "^2.13.1",
|
||||||
|
"express": "^4.19.2",
|
||||||
|
"compression": "^1.7.4",
|
||||||
|
"morgan": "^1.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@blitz/eslint-plugin": "0.1.0",
|
"@blitz/eslint-plugin": "0.1.0",
|
||||||
|
|||||||
27
server.js
Normal file
27
server.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
const { createRequestHandler } = require("@remix-run/express");
|
||||||
|
const express = require("express");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const BUILD_DIR = path.join(process.cwd(), "build/server");
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// Serve static files
|
||||||
|
app.use(express.static("build/client", {
|
||||||
|
maxAge: "1y",
|
||||||
|
immutable: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Handle Remix requests
|
||||||
|
app.all(
|
||||||
|
"*",
|
||||||
|
createRequestHandler({
|
||||||
|
build: require(BUILD_DIR),
|
||||||
|
mode: process.env.NODE_ENV,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const port = process.env.PORT || 8788;
|
||||||
|
app.listen(port, "0.0.0.0", () => {
|
||||||
|
console.log(`✅ Bolt.diy running on http://0.0.0.0:${port}`);
|
||||||
|
});
|
||||||
40
server.mjs
Normal file
40
server.mjs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { createRequestHandler } from "@remix-run/express";
|
||||||
|
import { installGlobals } from "@remix-run/node";
|
||||||
|
import compression from "compression";
|
||||||
|
import express from "express";
|
||||||
|
import morgan from "morgan";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname, join } from "path";
|
||||||
|
|
||||||
|
installGlobals();
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
const BUILD_PATH = join(__dirname, "build", "server", "index.js");
|
||||||
|
const PUBLIC_PATH = join(__dirname, "build", "client");
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(compression());
|
||||||
|
app.disable("x-powered-by");
|
||||||
|
app.use(morgan("tiny"));
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
express.static(PUBLIC_PATH, {
|
||||||
|
maxAge: "1h",
|
||||||
|
immutable: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
app.all(
|
||||||
|
"*",
|
||||||
|
createRequestHandler({
|
||||||
|
build: await import(BUILD_PATH),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const port = process.env.PORT || 8788;
|
||||||
|
app.listen(port, "0.0.0.0", () => {
|
||||||
|
console.log(`Express server listening on port ${port}`);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user