This commit is contained in:
@@ -38,7 +38,8 @@ npm run dev # start the dev server at http://localhost:5173
|
||||
### Other scripts
|
||||
|
||||
```bash
|
||||
npm run build # type-check and produce a production build in dist/
|
||||
npm run build # type-check, build to dist/, then inject the Discord embed
|
||||
npm run embed # (re)inject the Discord embed into an existing dist/ build
|
||||
npm run preview # serve the production build locally
|
||||
npm test # run the estimation-engine tests
|
||||
```
|
||||
@@ -136,6 +137,71 @@ Because `config.json` sits alongside `index.html` in the deployed files and is f
|
||||
runtime, you can update plan numbers, dates, or breaks by editing the deployed
|
||||
`config.json` directly. No rebuild or redeploy of the JS is required — just reload the page.
|
||||
|
||||
## Discord link embed
|
||||
|
||||
The page's `<head>` includes a Discord [Components V2](https://discord.com/developers/docs/components/reference)
|
||||
link embed (`<script id="discord:component-embed" type="application/json">`). When the site URL is
|
||||
pasted into Discord, its crawler reads that JSON and renders a rich card showing the site name, a
|
||||
short blurb, the site icon, and the **Red plan's on-pace estimate for the current day** (blocks
|
||||
left, FLEX left, suggested weekly pace).
|
||||
|
||||
Because Discord's crawler reads the static HTML and does not run the page's JavaScript, this data
|
||||
is baked in at build time by [`scripts/inject-embed.mjs`](scripts/inject-embed.mjs). That script
|
||||
uses the same estimation engine the app uses, so the embed and the app always agree. Two
|
||||
consequences worth knowing:
|
||||
|
||||
- The embed is a **snapshot from the moment of the build** — it does not update on its own. The
|
||||
nightly deploy (below) rebuilds it so it stays current.
|
||||
- It cannot show per-user results, since those only exist after the JS runs in a real browser.
|
||||
|
||||
`SITE_ICON_URL` controls the absolute icon URL Discord fetches; the deploy workflow sets it from
|
||||
`SITE_BASE_URL`.
|
||||
|
||||
## Automated nightly deployment (Gitea Actions)
|
||||
|
||||
[`.gitea/workflows/deploy.yml`](.gitea/workflows/deploy.yml) rebuilds the site every night
|
||||
(refreshing the embed snapshot), uploads `dist/` to CMU AFS space over SFTP, and then triggers
|
||||
CMU's publish CGI to make the files live. It also runs on pushes to `main` and can be triggered
|
||||
manually from the Gitea Actions UI.
|
||||
|
||||
### Required secrets
|
||||
|
||||
Set these in your Gitea repo under **Settings → Actions → Secrets**:
|
||||
|
||||
| Secret | Required | Description |
|
||||
| ------------------ | -------- | --------------------------------------------------------------------------- |
|
||||
| `SFTP_HOST` | yes | SFTP server hostname (e.g. `unix.andrew.cmu.edu`). |
|
||||
| `SFTP_USERNAME` | yes | Your Andrew (AFS) username. Also used for the publish callback. |
|
||||
| `SFTP_PASSWORD` | yes | Password for the SFTP account. |
|
||||
| `SFTP_REMOTE_PATH` | yes | Absolute remote directory to upload into (e.g. `/afs/andrew.cmu.edu/usr/<user>/www/meal-estimator`). |
|
||||
| `SITE_BASE_URL` | yes | Public base URL of the site (e.g. `https://www.andrew.cmu.edu/user/<user>`). Used to build the embed icon URL. No trailing slash. |
|
||||
| `SFTP_PORT` | no | SFTP port; defaults to `22` if unset. |
|
||||
|
||||
### What the workflow does
|
||||
|
||||
1. Checks out the repo and installs dependencies with `npm ci`.
|
||||
2. Runs `npm run build`, which type-checks, bundles, and injects the current day's Red-plan embed
|
||||
(with `SITE_ICON_URL` derived from `SITE_BASE_URL`).
|
||||
3. Uploads `dist/` to `SFTP_REMOTE_PATH` using `lftp mirror -R --delete` (the remote is mirrored
|
||||
to match the build; files no longer present locally are removed).
|
||||
4. Calls `https://www.andrew.cmu.edu/cgi-bin/publish?FLAG=0&NAME=<SFTP_USERNAME>` to publish. The
|
||||
step fails loudly if the publish request returns an HTTP error.
|
||||
|
||||
### Scheduling note
|
||||
|
||||
The cron in the workflow is in **UTC**. It is set to `0 5 * * *`, which is midnight US Eastern
|
||||
during EST. For EDT (daylight saving), use `0 4 * * *`. Adjust to taste, or keep both in mind when
|
||||
the clocks change.
|
||||
|
||||
### Security notes
|
||||
|
||||
- The SFTP password is passed to `lftp` via an environment variable interpolated into the command;
|
||||
keep it only in Gitea secrets, never in the repo.
|
||||
- Host-key checking is disabled on the runner (`sftp:auto-confirm yes`) because CMU's host key is
|
||||
not pre-seeded there. The transfer is still encrypted, but this does not verify the server's
|
||||
identity. If you want strict host-key verification, add the server's key to the runner and
|
||||
remove that setting.
|
||||
|
||||
## Data persistence
|
||||
|
||||
The selected plan, checked breaks, and additional-days value are saved to the browser's
|
||||
@@ -147,9 +213,10 @@ are ignored on load.
|
||||
## Project structure
|
||||
|
||||
```
|
||||
index.html # app shell
|
||||
index.html # app shell (includes the Discord embed placeholder)
|
||||
public/
|
||||
config.json # all tunable values (plans, dates, breaks); served + fetched at runtime
|
||||
favicon.svg # site icon (fork + progress arc)
|
||||
src/
|
||||
types.ts # shared types
|
||||
dates.ts # whole-day date helpers
|
||||
@@ -162,6 +229,10 @@ src/
|
||||
style.css # dark CMU-accented theme
|
||||
engine.test.ts # engine unit tests
|
||||
sanity.test.ts # checks against the shipped config
|
||||
scripts/
|
||||
inject-embed.mjs # build-time: injects today's Red-plan Discord embed into dist/
|
||||
.gitea/workflows/
|
||||
deploy.yml # nightly build + SFTP deploy + CMU publish
|
||||
```
|
||||
|
||||
## Tech stack
|
||||
|
||||
Reference in New Issue
Block a user