From 5c9f30b1cf1b48daa9ac679c8e791404bc228648 Mon Sep 17 00:00:00 2001 From: C-West8 <125926137+C-West8@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:18:27 -0500 Subject: [PATCH] added configs and gitea worker --- .env.example | 4 + .gitea/workflows/build-image.yml | 55 +++++++++++ README.md | 156 ++++++++++++++++--------------- package.json | 3 +- 4 files changed, 143 insertions(+), 75 deletions(-) create mode 100644 .gitea/workflows/build-image.yml diff --git a/.env.example b/.env.example index f14939d..d68d01c 100644 --- a/.env.example +++ b/.env.example @@ -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 diff --git a/.gitea/workflows/build-image.yml b/.gitea/workflows/build-image.yml new file mode 100644 index 0000000..3b22edf --- /dev/null +++ b/.gitea/workflows/build-image.yml @@ -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 :. +# +# 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 }} diff --git a/README.md b/README.md index a1c7de3..3bfbd04 100644 --- a/README.md +++ b/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://: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://: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/`); 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 `:`. + +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/`); the UI currently hardcodes `default`. A workspace picker would be a small addition. diff --git a/package.json b/package.json index 0f1f0b3..2332eec 100644 --- a/package.json +++ b/package.json @@ -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",