Add build and deploy workflow
All checks were successful
Build and Deploy / build-deploy (push) Successful in 29s

This commit is contained in:
2026-02-21 23:45:35 +00:00
parent a327e87ac9
commit 3bc7bda1f6
18 changed files with 219 additions and 33 deletions

10
.env.example Normal file
View File

@@ -0,0 +1,10 @@
PUBLIC_WS_URL=ws://localhost:3000/ws
PUBLIC_BOX_DRAWING_CHARS=─│┌┐└┘├┤
HOST=localhost
FE_PORT=5173
BE_PORT=3000
JWT_SECRET=your-secret-here
SIGNUP_SECRET=your-signup-secret-here
ADMIN_EMAIL=admin@localhost
DB_PATH=packages/be/db.sqlite
DATA_DIR=./data

View File

@@ -0,0 +1,47 @@
name: Build and Deploy
on:
push:
branches: [master]
jobs:
build-deploy:
runs-on: ubuntu-latest
container:
image: docker:latest
volumes:
- /opt/todo3:/deploy
steps:
- name: Install dependencies
run: apk add --no-cache git nodejs
- uses: actions/checkout@v4
- name: Copy source to deploy directory
run: |
rm -rf /deploy/build
cp -r . /deploy/build
- name: Create .env file
working-directory: /deploy/build
run: |
cat > .env << 'EOF'
PUBLIC_WS_URL=${{ vars.PUBLIC_WS_URL }}
PUBLIC_BOX_DRAWING_CHARS=${{ vars.PUBLIC_BOX_DRAWING_CHARS }}
HOST=${{ vars.HOST }}
FE_PORT=${{ vars.FE_PORT }}
BE_PORT=${{ vars.BE_PORT }}
JWT_SECRET=${{ secrets.JWT_SECRET }}
SIGNUP_SECRET=${{ secrets.SIGNUP_SECRET }}
ADMIN_EMAIL=${{ vars.ADMIN_EMAIL }}
DATA_DIR=/opt/todo3/data
EOF
- name: Build and deploy
working-directory: /deploy/build
run: |
docker compose build
docker compose up -d
- name: Cleanup
run: rm -rf /deploy/build

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
node_modules
.env
*.local
packages/be/db.sqlite
up.sh

31
Dockerfile Normal file
View File

@@ -0,0 +1,31 @@
FROM oven/bun:latest AS base
WORKDIR /app
FROM base AS deps
COPY package.json bun.lockb ./
COPY packages/fe/package.json packages/fe/
COPY packages/be/package.json packages/be/
RUN bun install --frozen-lockfile
FROM deps AS fe-build
COPY packages/fe packages/fe
COPY packages/be/src packages/be/src
COPY tsconfig.json .
ARG PUBLIC_WS_URL
ARG PUBLIC_BOX_DRAWING_CHARS
ENV PUBLIC_WS_URL=$PUBLIC_WS_URL
ENV PUBLIC_BOX_DRAWING_CHARS=$PUBLIC_BOX_DRAWING_CHARS
RUN bun run --filter fe build
FROM base AS frontend
WORKDIR /app/packages/fe
COPY --from=deps /app/node_modules /app/node_modules
COPY --from=fe-build /app/packages/fe/dist ./dist
COPY packages/fe/package.json packages/fe/vite.config.ts ./
CMD ["bun", "run", "start"]
FROM base AS backend
WORKDIR /app/packages/be
COPY --from=deps /app/node_modules /app/node_modules
COPY packages/be ./
CMD ["bun", "run", "start"]

View File

@@ -1,19 +1,50 @@
# Elysia with Bun runtime
# Todo3
## Getting Started
![Screenshot](screenshot.png)
To get started with this template, simply paste this command into your terminal:
Visually, this is an attempt at replicating lovely TUI aesthetics on the web. It isn't actually rendered in text, but everything is based around the `ch` unit.
```bash
bun create elysia ./elysia-example
```
Functionally, it's a real-time collaborative todo list, exclusively via WebSockets with optimistic updates. No REST here, not even for authentication. Whether or not this is a good idea remains to be seen.
## Tech Stack
- **Frontend**: React, Vite
- **Backend**: Elysia, Bun, SQLite
- **Real-time**: WebSockets
## Development
To start the development server run:
```bash
bun run dev
cp .env.example .env
# fill in your values
```
Open http://localhost:3000/ with your browser to see the result.
### With Docker (recommended)
```bash
bun install
docker compose -f docker-compose.dev.yml up
```
Frontend: http://localhost:5173
Backend: http://localhost:3000
## Production
```bash
docker compose up --build
```
## Environment Variables
| Variable | Description |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PUBLIC_WS_URL` | WebSocket URL for frontend |
| `PUBLIC_BOX_DRAWING_CHARS` | The 8 characters for box drawing. The default value `─│┌┐└┘├┤` results in boxes that are a bit to nice, you can also try `\|++++++` for a simpler style |
| `HOST` | Allowed host for Vite |
| `FE_PORT` | Frontend port (external) |
| `BE_PORT` | Backend port (external) |
| `JWT_SECRET` | Secret for JWT signing |
| `SIGNUP_SECRET` | Required for user registration |
| `ADMIN_EMAIL` | Contact email for signup errors |
| `DB_PATH` | Path to SQLite database |

29
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,29 @@
name: todo3
services:
fe:
image: oven/bun:latest
working_dir: /app
command: bun run --filter fe dev -- --host
ports:
- '${FE_PORT:-5173}:5173'
volumes:
- .:/app
environment:
HOST: ${HOST:-localhost}
PUBLIC_WS_URL: ${PUBLIC_WS_URL:-ws://localhost:3000/ws}
PUBLIC_BOX_DRAWING_CHARS: ${PUBLIC_BOX_DRAWING_CHARS:-─│┌┐└┘├┤}
be:
image: oven/bun:latest
working_dir: /app
command: bun run --filter be dev
ports:
- '${BE_PORT:-3000}:3000'
volumes:
- .:/app
environment:
JWT_SECRET: ${JWT_SECRET}
SIGNUP_SECRET: ${SIGNUP_SECRET}
ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@localhost}
DB_PATH: /app/${DB_PATH}

30
docker-compose.yml Normal file
View File

@@ -0,0 +1,30 @@
name: todo3
services:
fe:
build:
context: .
target: frontend
args:
PUBLIC_WS_URL: ${PUBLIC_WS_URL}
PUBLIC_BOX_DRAWING_CHARS: ${PUBLIC_BOX_DRAWING_CHARS:-─│┌┐└┘├┤}
ports:
- '${FE_PORT}:4173'
environment:
HOST: ${HOST}
restart: unless-stopped
be:
build:
context: .
target: backend
ports:
- '${BE_PORT}:3000'
environment:
JWT_SECRET: ${JWT_SECRET}
SIGNUP_SECRET: ${SIGNUP_SECRET}
ADMIN_EMAIL: ${ADMIN_EMAIL}
DB_PATH: /data/db.sqlite
volumes:
- ${DATA_DIR:-./data}:/data
restart: unless-stopped

View File

@@ -1,4 +0,0 @@
JWT_SECRET=FOOBAR
SIGNUP_SECRET=FOOBAR
ADMIN_EMAIL=foo@bar.com
PORT=3000

View File

@@ -1,3 +1,3 @@
import { Database } from 'bun:sqlite';
export const db = new Database('db.sqlite');
export const db = new Database(process.env.DB_PATH ?? 'db.sqlite');

View File

@@ -1,5 +0,0 @@
#PUBLIC_BOX_DRAWING_CHARS=─│┌┐└┘├┤
PUBLIC_BOX_DRAWING_CHARS=-|++++++
PUBLIC_WS_URL=wss://echo.websocket.org
HOST=example.org
PORT=5173

View File

@@ -3,17 +3,6 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="preload"
as="style"
href="https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap"
/>
<link
href="https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap"
rel="stylesheet"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Todo 3.0</title>
</head>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,33 @@
@font-face {
font-family: 'OGCourier';
src: url('/fonts/OGCourier.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'OGCourier';
src: url('/fonts/OGCourier-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'OGCourier';
src: url('/fonts/OGCourier-Italic.woff2') format('woff2');
font-weight: 400;
font-style: oblique;
}
@font-face {
font-family: 'OGCourier';
src: url('/fonts/OGCourier-BoldItalic.woff2') format('woff2');
font-weight: 700;
font-style: oblique;
}
:root {
font-family: 'Courier Prime', monospace;
font-family: 'OGCourier', monospace;
font-weight: 400;
font-size: 20px;
font-style: normal;

View File

@@ -13,7 +13,6 @@ export default ({ mode }: { mode: string }) => {
},
preview: {
allowedHosts: [env.HOST!],
port: Number(env.PORT!),
},
});
};

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB