Add records, Elijah scores, per-session notes, and PWA update prompt
App features: - Personal-best records per metric: manually settable in Settings and auto-updated when a session beats them; shown in the log modal and a new dashboard "Personal records" card. - Juggling now counts by 1 instead of 5. - 1-on-1 with Elijah gains Technical Skill and Effort scores (out of 10) as manual inputs, plus an optional per-session note. - Service worker now uses a controlled update flow: an in-app "new version ready" banner activates the update on tap and reloads. Data model: - category_metrics gains track_record + record; entries gains note. - Idempotent migrations bring existing databases up to date (juggling step/record, Elijah score metrics) alongside the updated seed. StartOS package: - Bump to 0.1.2:0 with release notes. - Build x86_64 only (drop aarch64) per deployment target.
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
||||
ARCHES := x86 arm
|
||||
ARCHES := x86
|
||||
# overrides to s9pk.mk must precede the include statement
|
||||
|
||||
# Build x86_64 + aarch64 only (matches manifest images.arch).
|
||||
|
||||
+68
-55
@@ -1,16 +1,23 @@
|
||||
<p align="center">
|
||||
<img src="icon.svg" alt="Hello World Logo" width="21%">
|
||||
<img src="icon.svg" alt="Premier Gunner Logo" width="21%">
|
||||
</p>
|
||||
|
||||
# Hello World on StartOS
|
||||
# Premier Gunner on StartOS
|
||||
|
||||
> **Upstream repo:** <https://github.com/Start9Labs/hello-world>
|
||||
> **Upstream repo:** <https://github.com/ten31/premier-gunner>
|
||||
|
||||
A minimal reference service for StartOS. It displays a simple web page — nothing more. Use [this repository](https://github.com/Start9Labs/hello-world-startos) as a template when packaging a new service for StartOS.
|
||||
Premier Gunner is a kid-friendly, mobile-friendly soccer training tracker for a single player. It logs daily training across categories, plans future sessions, tracks goals, and shows progress toward a big reward. This repository packages the app as a StartOS `.s9pk` service for StartOS 0.4.0.x.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To learn how to use this template to create your own StartOS service package, see the [Packaging Guide](https://docs.start9.com/packaging).
|
||||
This package builds its own Docker image from the vendored Node app (it does **not** pull a prebuilt image from a registry). The build copies the app source into `./app` and installs/compiles its dependencies inside the image.
|
||||
|
||||
```sh
|
||||
npm ci # install the start-sdk packaging deps
|
||||
make # vendor the app, build images, and pack the .s9pk(s)
|
||||
```
|
||||
|
||||
To learn the general workflow, see the [StartOS Packaging Guide](https://docs.start9.com/packaging/0.4.0.x/).
|
||||
|
||||
---
|
||||
|
||||
@@ -25,79 +32,88 @@ To learn how to use this template to create your own StartOS service package, se
|
||||
- [Backups and Restore](#backups-and-restore)
|
||||
- [Health Checks](#health-checks)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Limitations and Differences](#limitations-and-differences)
|
||||
- [What Is Unchanged from Upstream](#what-is-unchanged-from-upstream)
|
||||
- [Contributing](#contributing)
|
||||
- [Build Layout](#build-layout)
|
||||
- [Quick Reference for AI Consumers](#quick-reference-for-ai-consumers)
|
||||
|
||||
---
|
||||
|
||||
## Image and Container Runtime
|
||||
|
||||
| Property | Value |
|
||||
| ------------- | -------------------------------------- |
|
||||
| Image | `ghcr.io/start9labs/hello-world` |
|
||||
| Architectures | x86_64, aarch64, riscv64 |
|
||||
| Command | `hello-world` |
|
||||
| Property | Value |
|
||||
| ------------- | ---------------------------------------------- |
|
||||
| Image | Built locally from `./Dockerfile` (Node 22) |
|
||||
| Architectures | x86_64, aarch64 |
|
||||
| Command | `node src/server.js` (cwd `/app`) |
|
||||
|
||||
---
|
||||
|
||||
## Volume and Data Layout
|
||||
|
||||
| Volume | Mount Point | Purpose |
|
||||
| ------ | ----------- | --------------- |
|
||||
| `main` | `/data` | Persistent data |
|
||||
| Volume | Mount Point | Purpose |
|
||||
| ------ | ----------- | ------------------------------------------------------------- |
|
||||
| `main` | `/data` | SQLite database, sessions, and `store.json` (login password) |
|
||||
|
||||
The app reads `PG_DATA_DIR=/data` and writes its SQLite DB there. The package's `store.json` (managed by StartOS) lives in the same volume and holds the login password.
|
||||
|
||||
---
|
||||
|
||||
## Installation and First-Run Flow
|
||||
|
||||
No special setup. Install and start — the web page is immediately available.
|
||||
1. On install, a 16-character random login password is generated and written to `store.json`.
|
||||
2. On every start, `main` reads the password from `store.json` and injects it as `PG_PASSWORD`. The app treats this env var as authoritative and (re)hashes it, so the login password always matches what StartOS holds.
|
||||
3. The user opens the **ui** interface, logs in, and (recommended) sets their own password via the **Set Login Password** action.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Management
|
||||
|
||||
No configurable settings. The service runs with no user-facing configuration.
|
||||
The only user-managed setting is the login password, handled by the **Set Login Password** action — not a config form. The action writes to `store.json`; the resulting change triggers a daemon restart (via a reactive `.const` read) so the new password takes effect immediately.
|
||||
|
||||
**StartOS-managed environment variables injected into the app:**
|
||||
|
||||
| Variable | Value | Source |
|
||||
| ------------- | -------------- | ------------------------------- |
|
||||
| `NODE_ENV` | `production` | static (enables `Secure` cookie)|
|
||||
| `PG_HOST` | `0.0.0.0` | static |
|
||||
| `PG_PORT` | `3000` | `utils.uiPort` |
|
||||
| `PG_DATA_DIR` | `/data` | `main` volume mount |
|
||||
| `PG_PASSWORD` | (random/user) | `store.json` → reactive read |
|
||||
|
||||
---
|
||||
|
||||
## Network Access and Interfaces
|
||||
|
||||
| Interface | Port | Protocol | Purpose |
|
||||
| --------- | ---- | -------- | -------------------- |
|
||||
| Web UI | 80 | HTTP | Hello World web page |
|
||||
| Interface | Port | Protocol | Purpose |
|
||||
| --------------- | ---- | -------- | ---------------------- |
|
||||
| `ui` | 3000 | HTTP | Premier Gunner web app |
|
||||
|
||||
**Access methods:**
|
||||
**Access methods:** LAN IP, `<hostname>.local`, Tor `.onion`, and custom clearnet domains.
|
||||
|
||||
- LAN IP with unique port
|
||||
- `<hostname>.local` with unique port
|
||||
- Tor `.onion` address
|
||||
- Custom domains (if configured)
|
||||
### Clearnet via StartTunnel
|
||||
|
||||
The app serves plain HTTP; StartOS/StartTunnel terminate TLS. Point a StartTunnel domain at the **ui** interface (see the [StartTunnel docs](https://docs.start9.com/start-tunnel/1.0.x/)). Because `NODE_ENV=production` sets the session cookie's `Secure` flag, the app must be reached over HTTPS — which StartTunnel provides.
|
||||
|
||||
---
|
||||
|
||||
## Actions (StartOS UI)
|
||||
|
||||
None.
|
||||
| Action | Input | Effect |
|
||||
| -------------------- | ------------------- | ------------------------------------------------------------------- |
|
||||
| Set Login Password | Password (≥4 chars) | Writes the password to `store.json`; service restarts; sessions cleared |
|
||||
|
||||
---
|
||||
|
||||
## Backups and Restore
|
||||
|
||||
**Included in backup:**
|
||||
|
||||
- `main` volume
|
||||
|
||||
**Restore behavior:** Volume is fully restored before the service starts.
|
||||
**Included in backup:** the `main` volume (database + `store.json`). Restored fully before the service starts.
|
||||
|
||||
---
|
||||
|
||||
## Health Checks
|
||||
|
||||
| Check | Method | Messages |
|
||||
| ------------- | ------------------- | ------------------------------------------------------------------ |
|
||||
| Web Interface | Port listening (80) | Success: "The web interface is ready" / Error: "The web interface is not ready" |
|
||||
| Check | Method | Messages |
|
||||
| ------------- | ---------------------- | ------------------------------------------------------------------------------- |
|
||||
| Web Interface | Port listening (3000) | Success: "The web interface is ready" / Error: "The web interface is not ready" |
|
||||
|
||||
---
|
||||
|
||||
@@ -107,35 +123,32 @@ None.
|
||||
|
||||
---
|
||||
|
||||
## Limitations and Differences
|
||||
## Build Layout
|
||||
|
||||
1. **No meaningful functionality** — this is a reference/template package only
|
||||
|
||||
---
|
||||
|
||||
## What Is Unchanged from Upstream
|
||||
|
||||
The service is identical to upstream. There are no modifications.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for build instructions and development workflow.
|
||||
- `Dockerfile` — multi-stage Node 22 build; compiles `better-sqlite3`, copies the app, runs `node src/server.js`.
|
||||
- `Makefile` — `prep` vendors the app from the parent repo into `./app` (excluding `node_modules`/`data`), then delegates to `s9pk.mk` to build both arches.
|
||||
- `startos/` — the start-sdk package definition (manifest, main, interfaces, actions, fileModels/store, versions, i18n).
|
||||
- `./app` — generated, git-ignored vendor copy of the Node app used as the Docker build context.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference for AI Consumers
|
||||
|
||||
```yaml
|
||||
package_id: hello-world
|
||||
image: ghcr.io/start9labs/hello-world
|
||||
architectures: [x86_64, aarch64, riscv64]
|
||||
package_id: premier-gunner
|
||||
image: built locally from ./Dockerfile (node:22-bookworm-slim)
|
||||
architectures: [x86_64, aarch64]
|
||||
volumes:
|
||||
main: /data
|
||||
ports:
|
||||
ui: 80
|
||||
ui: 3000
|
||||
dependencies: none
|
||||
startos_managed_env_vars: none
|
||||
actions: none
|
||||
startos_managed_env_vars:
|
||||
PG_PASSWORD: from store.json (login password, authoritative)
|
||||
PG_PORT: 3000
|
||||
PG_DATA_DIR: /data
|
||||
PG_HOST: 0.0.0.0
|
||||
NODE_ENV: production
|
||||
actions:
|
||||
- set-password (Set Login Password)
|
||||
```
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
# TODO
|
||||
|
||||
## Known follow-ups
|
||||
|
||||
- **In-app password change vs. StartOS action.** When deployed on StartOS, `PG_PASSWORD` (from `store.json`) is authoritative and re-applied on every restart. A password changed through the app's own Settings screen will be reverted on the next restart. Either hide the in-app password field under StartOS, or have the app write changes back to `store.json` so StartOS picks them up. For now, documented to use the **Set Login Password** action.
|
||||
- **Vendored app is git-ignored.** `./app` is regenerated by `make prep` and excluded from git, so app source changes do not bump the package `gitHash`. Fine for single-user deployment; revisit if publishing.
|
||||
- **riscv64 not built.** Manifest declares only x86_64 + aarch64 (Node + better-sqlite3 prebuilds). Add riscv64 only if a target host needs it.
|
||||
|
||||
## Phase 3 (deferred — not started)
|
||||
|
||||
- DGX Spark (Qwen3.6 35B, OpenAI-compatible endpoint) AI coach: login-time training suggestions and per-category drill ideas.
|
||||
|
||||
+31
-17
@@ -1,25 +1,39 @@
|
||||
# Hello World
|
||||
# Premier Gunner
|
||||
|
||||
You've installed Hello World — there's nothing to configure and nothing to set up. This page covers how to open the page it serves and where to read more. (If you're a developer, Hello World is also the recommended packaging template.)
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Hello World upstream docs](https://github.com/Start9Labs/hello-world/blob/master/README.md) — the README for the web server this package runs.
|
||||
- [StartOS Packaging Guide](https://docs.start9.com/packaging) — how to build a StartOS service package from that template.
|
||||
|
||||
## What you get on StartOS
|
||||
|
||||
- **A running web server** that serves a single static page.
|
||||
- **Nothing to configure and no actions** — the service starts on its own and is immediately usable.
|
||||
Premier Gunner is a kid-friendly, mobile-friendly soccer training tracker built for one player. Log daily training across categories (juggling, shooting, dribbling, soccer tennis, and more), plan upcoming sessions, set goals, and watch progress climb the thermometer toward the big reward.
|
||||
|
||||
## Getting set up
|
||||
|
||||
There's no setup wizard, no admin password, no first-run prompt — Hello World is usable the moment it starts. To view the page it serves:
|
||||
1. Open Premier Gunner's **Dashboard** tab and click the **Premier Gunner** web interface to open the app.
|
||||
2. You'll land on a **login screen** asking for a password. A strong random password is generated automatically when the service is installed.
|
||||
3. To set your own password, run the **Set Login Password** action (Actions tab). Enter the password Gunner will type on the login screen (at least 4 characters) and save. The service restarts and the new password takes effect immediately. Any active logins are signed out.
|
||||
|
||||
1. Open Hello World's **Dashboard** tab.
|
||||
2. Click the **Web UI** interface to open the served page in your browser.
|
||||
> The login password is managed entirely by the **Set Login Password** action. Changing it inside the app's own Settings screen will be overwritten on the next restart, so always use the action.
|
||||
|
||||
## What you get on StartOS
|
||||
|
||||
- **A single-user web app**, password protected, reachable over your StartOS networking (Tor, LAN, or a clearnet domain via StartTunnel).
|
||||
- **Daily logging** by category with point-and-click steppers, plus optional per-session notes.
|
||||
- **Personal-best records** — turn on 🏆 tracking for any metric (juggling is on by default). Set the current record by hand in Settings, and it updates automatically whenever a session beats it.
|
||||
- **1-on-1 with Elijah scores** — log a Technical Skill score and an Effort score (out of 10) alongside the session note.
|
||||
- **Planning, goals, and a dashboard** with a streak calendar, training-spread radar, improvement charts, a records list, and the main-goal thermometer.
|
||||
- **Persistent data** stored in the package's `main` volume (`/data`), included in StartOS backups.
|
||||
- **One action** — Set Login Password.
|
||||
|
||||
### Updates
|
||||
|
||||
When you install a new version of Premier Gunner, the installed web app (including a phone home-screen PWA) detects it automatically and shows a **"A new version is ready! — Refresh"** banner. Tap **Refresh** to update instantly. If you don't, it updates on its own the next time you fully close and reopen the app.
|
||||
|
||||
## Exposing on a clearnet domain (StartTunnel)
|
||||
|
||||
Premier Gunner serves plain HTTP on its interface; StartOS terminates TLS. To reach it from a normal browser on the open internet:
|
||||
|
||||
1. Install and configure **StartTunnel** and point a domain at this service's **ui** interface (see the [StartTunnel docs](https://docs.start9.com/start-tunnel/1.0.x/)).
|
||||
2. Add the clearnet address to the **ui** interface so the app's session cookie is issued for that host.
|
||||
|
||||
The app sets its session cookie with the `Secure` flag in production, so it requires HTTPS — which StartTunnel provides.
|
||||
|
||||
## Limitations
|
||||
|
||||
- Hello World is intentionally minimal. It is not a useful service on its own; it exists to demonstrate the StartOS packaging system.
|
||||
- The page content is static and cannot be customized through the StartOS UI.
|
||||
- Premier Gunner is intentionally single-user. There are no separate accounts; everyone who has the password shares the same data.
|
||||
- The login password must be changed via the **Set Login Password** action, not the in-app Settings screen, when running on StartOS.
|
||||
|
||||
@@ -14,7 +14,7 @@ export const manifest = setupManifest({
|
||||
images: {
|
||||
'premier-gunner': {
|
||||
source: { dockerBuild: { dockerfile: 'Dockerfile', workdir: '.' } },
|
||||
arch: ['x86_64', 'aarch64'],
|
||||
arch: ['x86_64'],
|
||||
},
|
||||
},
|
||||
alerts: {
|
||||
|
||||
@@ -2,13 +2,13 @@ import { IMPOSSIBLE, utils, VersionInfo } from '@start9labs/start-sdk'
|
||||
import { store } from '../fileModels/store'
|
||||
|
||||
export const current = VersionInfo.of({
|
||||
version: '0.1.0:0',
|
||||
version: '0.1.2:0',
|
||||
releaseNotes: {
|
||||
en_US: 'Initial release of Premier Gunner for StartOS.',
|
||||
es_ES: 'Versión inicial de Premier Gunner para StartOS.',
|
||||
de_DE: 'Erste Veröffentlichung von Premier Gunner für StartOS.',
|
||||
pl_PL: 'Pierwsze wydanie Premier Gunner dla StartOS.',
|
||||
fr_FR: 'Première version de Premier Gunner pour StartOS.',
|
||||
en_US: 'Personal-best records (auto-update + manual set), juggling counts by 1, technical-skill/effort scores plus per-session notes for 1-on-1 with Elijah, and an in-app "new version ready" refresh prompt.',
|
||||
es_ES: 'Récords personales (actualización automática + ajuste manual), los toques cuentan de 1 en 1, puntuaciones de técnica/esfuerzo con notas por sesión para el 1 contra 1 con Elijah, y un aviso de actualización dentro de la app.',
|
||||
de_DE: 'Persönliche Bestwerte (automatisch + manuell setzbar), Jonglieren zählt in 1er-Schritten, Technik-/Einsatz-Bewertungen mit Notizen pro Einheit für 1-gegen-1 mit Elijah und ein In-App-Hinweis „Neue Version verfügbar".',
|
||||
pl_PL: 'Rekordy życiowe (automatyczna aktualizacja + ręczne ustawianie), żonglerka liczona co 1, oceny techniki/zaangażowania z notatkami dla sesji 1 na 1 z Elijah oraz powiadomienie o aktualizacji w aplikacji.',
|
||||
fr_FR: "Records personnels (mise à jour automatique + réglage manuel), jonglages comptés par 1, notes de technique/d'effort avec commentaires par séance pour le 1-contre-1 avec Elijah, et une invite de mise à jour dans l'application.",
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {
|
||||
|
||||
Reference in New Issue
Block a user