docker & prod building 🎉
This commit is contained in:
parent
613b75edb6
commit
004e4926c4
|
@ -0,0 +1,10 @@
|
|||
# Canvas
|
||||
|
||||
## Running via Docker Compose
|
||||
|
||||
1. Run `npm run build:all`
|
||||
2. Run `npm run build:docker`
|
||||
3. Run `docker compose run --rm canvas npx prisma migrate deploy`
|
||||
4. (optional) Load default palette colors
|
||||
Run `docker compose run --rm canvas npm run -w packages/server prisma:seed:palette`
|
||||
5. Run `docker compose up -d`
|
|
@ -0,0 +1,34 @@
|
|||
# this docker-compose does not include a build for the Canvas image
|
||||
# generate the image via a build script
|
||||
|
||||
name: canvas
|
||||
|
||||
services:
|
||||
canvas:
|
||||
image: sc07/canvas
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- SESSION_SECRET=CHANGE ME TO RANDOM VALUE
|
||||
- REDIS_HOST=redis://redis
|
||||
- DATABASE_URL=postgres://postgres@postgres/canvas
|
||||
depends_on:
|
||||
- redis
|
||||
- postgres
|
||||
redis:
|
||||
restart: always
|
||||
image: redis:7-alpine
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', 'ping']
|
||||
volumes:
|
||||
- ./data/redis:/data
|
||||
postgres:
|
||||
restart: always
|
||||
image: postgres:14-alpine
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres']
|
||||
volumes:
|
||||
- ./data/postgres:/var/lib/postgresql/data
|
||||
environment:
|
||||
- 'POSTGRES_HOST_AUTH_METHOD=trust'
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -12,7 +12,13 @@
|
|||
"scripts": {
|
||||
"dev:client": "npm run dev -w packages/client",
|
||||
"dev:server": "npm run dev -w packages/server",
|
||||
"prisma:studio": "npm run prisma:studio -w packages/server"
|
||||
"prisma:studio": "npm run prisma:studio -w packages/server",
|
||||
"build:all": "./packages/build/build-all.sh",
|
||||
"build:docker": "./packages/build/docker-build.sh",
|
||||
"build:lib": "npm run build -w packages/lib",
|
||||
"build:client": "npm run build -w packages/client",
|
||||
"build:admin": "npm run build -w packages/admin",
|
||||
"build:server": "npm run build -w packages/server"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# expose APP_ROOT to client, for routing
|
||||
VITE_APP_ROOT=$APP_ROOT
|
|
@ -8,22 +8,27 @@ import { Root } from "./Root.tsx";
|
|||
import { HomePage } from "./pages/Home/page.tsx";
|
||||
import { AccountsPage } from "./pages/Accounts/Accounts/page.tsx";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
const router = createBrowserRouter(
|
||||
[
|
||||
{
|
||||
path: "/",
|
||||
element: <Root />,
|
||||
children: [
|
||||
{
|
||||
path: "/",
|
||||
element: <HomePage />,
|
||||
},
|
||||
{
|
||||
path: "/accounts",
|
||||
element: <AccountsPage />,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
{
|
||||
path: "/",
|
||||
element: <Root />,
|
||||
children: [
|
||||
{
|
||||
path: "/",
|
||||
element: <HomePage />,
|
||||
},
|
||||
{
|
||||
path: "/accounts",
|
||||
element: <AccountsPage />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
basename: import.meta.env.VITE_APP_ROOT,
|
||||
}
|
||||
);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
|
|
|
@ -5,8 +5,10 @@ import react from "@vitejs/plugin-react";
|
|||
export default defineConfig({
|
||||
root: "src",
|
||||
envDir: "..",
|
||||
base: process.env.APP_ROOT,
|
||||
build: {
|
||||
outDir: "../dist",
|
||||
emptyOutDir: true,
|
||||
},
|
||||
plugins: [
|
||||
react({
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# this file needs to be copied to /build
|
||||
|
||||
FROM node:20-alpine
|
||||
|
||||
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
|
||||
WORKDIR /home/node/app
|
||||
COPY --chown=node:node . /home/node/app/
|
||||
USER node
|
||||
RUN npm install --omit=dev
|
||||
RUN npx prisma generate
|
||||
|
||||
ENV PORT 3000
|
||||
ENV NODE_ENV production
|
||||
ENV SERVE_CLIENT /home/node/app/packages/client
|
||||
ENV SERVE_ADMIN /home/node/app/packages/admin
|
||||
|
||||
EXPOSE 3000
|
||||
CMD [ "npm", "-w", "packages/server", "start" ]
|
|
@ -0,0 +1,68 @@
|
|||
#!/bin/bash
|
||||
|
||||
# builds client, admin & server into one folder
|
||||
# - client is mounted at /
|
||||
# - admin is mounted at /admin
|
||||
|
||||
# ensure we are in packages/build
|
||||
MY_DIR="$(cd "$(dirname "$0")"; pwd)"
|
||||
OUT_DIR="$(cd "$MY_DIR/../../build"; pwd)"
|
||||
cd $MY_DIR
|
||||
|
||||
# empty out directory
|
||||
rm -rf $OUT_DIR/*
|
||||
mkdir -p $OUT_DIR/packages/lib
|
||||
mkdir -p $OUT_DIR/packages/client
|
||||
mkdir -p $OUT_DIR/packages/admin
|
||||
mkdir -p $OUT_DIR/packages/server
|
||||
mkdir -p $OUT_DIR/prisma
|
||||
|
||||
cp $MY_DIR/../../package.json $MY_DIR/../../package-lock.json $OUT_DIR/
|
||||
|
||||
LIB_DIR="$MY_DIR/../../packages/lib"
|
||||
CLIENT_DIR="$MY_DIR/../../packages/client"
|
||||
ADMIN_DIR="$MY_DIR/../../packages/admin"
|
||||
SERVER_DIR="$MY_DIR/../../packages/server"
|
||||
PRISMA_DIR="$SERVER_DIR/prisma"
|
||||
|
||||
cp -r $PRISMA_DIR/schema.prisma $PRISMA_DIR/migrations $OUT_DIR/prisma/
|
||||
|
||||
# --- Shared Library ---
|
||||
|
||||
echo "Building lib..."
|
||||
|
||||
cd "$MY_DIR/../.." && npm run-script build:lib
|
||||
cd $LIB_DIR
|
||||
mv dist $OUT_DIR/packages/lib
|
||||
cp package.json $OUT_DIR/packages/lib/
|
||||
|
||||
# janky? fix to keep imports in dev
|
||||
sed -i -e 's/"main": ".*"/"main": ".\/dist\/index.js"/' $OUT_DIR/packages/lib/package.json
|
||||
|
||||
# --- Main Client ---
|
||||
|
||||
echo "Building client..."
|
||||
|
||||
cd "$MY_DIR/../.." && npm run-script build:client
|
||||
cd $CLIENT_DIR
|
||||
mv dist/* $OUT_DIR/packages/client
|
||||
rm -r dist # this dir is empty, delete it to prevent confusion
|
||||
|
||||
# --- Admin Client ---
|
||||
|
||||
echo "Building admin..."
|
||||
|
||||
cd "$MY_DIR/../../" && APP_ROOT=/admin npm run-script build:admin
|
||||
cd $ADMIN_DIR
|
||||
mv dist/* $OUT_DIR/packages/admin
|
||||
rm -r dist # this dir is empty, delete it to prevent confusion
|
||||
|
||||
# --- Server ---
|
||||
|
||||
echo "Building server..."
|
||||
|
||||
cd "$MY_DIR/../../" && npm run-script build:server
|
||||
cd $SERVER_DIR
|
||||
mv dist $OUT_DIR/packages/server
|
||||
cp package.json tool.sh $OUT_DIR/packages/server
|
||||
# rm -r dist # this dir is empty, delete it to prevent confusion
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
MY_DIR="$(cd "$(dirname "$0")"; pwd)"
|
||||
OUT_DIR="$(cd "$MY_DIR/../../build"; pwd)"
|
||||
cd $MY_DIR
|
||||
|
||||
cp Dockerfile $OUT_DIR/
|
||||
cd $OUT_DIR
|
||||
|
||||
docker build . -t sc07/canvas
|
|
@ -6,6 +6,7 @@ export default defineConfig({
|
|||
envDir: "..",
|
||||
build: {
|
||||
outDir: "../dist",
|
||||
emptyOutDir: true,
|
||||
},
|
||||
plugins: [
|
||||
react({
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
"name": "@sc07-canvas/lib",
|
||||
"version": "1.0.0",
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"eventemitter3": "^5.0.1"
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import * as net from "./net";
|
||||
import { CanvasLib } from "./canvas";
|
||||
|
||||
export { net };
|
||||
export { net, CanvasLib };
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": "@tsconfig/recommended/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"inlineSourceMap": true,
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
},
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
{
|
||||
"name": "@sc07-canvas/server",
|
||||
"version": "1.0.0",
|
||||
"main": "./build/index.js",
|
||||
"scripts": {
|
||||
"dev": "DOTENV_CONFIG_PATH=.env.local nodemon -r dotenv/config src/index.ts",
|
||||
"start": "node dist/index.js",
|
||||
"build": "tsc",
|
||||
"lint": "eslint .",
|
||||
"prisma:studio": "prisma studio"
|
||||
"prisma:studio": "prisma studio",
|
||||
"prisma:migrate": "prisma migrate deploy",
|
||||
"prisma:seed:palette": "./tool.sh seed_palette"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
@ -21,7 +24,6 @@
|
|||
"nodemon": "^3.0.1",
|
||||
"prettier": "^3.0.1",
|
||||
"prisma": "^5.3.1",
|
||||
"prisma-dbml-generator": "^0.12.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
|
@ -31,6 +33,7 @@
|
|||
"connect-redis": "^7.1.1",
|
||||
"express": "^4.18.2",
|
||||
"express-session": "^1.17.3",
|
||||
"prisma-dbml-generator": "^0.12.0",
|
||||
"redis": "^4.6.12",
|
||||
"socket.io": "^4.7.2",
|
||||
"winston": "^3.11.0"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import http from "node:http";
|
||||
import path from "node:path";
|
||||
import express, { type Express } from "express";
|
||||
import expressSession from "express-session";
|
||||
import RedisStore from "connect-redis";
|
||||
|
@ -28,6 +29,49 @@ export class ExpressServer {
|
|||
this.app = express();
|
||||
this.httpServer = http.createServer(this.app);
|
||||
|
||||
if (process.env.SERVE_CLIENT) {
|
||||
// client is needing to serve
|
||||
Logger.info(
|
||||
"Serving client UI at / using root " +
|
||||
path.join(__dirname, process.env.SERVE_CLIENT)
|
||||
);
|
||||
this.app.use(express.static(process.env.SERVE_CLIENT));
|
||||
} else {
|
||||
this.app.get("/", (req, res) => {
|
||||
res.status(404).contentType("html").send(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Canvas Server</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Canvas Server</h1>
|
||||
<p>This instance is not serving the client</p>
|
||||
<i>This instance might not be configured correctly</i>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.SERVE_ADMIN) {
|
||||
// client is needing to serve
|
||||
Logger.info(
|
||||
"Serving admin UI at /admin using root " +
|
||||
path.join(__dirname, process.env.SERVE_ADMIN)
|
||||
);
|
||||
const assetsDir = path.join(__dirname, process.env.SERVE_ADMIN, "assets");
|
||||
const indexFile = path.join(
|
||||
__dirname,
|
||||
process.env.SERVE_ADMIN,
|
||||
"index.html"
|
||||
);
|
||||
|
||||
this.app.use("/admin/assets", express.static(assetsDir));
|
||||
this.app.use("/admin/*", (req, res) => {
|
||||
res.sendFile(indexFile);
|
||||
});
|
||||
}
|
||||
|
||||
this.app.use(session);
|
||||
this.app.use("/api", APIRoutes);
|
||||
|
||||
|
|
|
@ -71,12 +71,20 @@ export class User {
|
|||
return Date.now() - this._updatedAt >= 1000 * 60;
|
||||
}
|
||||
|
||||
static async fromAuthSession(auth: AuthSession): Promise<User> {
|
||||
const user = await this.fromSub(
|
||||
auth.user.username + "@" + auth.service.instance.hostname
|
||||
);
|
||||
user.authSession = auth;
|
||||
return user;
|
||||
static async fromAuthSession(auth: AuthSession): Promise<User | undefined> {
|
||||
try {
|
||||
const user = await this.fromSub(
|
||||
auth.user.username + "@" + auth.service.instance.hostname
|
||||
);
|
||||
user.authSession = auth;
|
||||
return user;
|
||||
} catch (e) {
|
||||
if (e instanceof UserNotFound) {
|
||||
return undefined;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async fromSub(sub: string): Promise<User> {
|
||||
|
|
|
@ -31,6 +31,15 @@ declare global {
|
|||
* Specifically setting CORS origin is required because of use of credentials (cookies)
|
||||
*/
|
||||
CLIENT_ORIGIN?: string;
|
||||
|
||||
/**
|
||||
* If set, use this relative path to serve the client at the root
|
||||
*/
|
||||
SERVE_CLIENT?: string;
|
||||
/**
|
||||
* If set, use this relative path to serve the admin UI at /admin
|
||||
*/
|
||||
SERVE_ADMIN?: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"extends": "@tsconfig/recommended/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "build"
|
||||
}
|
||||
"outDir": "dist",
|
||||
"inlineSourceMap": true,
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue