added configs and gitea worker
This commit is contained in:
@@ -15,3 +15,7 @@ ANTHROPIC_MODEL=claude-sonnet-5
|
||||
|
||||
# --- Server ---
|
||||
PORT=3000
|
||||
|
||||
# Public URL the app is served from. The Node server uses this to validate the
|
||||
# origin of form/POST requests. Set it to the address people actually use.
|
||||
ORIGIN=http://localhost:3000
|
||||
|
||||
55
.gitea/workflows/build-image.yml
Normal file
55
.gitea/workflows/build-image.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
# Builds the Docker image and pushes it to Gitea's built-in container registry
|
||||
# on every push to main. Tags each build as :latest and :<commit-sha>.
|
||||
#
|
||||
# PREREQUISITES on your Gitea instance:
|
||||
# 1. Actions must be enabled (app.ini: [actions] ENABLED = true) and at least
|
||||
# one act_runner registered.
|
||||
# 2. The runner must be able to build images. act_runner needs access to a
|
||||
# Docker daemon — either run the runner with the host Docker socket mounted
|
||||
# (-v /var/run/docker.sock:/var/run/docker.sock) or in docker-in-docker mode.
|
||||
# 3. The Package Registry must be enabled (it is by default).
|
||||
#
|
||||
# TOKEN: the workflow tries the auto-injected GITHUB_TOKEN first. If your Gitea
|
||||
# version does not grant it package-write scope, create a Personal Access Token
|
||||
# with "write:package" scope and add it as a repository secret named
|
||||
# REGISTRY_TOKEN (Repo > Settings > Actions > Secrets).
|
||||
|
||||
name: build-image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Compute registry host and image name
|
||||
id: meta
|
||||
run: |
|
||||
# Registry host = the Gitea instance host (strip the scheme).
|
||||
echo "registry=$(echo '${{ github.server_url }}' | sed -E 's#^https?://##')" >> "$GITHUB_OUTPUT"
|
||||
# Image path must be lowercase for OCI registries.
|
||||
echo "image=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to the Gitea container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ steps.meta.outputs.registry }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta.outputs.registry }}/${{ steps.meta.outputs.image }}:latest
|
||||
${{ steps.meta.outputs.registry }}/${{ steps.meta.outputs.image }}:${{ github.sha }}
|
||||
156
README.md
156
README.md
@@ -2,105 +2,115 @@
|
||||
|
||||
A shared, multi-user web app with two meeting tools:
|
||||
|
||||
- **Field Problem Workshop** — capture field problems, cluster them into root problems, dot-vote, build "breadcrumb" trails to measurable outcomes, and assign actions. Includes an optional AI draft feature.
|
||||
- **Field Problem Workshop** — capture field problems, cluster them into root problems, dot-vote, build "breadcrumb" trails from problem to measurable outcome, and assign actions. Includes an optional AI draft feature.
|
||||
- **Scope Lock Meeting Suite** — Scope Boundary Board, MVP Priority Ranker, Decision Registry, and Micron Pilot Readiness.
|
||||
|
||||
Unlike the original single-file HTML versions (kept in [`legacy/`](./legacy) for reference), everyone who opens the same link works from the **same data**, stored on a shared server. Edits autosave; a version check prevents two people from silently overwriting each other.
|
||||
Everyone who opens the same link works from the **same data**, stored on a shared server. Edits autosave, and a version check stops two people from silently overwriting each other. The original single-file HTML versions are kept in [`legacy/`](./legacy) for reference.
|
||||
|
||||
## Tech stack
|
||||
|
||||
| Piece | Choice | Why |
|
||||
|---|---|---|
|
||||
| Framework | SvelteKit (Svelte 5) + TypeScript | Frontend and server API in one project |
|
||||
| Server | Node (`adapter-node`) | Runs as a plain server, easy to containerize |
|
||||
| Database | PostgreSQL | Shared storage for all users |
|
||||
| Packaging | Docker + docker-compose | One command to run the whole thing internally |
|
||||
- **SvelteKit (Svelte 5) + TypeScript** — frontend and server API in one project
|
||||
- **Node** (`@sveltejs/adapter-node`) — runs as a plain server, easy to containerize
|
||||
- **PostgreSQL** — shared storage for all users (accessed with `pg`, no ORM)
|
||||
- **Docker + docker-compose** — one command to run the whole thing internally
|
||||
|
||||
## Quick start (Docker)
|
||||
|
||||
Requires Docker Desktop / Docker Engine.
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Open **http://localhost:3000**. To let others on your network in, they use `http://<your-machine-ip>:3000`.
|
||||
|
||||
```bash
|
||||
docker compose down # stop
|
||||
docker compose down -v # stop AND erase all saved data
|
||||
docker compose logs -f app # watch app logs
|
||||
```
|
||||
|
||||
Data persists in a Docker volume (`db_data`), so it survives restarts.
|
||||
|
||||
To enable the **AI draft** feature, provide an Anthropic API key before starting — either export `ANTHROPIC_API_KEY` in your shell or put it in a `.env` file next to `docker-compose.yml`. Without a key the AI button simply fails politely; everything else works.
|
||||
|
||||
## Local development (without Docker)
|
||||
|
||||
Needs Node 20+ and a PostgreSQL you can reach.
|
||||
|
||||
```bash
|
||||
cp .env.example .env # edit DATABASE_URL (and ANTHROPIC_API_KEY if wanted)
|
||||
npm install
|
||||
psql "$DATABASE_URL" -f db/init.sql # create the schema once
|
||||
npm run dev # http://localhost:5173
|
||||
```
|
||||
|
||||
Scripts: `npm run build` (production build) · `npm run preview` (serve the build) · `npm run start` (run the built server) · `npm run check` (type-check).
|
||||
|
||||
## Configuration
|
||||
|
||||
All config is via environment variables — see [`.env.example`](./.env.example):
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `DATABASE_URL` | Postgres connection string. Set automatically by docker-compose. |
|
||||
| `ANTHROPIC_API_KEY` | Enables the "AI draft" button. Blank = feature disabled. |
|
||||
| `ANTHROPIC_MODEL` | Model for AI drafting (default `claude-sonnet-5`). |
|
||||
| `PORT` | Server port (default 3000). |
|
||||
| `ORIGIN` | Public URL the app is served from; used to validate POST origins. |
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
src/
|
||||
app.css shared design tokens + base styles
|
||||
app.html page shell
|
||||
lib/
|
||||
types.ts data shapes for both tools
|
||||
workspace.svelte.ts client sync: load / autosave / poll / conflict handling
|
||||
toast.svelte.ts shared toast controller
|
||||
components/ Toast, SyncBadge
|
||||
defaults/ seeded baseline state for each tool
|
||||
components/ Toast.svelte, SyncBadge.svelte
|
||||
defaults/ seeded baseline state for each tool (index, workshop, scope-lock)
|
||||
server/
|
||||
db/ Postgres pool + workspace repository
|
||||
db/index.ts Postgres connection pool (lazy)
|
||||
db/workspaces.ts load / save / reset with optimistic concurrency
|
||||
ai.ts Anthropic proxy (holds the API key server-side)
|
||||
routes/
|
||||
+layout.svelte imports global CSS
|
||||
+page.svelte home / tool picker
|
||||
workshop/ Field Problem Workshop
|
||||
scope-lock/ Scope Lock Meeting Suite
|
||||
workshop/+page.svelte Field Problem Workshop
|
||||
scope-lock/+page.svelte Scope Lock Meeting Suite
|
||||
api/
|
||||
workspaces/[tool]/[name]/ GET (load) + PUT (save)
|
||||
workspaces/[tool]/[name]/reset/ POST (reset to baseline)
|
||||
ai/draft/ POST (AI breadcrumb draft)
|
||||
workspaces/[tool]/[name]/+server.ts GET (load) + PUT (save)
|
||||
workspaces/[tool]/[name]/reset/+server.ts POST (reset to baseline)
|
||||
ai/draft/+server.ts POST (AI breadcrumb draft)
|
||||
db/init.sql database schema (runs once on first DB startup)
|
||||
static/favicon.svg
|
||||
Dockerfile, docker-compose.yml
|
||||
legacy/ the original single-file HTML tools
|
||||
.gitea/workflows/build-image.yml CI: build + push image on push to main
|
||||
legacy/ the original single-file HTML tools + the .skill file
|
||||
```
|
||||
|
||||
## Run it with Docker (recommended)
|
||||
|
||||
Requires Docker Desktop / Docker Engine.
|
||||
|
||||
```bash
|
||||
# optional: enable the AI feature by putting a key in your shell/.env first
|
||||
# ANTHROPIC_API_KEY=sk-ant-...
|
||||
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Then open **http://localhost:3000**. To share on your network, others use `http://<your-machine-ip>:3000`.
|
||||
|
||||
```bash
|
||||
docker compose down # stop
|
||||
docker compose down -v # stop AND erase all saved data (reset the database)
|
||||
docker compose logs -f app # watch app logs
|
||||
```
|
||||
|
||||
The database persists in a Docker volume (`db_data`), so your data survives restarts.
|
||||
|
||||
## Run it for development (without Docker)
|
||||
|
||||
You need Node 20+ and a PostgreSQL you can connect to.
|
||||
|
||||
```bash
|
||||
cp .env.example .env # then edit DATABASE_URL (and ANTHROPIC_API_KEY if wanted)
|
||||
npm install
|
||||
# create the schema in your database:
|
||||
psql "$DATABASE_URL" -f db/init.sql
|
||||
npm run dev # http://localhost:5173
|
||||
```
|
||||
|
||||
Other scripts: `npm run build` (production build), `npm run preview` (serve the build), `npm run check` (type-check).
|
||||
|
||||
## Configuration
|
||||
|
||||
All config is via environment variables (see `.env.example`):
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `DATABASE_URL` | Postgres connection string. Set automatically by docker-compose. |
|
||||
| `ANTHROPIC_API_KEY` | Enables the "AI draft" button. Leave blank to disable it (the button then fails politely). |
|
||||
| `ANTHROPIC_MODEL` | Model for AI drafting (default `claude-sonnet-5`). |
|
||||
| `PORT` | Server port (default 3000). |
|
||||
| `ORIGIN` | Public URL of the app, e.g. `http://server:3000`. Needed by the Node server for form/security handling. |
|
||||
|
||||
## How the shared-workspace model works
|
||||
|
||||
- Each tool has a named workspace (default: `default`) stored as one JSON document with a `version` number.
|
||||
- When you edit, the browser autosaves after a short pause, sending your changes **plus the version you started from**.
|
||||
- If a teammate saved first, the server rejects your save (HTTP 409) and hands back their current copy, which your page loads — so nobody's work is silently overwritten. The status badge in the header shows this.
|
||||
- Open pages poll every few seconds, so a teammate's saved changes show up without a manual refresh.
|
||||
- Each tool has a named workspace (the UI uses `default`) stored as one JSON document with a `version` number.
|
||||
- Editing autosaves after a short pause, sending your changes **plus the version you started from**.
|
||||
- If a teammate saved first, the server rejects your save (HTTP 409) and returns their current copy, which your page loads — so no one's work is silently overwritten. The header status badge reflects this.
|
||||
- Open pages poll every few seconds, so a teammate's saved changes appear without a manual refresh.
|
||||
|
||||
This fits turn-taking meeting use. It is **not** live character-by-character co-editing (like Google Docs). If that's needed later, the version/polling foundation here can be upgraded to websockets.
|
||||
This fits turn-taking meeting use. It is **not** live character-by-character co-editing (like Google Docs); the version/polling foundation could be upgraded to websockets if that's ever needed.
|
||||
|
||||
## Notes / possible next steps
|
||||
## Continuous integration (Gitea Actions)
|
||||
|
||||
- **Authentication**: there is none yet — anyone with the link can view and edit. The data model already has an `updated_by` column ready for it.
|
||||
- **Multiple named workspaces**: the API supports any name (`/api/workspaces/workshop/<name>`); the UI currently uses `default`. Adding a workspace picker is straightforward.
|
||||
- **The `.skill` file** in `legacy/` is a separate Claude skill, unrelated to running the website.
|
||||
[`.gitea/workflows/build-image.yml`](./.gitea/workflows/build-image.yml) builds the Docker image and pushes it to Gitea's built-in container registry on every push to `main`, tagged `:latest` and `:<commit-sha>`.
|
||||
|
||||
It requires, on your Gitea instance: Actions enabled with a registered `act_runner`, that runner able to build images (host Docker socket mounted, or docker-in-docker), and the package registry enabled (on by default). The workflow uses the auto-injected token; if that lacks package-write scope, add a Personal Access Token with `write:package` as a repo secret named `REGISTRY_TOKEN`. Details are in the workflow file's header comments.
|
||||
|
||||
## The `.skill` file
|
||||
|
||||
`legacy/construction-breadcrumbs.skill` is a **Claude skill** (a zip of instructions + a methodology knowledge base). It is not part of the website — it teaches Claude how to draft breadcrumb fields using construction methodology (EVM, CII AWP, Last Planner, first-time quality). The same knowledge is embedded server-side in `src/lib/server/ai.ts`, which powers the app's AI draft button. Install the skill in Claude (via "Save skill") to use the methodology in any chat.
|
||||
|
||||
## Possible next steps
|
||||
|
||||
- **Authentication** — none yet; anyone with the link can view and edit. The `workspaces.updated_by` column is already there for when it's added.
|
||||
- **Multiple named workspaces** — the API accepts any name (`/api/workspaces/workshop/<name>`); the UI currently hardcodes `default`. A workspace picker would be a small addition.
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
"preview": "vite preview",
|
||||
"start": "node build",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"db:init": "node scripts/db-init.js"
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-node": "^5.2.9",
|
||||
|
||||
Reference in New Issue
Block a user