chore(tooling): switch to fnm + pnpm, add DEPLOYMENT.md
Pin Node 24 via .node-version/.nvmrc and pnpm 9.15.0 via package.json#packageManager. Regenerate lockfile as pnpm-lock.yaml. Rewrite README setup + scripts table around pnpm, and add a production deployment guide covering systemd, nginx, upgrades, rollback, and backups. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
24
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
save-exact=false
|
save-exact=false
|
||||||
engine-strict=true
|
engine-strict=true
|
||||||
|
auto-install-peers=true
|
||||||
|
|||||||
+300
@@ -0,0 +1,300 @@
|
|||||||
|
# Deployment
|
||||||
|
|
||||||
|
Guide for running `buildfor_life_ops` on a Linux server behind a reverse proxy. The build output of `@sveltejs/adapter-node` is a plain Node HTTP server — nothing exotic.
|
||||||
|
|
||||||
|
Stack assumptions:
|
||||||
|
|
||||||
|
- Linux host (Debian/Ubuntu preferred; works on anything with glibc ≥ 2.31)
|
||||||
|
- **fnm** for Node version management — pinned to `24` via `.node-version`
|
||||||
|
- **pnpm** `9.15.0` via Corepack — pinned in `package.json#packageManager`
|
||||||
|
- PostgreSQL 16+ reachable from the app host (local socket or remote)
|
||||||
|
- A reverse proxy terminating TLS (nginx / Caddy / Traefik — examples below use nginx)
|
||||||
|
- systemd to supervise the Node process
|
||||||
|
|
||||||
|
## 1. Prepare the host
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# As root, one-time
|
||||||
|
apt update
|
||||||
|
apt install -y build-essential curl git postgresql-client
|
||||||
|
|
||||||
|
# Dedicated service user
|
||||||
|
adduser --system --group --home /opt/buildfor_life_ops --shell /bin/bash buildfor_life_ops
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Install fnm and pin Node
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run as the service user
|
||||||
|
sudo -iu buildfor_life_ops
|
||||||
|
curl -fsSL https://fnm.vercel.app/install | bash -s -- --skip-shell
|
||||||
|
|
||||||
|
# Activate in current shell and on login
|
||||||
|
cat >> ~/.bashrc <<'EOF'
|
||||||
|
export PATH="$HOME/.local/share/fnm:$PATH"
|
||||||
|
eval "$(fnm env --use-on-cd --shell bash)"
|
||||||
|
EOF
|
||||||
|
source ~/.bashrc
|
||||||
|
|
||||||
|
# Install the pinned major — fnm resolves .node-version once the repo is cloned
|
||||||
|
fnm install 24
|
||||||
|
fnm default 24
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Enable pnpm via Corepack
|
||||||
|
|
||||||
|
Corepack ships with Node, so nothing extra to install:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
corepack enable
|
||||||
|
corepack prepare pnpm@9.15.0 --activate
|
||||||
|
pnpm --version # → 9.15.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Clone the repo and install dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/buildfor_life_ops
|
||||||
|
git clone git@gitssh.b4l.co.th:B4L/buildfor_life_ops.git app
|
||||||
|
cd app
|
||||||
|
|
||||||
|
# fnm picks up .node-version automatically on cd; verify:
|
||||||
|
node --version # → v24.x.x
|
||||||
|
|
||||||
|
# Reproducible install — fails if lockfile drifts
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Configure `.env`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env # if present — otherwise copy from a trusted source
|
||||||
|
$EDITOR .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Required keys (the app refuses to boot without them — see `src/lib/server/env.ts`):
|
||||||
|
|
||||||
|
| Key | Notes |
|
||||||
|
| --- | --- |
|
||||||
|
| `DATABASE_URL` | `postgres://user:pass@host:5432/buildfor_life_ops` |
|
||||||
|
| `SESSION_SECRET` | ≥ 32 hex chars — `openssl rand -hex 32` |
|
||||||
|
| `STORAGE_SIGNING_SECRET` | ≥ 32 hex chars, independent of `SESSION_SECRET` |
|
||||||
|
| `PUBLIC_BASE_URL` | External URL, e.g. `https://ops.b4l.co.th` |
|
||||||
|
| `STORAGE_BACKEND` | `local` or `s3` |
|
||||||
|
| `STORAGE_LOCAL_ROOT` | Absolute path to blob root, e.g. `/var/lib/buildfor_life_ops/storage` |
|
||||||
|
|
||||||
|
Optional keys:
|
||||||
|
|
||||||
|
- **SMTP**: `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM`, `SMTP_SECURE`. Email is silently disabled when any of HOST/PORT/FROM is unset.
|
||||||
|
- **Matrix**: `MATRIX_HOMESERVER`, `MATRIX_ACCESS_TOKEN`. Matrix delivery is disabled when either is unset. The per-company room comes from `companies.settings.matrix_room_id`.
|
||||||
|
- **OIDC**: `OIDC_ENABLED=true` + the four `OIDC_*` values when wiring SSO.
|
||||||
|
- **S3**: `S3_BUCKET`, `S3_REGION`, optional `S3_ENDPOINT` for MinIO/compatibles, plus credentials.
|
||||||
|
|
||||||
|
Lock it down:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod 600 .env
|
||||||
|
chown buildfor_life_ops:buildfor_life_ops .env
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Create the database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo -iu postgres psql <<'SQL'
|
||||||
|
CREATE USER buildfor_life_ops WITH PASSWORD '<strong-password>';
|
||||||
|
CREATE DATABASE buildfor_life_ops OWNER buildfor_life_ops;
|
||||||
|
SQL
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Run migrations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm run db:migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
Re-run after every deploy — the migrator is idempotent and skips applied migrations.
|
||||||
|
|
||||||
|
## 8. Bootstrap the first admin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm run create-user -- \
|
||||||
|
--email admin@b4l.co.th \
|
||||||
|
--password '<strong-password>' \
|
||||||
|
--name 'Admin' \
|
||||||
|
--company 'B4L' \
|
||||||
|
--role admin
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. Build for production
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: `build/` — a standalone Node server bundle. The `node_modules` it needs at runtime are the production deps:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install --prod --frozen-lockfile
|
||||||
|
```
|
||||||
|
|
||||||
|
(Do this on the deploy host, not a cross-compiled one, so native modules like `@node-rs/argon2` and `sharp` pick the right binaries.)
|
||||||
|
|
||||||
|
## 10. systemd unit
|
||||||
|
|
||||||
|
`/etc/systemd/system/buildfor_life_ops.service`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=buildfor_life_ops (SvelteKit node adapter)
|
||||||
|
After=network.target postgresql.service
|
||||||
|
Wants=postgresql.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=buildfor_life_ops
|
||||||
|
Group=buildfor_life_ops
|
||||||
|
WorkingDirectory=/opt/buildfor_life_ops/app
|
||||||
|
EnvironmentFile=/opt/buildfor_life_ops/app/.env
|
||||||
|
Environment=NODE_ENV=production
|
||||||
|
Environment=HOST=127.0.0.1
|
||||||
|
Environment=PORT=3000
|
||||||
|
Environment=BODY_SIZE_LIMIT=10M
|
||||||
|
# fnm-installed Node — path resolved via the service user's shim dir.
|
||||||
|
# Pin the exact version here so a stray `fnm default` does not change the runtime.
|
||||||
|
ExecStart=/opt/buildfor_life_ops/.local/state/fnm_multishells/current/bin/node build/index.js
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
# Hardening
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ReadWritePaths=/opt/buildfor_life_ops/app/storage /var/lib/buildfor_life_ops
|
||||||
|
ProtectHome=true
|
||||||
|
PrivateTmp=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
If `fnm` multishell paths are awkward (they rotate per shell), use the canonical alias path instead:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
ExecStart=/opt/buildfor_life_ops/.local/share/fnm/aliases/default/bin/node build/index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable and start:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now buildfor_life_ops
|
||||||
|
systemctl status buildfor_life_ops
|
||||||
|
journalctl -u buildfor_life_ops -f
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. Reverse proxy (nginx)
|
||||||
|
|
||||||
|
`/etc/nginx/sites-available/buildfor_life_ops`:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name ops.b4l.co.th;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name ops.b4l.co.th;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/ops.b4l.co.th/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/ops.b4l.co.th/privkey.pem;
|
||||||
|
|
||||||
|
# Uploads: documents + CSV import. Keep in sync with BODY_SIZE_LIMIT in systemd unit.
|
||||||
|
client_max_body_size 10m;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SvelteKit trusts `X-Forwarded-*` when `ORIGIN` or `PROTOCOL_HEADER`/`HOST_HEADER` env vars are set. Recommended:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Add to the systemd unit [Service] block
|
||||||
|
Environment=ORIGIN=https://ops.b4l.co.th
|
||||||
|
Environment=PROTOCOL_HEADER=x-forwarded-proto
|
||||||
|
Environment=HOST_HEADER=x-forwarded-host
|
||||||
|
```
|
||||||
|
|
||||||
|
## 12. Upgrades
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/buildfor_life_ops/app
|
||||||
|
git fetch --tags
|
||||||
|
git checkout <tag-or-sha>
|
||||||
|
|
||||||
|
# Node version may have changed — fnm re-reads .node-version on cd, but force it:
|
||||||
|
fnm use --install-if-missing
|
||||||
|
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
pnpm run db:migrate
|
||||||
|
pnpm run build
|
||||||
|
pnpm install --prod --frozen-lockfile
|
||||||
|
|
||||||
|
systemctl restart buildfor_life_ops
|
||||||
|
journalctl -u buildfor_life_ops -n 100 --no-pager
|
||||||
|
```
|
||||||
|
|
||||||
|
A migration that cannot be rolled back forward-only (rare — see `drizzle/README.md`) needs a maintenance window and a DB snapshot first.
|
||||||
|
|
||||||
|
## 13. Rollback
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/buildfor_life_ops/app
|
||||||
|
git checkout <previous-tag>
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
pnpm run build
|
||||||
|
pnpm install --prod --frozen-lockfile
|
||||||
|
systemctl restart buildfor_life_ops
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schema rollback is manual.** Drizzle does not ship down-migrations. If the previous code cannot read the current schema, restore the DB from the pre-upgrade snapshot before checking out the old tag.
|
||||||
|
|
||||||
|
## 14. Backups
|
||||||
|
|
||||||
|
Two things matter:
|
||||||
|
|
||||||
|
- **Postgres** — `pg_dump -Fc buildfor_life_ops > ops-$(date +%F).dump`, daily, offsite. Retain ≥ 14 days.
|
||||||
|
- **Blob storage** — when `STORAGE_BACKEND=local`, `STORAGE_LOCAL_ROOT` holds all uploaded documents. Snapshot it with the filesystem (ZFS/btrfs) or rsync it alongside the DB dump. When `STORAGE_BACKEND=s3`, rely on bucket versioning + cross-region replication.
|
||||||
|
|
||||||
|
The DB is the source of truth for `documents.storage_key` → blob mapping. A blob directory without its matching DB rows is unusable.
|
||||||
|
|
||||||
|
## 15. Health check
|
||||||
|
|
||||||
|
There is no dedicated `/healthz` endpoint yet. For now, probe `GET /login` — it returns 200 without a session:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsS -o /dev/null -w '%{http_code}\n' https://ops.b4l.co.th/login
|
||||||
|
```
|
||||||
|
|
||||||
|
## 16. Observability
|
||||||
|
|
||||||
|
- **Logs**: `journalctl -u buildfor_life_ops`. All app logs go to stdout/stderr.
|
||||||
|
- **Metrics**: not wired yet. If/when added, expose on a separate localhost port so nginx does not proxy them publicly.
|
||||||
|
|
||||||
|
## 17. Common pitfalls
|
||||||
|
|
||||||
|
- **`Environment validation failed`** on boot — `.env` is missing, the `EnvironmentFile=` path is wrong, or one of the `min(32)` secrets is too short.
|
||||||
|
- **`sharp` fails with `could not load the "sharp" module`** — cross-compiled install. Re-run `pnpm install --prod --frozen-lockfile` on the deploy host.
|
||||||
|
- **`@node-rs/argon2` prebuilt binary missing** — same cause, same fix. If the host is exotic (musl, ARM), set `npm_config_build_from_source=true` before install.
|
||||||
|
- **Cookies not setting** — `PUBLIC_BASE_URL` must match the user-facing URL exactly (scheme + host). In production this means HTTPS; the session cookie is `Secure`.
|
||||||
|
- **413 on document upload** — bump both `client_max_body_size` in nginx and `BODY_SIZE_LIMIT` in the systemd unit; they must agree.
|
||||||
|
- **fnm picks the wrong Node after a server reboot** — ensure `fnm default 24` was run for the service user, and the systemd `ExecStart=` points at the aliases path, not a multishell path.
|
||||||
@@ -16,17 +16,33 @@ Siblings: [`buildfor_life_budget`](https://git.b4l.co.th/B4L/buildfor_life_budge
|
|||||||
|
|
||||||
### 1. Prerequisites
|
### 1. Prerequisites
|
||||||
|
|
||||||
- Node 20+ (24.x tested)
|
- **Node 24** pinned via `.node-version` (installed with [`fnm`](https://github.com/Schniz/fnm))
|
||||||
|
- **pnpm 9** as the package manager (declared in `package.json#packageManager`)
|
||||||
- PostgreSQL 16+ running locally or reachable by URL
|
- PostgreSQL 16+ running locally or reachable by URL
|
||||||
- `git` with SSH access to `gitssh.b4l.co.th`
|
- `git` with SSH access to `gitssh.b4l.co.th`
|
||||||
|
|
||||||
### 2. Install
|
### 2. Install fnm + pnpm
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
# fnm (one-time, per machine)
|
||||||
|
curl -fsSL https://fnm.vercel.app/install | bash # macOS / Linux
|
||||||
|
winget install Schniz.fnm # Windows (or: scoop install fnm)
|
||||||
|
|
||||||
|
# pnpm (one-time, via Corepack which ships with Node)
|
||||||
|
corepack enable
|
||||||
|
corepack prepare pnpm@9.15.0 --activate
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Configure environment
|
### 3. Activate the pinned Node version and install deps
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fnm use --install-if-missing
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
`fnm use` reads `.node-version` and drops you onto the correct Node. Add `eval "$(fnm env --use-on-cd)"` to your shell rc (bash/zsh) or the PowerShell equivalent so this happens automatically on `cd` into the repo.
|
||||||
|
|
||||||
|
### 4. Configure environment
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
@@ -42,22 +58,22 @@ Edit `.env`:
|
|||||||
- `STORAGE_LOCAL_ROOT` — where uploaded blobs live on disk (default `./storage`)
|
- `STORAGE_LOCAL_ROOT` — where uploaded blobs live on disk (default `./storage`)
|
||||||
- OIDC block — leave `OIDC_ENABLED=false` unless you're wiring SSO
|
- OIDC block — leave `OIDC_ENABLED=false` unless you're wiring SSO
|
||||||
|
|
||||||
### 4. Create the database
|
### 5. Create the database
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
createdb buildfor_life_ops # or use your tool of choice
|
createdb buildfor_life_ops # or use your tool of choice
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Apply migrations
|
### 6. Apply migrations
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run db:migrate
|
pnpm run db:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Bootstrap the first admin user
|
### 7. Bootstrap the first admin user
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run create-user -- \
|
pnpm run create-user -- \
|
||||||
--email you@b4l.co.th \
|
--email you@b4l.co.th \
|
||||||
--password 'a-long-password' \
|
--password 'a-long-password' \
|
||||||
--name 'Your Name' \
|
--name 'Your Name' \
|
||||||
@@ -65,29 +81,29 @@ npm run create-user -- \
|
|||||||
--role admin
|
--role admin
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7. Run the dev server
|
### 8. Run the dev server
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Open http://localhost:5173 and log in.
|
Open http://localhost:5173 and log in.
|
||||||
|
|
||||||
## npm scripts
|
## pnpm scripts
|
||||||
|
|
||||||
| Command | What it does |
|
| Command | What it does |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `npm run dev` | Start the Vite dev server with HMR |
|
| `pnpm dev` | Start the Vite dev server with HMR |
|
||||||
| `npm run build` | Production build (outputs to `build/`) |
|
| `pnpm build` | Production build (outputs to `build/`) |
|
||||||
| `npm run preview` | Preview the production build locally |
|
| `pnpm preview` | Preview the production build locally |
|
||||||
| `npm run check` | Typecheck: `svelte-kit sync` then `svelte-check --threshold warning` |
|
| `pnpm check` | Typecheck: `svelte-kit sync` then `svelte-check --threshold warning` |
|
||||||
| `npm run validate` | `check` + `build` — use this as a pre-commit smoke test |
|
| `pnpm validate` | `check` + `build` — use this as a pre-commit smoke test |
|
||||||
| `npm run db:generate` | Diff the Drizzle schema against the last snapshot and emit a new migration under `drizzle/` |
|
| `pnpm db:generate` | Diff the Drizzle schema against the last snapshot and emit a new migration under `drizzle/` |
|
||||||
| `npm run db:migrate` | Apply pending migrations against `$DATABASE_URL` |
|
| `pnpm db:migrate` | Apply pending migrations against `$DATABASE_URL` |
|
||||||
| `npm run db:push` | Skip migration files and sync the schema directly — **dev only** |
|
| `pnpm db:push` | Skip migration files and sync the schema directly — **dev only** |
|
||||||
| `npm run db:studio` | Open Drizzle Studio (web UI at localhost for inspecting data) |
|
| `pnpm db:studio` | Open Drizzle Studio (web UI at localhost for inspecting data) |
|
||||||
| `npm run db:seed` | Seed the system catalog of asset types (wired when the assets schema lands in Phase 1) |
|
| `pnpm db:seed` | Seed the system catalog of asset types |
|
||||||
| `npm run create-user -- --email ... --password ... --name ... [--company ...] [--role ...]` | Create or update a user; optionally attach them to a company with a role |
|
| `pnpm run create-user -- --email ... --password ... --name ... [--company ...] [--role ...]` | Create or update a user; optionally attach them to a company with a role |
|
||||||
|
|
||||||
## Project layout
|
## Project layout
|
||||||
|
|
||||||
@@ -181,6 +197,6 @@ When in doubt, check the siblings before inventing:
|
|||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
- **`Environment validation failed`** on boot — `.env` is missing or incomplete; compare against `.env.example`
|
- **`Environment validation failed`** on boot — `.env` is missing or incomplete; compare against `.env.example`
|
||||||
- **`relation "sessions" does not exist`** — you skipped `npm run db:migrate` after `db:generate`
|
- **`relation "sessions" does not exist`** — you skipped `pnpm run db:migrate` after `db:generate`
|
||||||
- **Session cookie never sets** — in dev, make sure `PUBLIC_BASE_URL` includes `localhost` so the hook sets `secure: false`; in production use HTTPS
|
- **Session cookie never sets** — in dev, make sure `PUBLIC_BASE_URL` includes `localhost` so the hook sets `secure: false`; in production use HTTPS
|
||||||
- **`sha256 mismatch on upload`** — the client-computed sha256 disagrees with the server's stream hash; retry the upload
|
- **`sha256 mismatch on upload`** — the client-computed sha256 disagrees with the server's stream hash; retry the upload
|
||||||
|
|||||||
+4
-4
@@ -5,10 +5,10 @@ TypeScript schema in `src/lib/server/db/schema/`.
|
|||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
|
|
||||||
- `npm run db:generate` — diff the schema vs. the latest snapshot and emit a new `.sql` file
|
- `pnpm run db:generate` — diff the schema vs. the latest snapshot and emit a new `.sql` file
|
||||||
- `npm run db:migrate` — apply pending migrations to `DATABASE_URL`
|
- `pnpm run db:migrate` — apply pending migrations to `DATABASE_URL`
|
||||||
- `npm run db:push` — skip migrations and sync the schema directly (**dev only**)
|
- `pnpm run db:push` — skip migrations and sync the schema directly (**dev only**)
|
||||||
- `npm run db:studio` — open the Drizzle Studio UI
|
- `pnpm run db:studio` — open the Drizzle Studio UI
|
||||||
|
|
||||||
After every generate, review the SQL for surprises — especially around:
|
After every generate, review the SQL for surprises — especially around:
|
||||||
|
|
||||||
|
|||||||
Generated
-7295
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -3,12 +3,13 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"packageManager": "pnpm@9.15.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --threshold warning",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --threshold warning",
|
||||||
"validate": "npm run check && npm run build",
|
"validate": "pnpm run check && pnpm run build",
|
||||||
"db:generate": "drizzle-kit generate",
|
"db:generate": "drizzle-kit generate",
|
||||||
"db:migrate": "drizzle-kit migrate",
|
"db:migrate": "drizzle-kit migrate",
|
||||||
"db:push": "drizzle-kit push",
|
"db:push": "drizzle-kit push",
|
||||||
|
|||||||
Generated
+4707
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@ async function main() {
|
|||||||
const role = (readArg('--role', 'admin') ?? 'admin') as 'admin' | 'manager' | 'user' | 'viewer';
|
const role = (readArg('--role', 'admin') ?? 'admin') as 'admin' | 'manager' | 'user' | 'viewer';
|
||||||
|
|
||||||
if (!email || !password || !name) {
|
if (!email || !password || !name) {
|
||||||
console.error('Usage: npm run create-user -- --email <e> --password <p> --name <n> [--company <c>] [--role admin|manager|user|viewer]');
|
console.error('Usage: pnpm run create-user -- --email <e> --password <p> --name <n> [--company <c>] [--role admin|manager|user|viewer]');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user