@pplethai/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -0
- package/askpass/pple-askpass.mjs +18 -0
- package/dist/api.js +62 -0
- package/dist/bin.js +16 -0
- package/dist/cli.js +103 -0
- package/dist/commands/auth.js +32 -0
- package/dist/commands/deploy.js +106 -0
- package/dist/commands/init.js +77 -0
- package/dist/commands/upgrade.js +29 -0
- package/dist/credentials.js +53 -0
- package/dist/deps.js +87 -0
- package/dist/device-flow.js +115 -0
- package/dist/errors.js +16 -0
- package/dist/git.js +127 -0
- package/dist/link.js +49 -0
- package/dist/node-deps.js +84 -0
- package/dist/npm.js +127 -0
- package/dist/scaffold.js +58 -0
- package/dist/session.js +29 -0
- package/dist/skills.js +86 -0
- package/dist/ui.js +12 -0
- package/dist/update.js +142 -0
- package/package.json +35 -0
- package/skills/pple-platform/SKILL.md +102 -0
- package/skills/pple-platform/evals/evals.json +29 -0
- package/skills/pple-platform/references/deployment.md +61 -0
- package/skills/pple-platform/references/design-system.md +50 -0
- package/skills/pple-platform/references/mini-app.md +74 -0
- package/skills/pple-platform/references/pple-id-auth.md +85 -0
- package/skills/pple-platform/references/project-scaffold.md +75 -0
- package/templates/golden-path/.dev.vars.example +17 -0
- package/templates/golden-path/.env.example +8 -0
- package/templates/golden-path/.github/workflows/ci.yml +22 -0
- package/templates/golden-path/CLAUDE.md +60 -0
- package/templates/golden-path/README.md +60 -0
- package/templates/golden-path/gitignore +6 -0
- package/templates/golden-path/index.html +16 -0
- package/templates/golden-path/migrations/0001_init.sql +9 -0
- package/templates/golden-path/package-lock.json +4283 -0
- package/templates/golden-path/package.json +38 -0
- package/templates/golden-path/shared/api.ts +56 -0
- package/templates/golden-path/src/auth.ts +53 -0
- package/templates/golden-path/src/db.ts +32 -0
- package/templates/golden-path/src/env.ts +19 -0
- package/templates/golden-path/src/files.ts +38 -0
- package/templates/golden-path/src/index.ts +104 -0
- package/templates/golden-path/src/notify.ts +64 -0
- package/templates/golden-path/test/apply-migrations.ts +4 -0
- package/templates/golden-path/test/env.d.ts +17 -0
- package/templates/golden-path/test/harness.ts +57 -0
- package/templates/golden-path/test/smoke.test.ts +155 -0
- package/templates/golden-path/tsconfig.json +20 -0
- package/templates/golden-path/tsconfig.worker.json +21 -0
- package/templates/golden-path/vite.config.ts +29 -0
- package/templates/golden-path/vitest.config.ts +26 -0
- package/templates/golden-path/web/App.tsx +250 -0
- package/templates/golden-path/web/NotInApp.tsx +23 -0
- package/templates/golden-path/web/app.css +112 -0
- package/templates/golden-path/web/main.tsx +40 -0
- package/templates/golden-path/web/pple.ts +73 -0
- package/templates/golden-path/web/vite-env.d.ts +10 -0
- package/templates/golden-path/wrangler.jsonc +53 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# PPLE ID authentication (Builder Apps)
|
|
2
|
+
|
|
3
|
+
PPLE ID is the party's OIDC identity provider. For a **mini-app**, the flow is:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
PPLE Today ──opens mini-app in WebView──▶ web (React)
|
|
7
|
+
web: PPLEMiniApp.init() → getAccessToken() ── bearer token
|
|
8
|
+
web ──Authorization: Bearer <token>──▶ Worker
|
|
9
|
+
Worker ──GET {SSO_BASE_URL}/oidc/v1/userinfo──▶ PPLE ID
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The Worker never sees a password and holds no session; the bearer token is
|
|
13
|
+
the credential on every request (`src/auth.ts`).
|
|
14
|
+
|
|
15
|
+
Tokens also exist outside mini-apps for **Builders** (Console browser login,
|
|
16
|
+
`pple auth login` device flow). That does not change end-user auth for the
|
|
17
|
+
app you are building: do not invent a browser sign-in for the mini-app itself.
|
|
18
|
+
|
|
19
|
+
## Three ways the mini-app can start
|
|
20
|
+
|
|
21
|
+
Canonical wiring: `web/pple.ts` + `web/main.tsx` + `web/NotInApp.tsx` in the
|
|
22
|
+
golden-path template.
|
|
23
|
+
|
|
24
|
+
1. **Local `vite dev`** — set `token` to the `DEV_AUTH` value from `.dev.vars`
|
|
25
|
+
and treat the session as signed-in. No PPLE Today required.
|
|
26
|
+
2. **Inside PPLE Today** — detect the mini-app user agent, init
|
|
27
|
+
`@pplethai/pple-today-miniapp-sdk`, capture the access token from the
|
|
28
|
+
launch URL, attach it on every `api()` call.
|
|
29
|
+
3. **Plain browser (production)** — show the friendly NOT_IN_APP screen
|
|
30
|
+
("เปิดในแอป PPLE Today"). There is no browser login for mini-apps.
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { isInMiniAppUserAgent } from "@pplethai/components";
|
|
34
|
+
import { PPLEMiniApp } from "@pplethai/pple-today-miniapp-sdk";
|
|
35
|
+
|
|
36
|
+
// Local: import.meta.env.DEV → DEV_AUTH token ("dev-token" by default)
|
|
37
|
+
// Plain browser: !isInMiniAppUserAgent → { kind: "not-in-app" }
|
|
38
|
+
// Inside Today: sdk.init() → getAccessToken() → { kind: "signed-in" }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
OAuth client values (`VITE_OAUTH_URL`, `VITE_OAUTH_CLIENT_ID`,
|
|
42
|
+
`VITE_OAUTH_REDIRECT_URI`) are provisioned per app and injected at build
|
|
43
|
+
time — Builders do not register clients by hand.
|
|
44
|
+
|
|
45
|
+
## Worker: validating the token
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// src/auth.ts — pattern from the template
|
|
49
|
+
if (env.DEV_AUTH && token === env.DEV_AUTH) {
|
|
50
|
+
return { ok: true, identity: { sub: "dev-user", name: "Local Dev" } };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const res = await fetch(`${env.SSO_BASE_URL}/oidc/v1/userinfo`, {
|
|
54
|
+
headers: { authorization: `Bearer ${token}` },
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- Missing / invalid token → 401.
|
|
59
|
+
- PPLE ID unreachable / malformed profile → 502 (so the client can tell
|
|
60
|
+
"sign in again" from "try again later").
|
|
61
|
+
- Attach `{ sub, name }` to the request context; keep route handlers thin.
|
|
62
|
+
|
|
63
|
+
## Local DEV_AUTH only
|
|
64
|
+
|
|
65
|
+
```env
|
|
66
|
+
# .dev.vars (gitignored) — copy from .dev.vars.example
|
|
67
|
+
DEV_AUTH="dev-token"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Rules:
|
|
71
|
+
|
|
72
|
+
- **Never** set `DEV_AUTH` in production. The platform never provisions it;
|
|
73
|
+
absence means real auth only.
|
|
74
|
+
- The React app's local token must match `DEV_AUTH` exactly (template default:
|
|
75
|
+
`dev-token`).
|
|
76
|
+
- Do not resurrect header-based impersonation (`x-dev-phone`,
|
|
77
|
+
`ALLOW_DEV_AUTH`) — that was the Express-era bypass.
|
|
78
|
+
|
|
79
|
+
## What not to tell the Builder
|
|
80
|
+
|
|
81
|
+
- Do not claim "tokens only exist inside PPLE Today" as a platform-wide fact
|
|
82
|
+
(Console and CLI also hold Builder tokens).
|
|
83
|
+
- Do not add a custom OAuth redirect login page to the mini-app "so it works
|
|
84
|
+
in Chrome."
|
|
85
|
+
- Do not ask for GitHub or Cloudflare credentials to "get a token."
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Project scaffold & local development
|
|
2
|
+
|
|
3
|
+
New Builder Apps start from the golden-path template in
|
|
4
|
+
`pple-platform/template`, copied by `pple init <slug>` after the Console
|
|
5
|
+
creates the app. Do not scaffold Express/Prisma monorepos or copy
|
|
6
|
+
`pple-erp` — that layout is the old path.
|
|
7
|
+
|
|
8
|
+
## Layout (single package)
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
my-app/ # folder name matches the App Slug
|
|
12
|
+
├── package.json # scripts: dev, build, test, deploy, db:migrate:*
|
|
13
|
+
├── wrangler.jsonc # Worker + D1 + R2 bindings (platform-owned names)
|
|
14
|
+
├── .dev.vars.example # copy to .dev.vars for local DEV_AUTH
|
|
15
|
+
├── .pple/app.json # App Link — written by pple init; do not invent
|
|
16
|
+
├── src/ # Worker (API) — Hono routes under /api/*
|
|
17
|
+
│ ├── index.ts # thin route handlers
|
|
18
|
+
│ ├── auth.ts # PPLE ID userinfo + DEV_AUTH bypass
|
|
19
|
+
│ ├── db.ts # D1 helpers
|
|
20
|
+
│ ├── files.ts # R2 helpers
|
|
21
|
+
│ ├── notify.ts # audience-bound notifications
|
|
22
|
+
│ └── env.ts # Env bindings type
|
|
23
|
+
├── web/ # React SPA (Vite)
|
|
24
|
+
│ ├── main.tsx # bootstrap → App | NotInApp | error
|
|
25
|
+
│ ├── pple.ts # SDK / DEV_AUTH / api() helpers
|
|
26
|
+
│ ├── App.tsx
|
|
27
|
+
│ ├── NotInApp.tsx
|
|
28
|
+
│ └── app.css # mobile-first layout only
|
|
29
|
+
├── shared/ # Zod schemas + types used by both sides
|
|
30
|
+
│ └── api.ts
|
|
31
|
+
├── migrations/ # D1 SQL migrations (0001_*.sql, …)
|
|
32
|
+
└── test/ # smoke tests in the Workers runtime
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**`shared/api.ts` is the contract** between Worker and SPA. Change a shape
|
|
36
|
+
there once; both sides import the Zod schema / inferred type.
|
|
37
|
+
|
|
38
|
+
## Conventions
|
|
39
|
+
|
|
40
|
+
- **One origin.** The Worker serves `/api/*` and the SPA for everything else.
|
|
41
|
+
Frontend calls use relative paths (`/api/notes`) via `api()` / `apiJson()`
|
|
42
|
+
in `web/pple.ts`.
|
|
43
|
+
- **Keep handlers thin.** Put logic in `auth` / `db` / `files` / `notify`.
|
|
44
|
+
- **Validate with Zod** from `shared/api.ts`.
|
|
45
|
+
- **Bind D1 parameters** (`?` + `.bind(...)`) — never string-concatenate SQL.
|
|
46
|
+
- **Add a table** with a new `migrations/000N_*.sql` (never edit an applied
|
|
47
|
+
migration), then `npm run db:migrate:local`.
|
|
48
|
+
- **Mobile-first.** The app runs in a phone-sized WebView.
|
|
49
|
+
- **User-facing strings are Thai** where the template already is; code and
|
|
50
|
+
comments stay English.
|
|
51
|
+
|
|
52
|
+
## Local development
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# After Console create-app + pple auth login + pple init <slug>:
|
|
56
|
+
cd <slug>
|
|
57
|
+
npm install
|
|
58
|
+
cp .dev.vars.example .dev.vars # enables DEV_AUTH for plain-browser work
|
|
59
|
+
npm run db:migrate:local
|
|
60
|
+
npm run dev # http://localhost:5173 — SPA + Worker
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Everyday commands:
|
|
64
|
+
|
|
65
|
+
| Command | Does |
|
|
66
|
+
| ------- | ---- |
|
|
67
|
+
| `npm run dev` | Full-stack Vite + Worker with hot reload |
|
|
68
|
+
| `npm run typecheck` | Type-check app and Worker |
|
|
69
|
+
| `npm test` | Smoke tests in the Workers runtime |
|
|
70
|
+
| `npm run build` | Deployable bundle |
|
|
71
|
+
| `npm run db:migrate:local` | Apply D1 migrations locally |
|
|
72
|
+
| `pple deploy` | Ship via the platform CLI |
|
|
73
|
+
|
|
74
|
+
In local `vite dev`, `web/pple.ts` uses the `DEV_AUTH` token automatically so
|
|
75
|
+
you never need PPLE Today open. See [pple-id-auth.md](pple-id-auth.md).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Local development configuration. Copy this file to `.dev.vars` (which is
|
|
2
|
+
# gitignored) and adjust. `wrangler dev` and `vite dev` load `.dev.vars`
|
|
3
|
+
# automatically.
|
|
4
|
+
|
|
5
|
+
# Turns on the local auth bypass. When set, any request whose bearer token
|
|
6
|
+
# equals this value is accepted as a fake signed-in user, so you can develop
|
|
7
|
+
# in a plain browser without PPLE Today. The React app uses this token
|
|
8
|
+
# automatically during `vite dev`.
|
|
9
|
+
#
|
|
10
|
+
# NEVER set DEV_AUTH in production — the platform never provisions it, and the
|
|
11
|
+
# Worker treats its absence as "real auth only".
|
|
12
|
+
DEV_AUTH="dev-token"
|
|
13
|
+
|
|
14
|
+
# Optional: exercise the notification helper locally. These are normally
|
|
15
|
+
# injected by the platform at deploy time.
|
|
16
|
+
# NOTIFY_API_BASE="https://today.peoplesparty.or.th"
|
|
17
|
+
# NOTIFY_API_KEY="your-app-notification-key"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Frontend build-time config (Vite exposes VITE_* to the browser bundle).
|
|
2
|
+
# These are the app's PPLE ID OIDC client details, used by the mini-app SDK
|
|
3
|
+
# when running inside PPLE Today. They are public values, safe to ship.
|
|
4
|
+
# The platform injects them at deploy time; copy to `.env` to set locally.
|
|
5
|
+
VITE_OAUTH_URL="https://sso.peoplesparty.or.th"
|
|
6
|
+
VITE_OAUTH_CLIENT_ID=""
|
|
7
|
+
# Defaults to the app's own origin if unset.
|
|
8
|
+
# VITE_OAUTH_REDIRECT_URI=""
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# CI for a PPLE Platform Builder App. Runs on every push and pull request:
|
|
2
|
+
# type-checks the Worker and the React app, runs the smoke tests in the
|
|
3
|
+
# Workers runtime, and builds the deployable bundle.
|
|
4
|
+
name: CI
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
check:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 22
|
|
18
|
+
cache: npm
|
|
19
|
+
- run: npm ci
|
|
20
|
+
- run: npm run typecheck
|
|
21
|
+
- run: npm test
|
|
22
|
+
- run: npm run build
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Working in this repo
|
|
2
|
+
|
|
3
|
+
This is a **PPLE Platform Builder App** — a mobile-first mini-app that runs
|
|
4
|
+
inside PPLE Today. The person you're helping is a party workforce member who
|
|
5
|
+
may not read stack traces; keep changes simple, explain them plainly, and
|
|
6
|
+
prefer editing the existing demo cards over introducing new patterns.
|
|
7
|
+
|
|
8
|
+
## Architecture (read once)
|
|
9
|
+
|
|
10
|
+
One Cloudflare Worker serves everything:
|
|
11
|
+
|
|
12
|
+
- **`src/`** — the Worker (API). Hono, TypeScript, Workers runtime. Routes
|
|
13
|
+
live under `/api/*` in `src/index.ts`; keep handlers thin and put logic in
|
|
14
|
+
the helper modules (`auth`, `db`, `files`, `notify`).
|
|
15
|
+
- **`web/`** — the React app (browser). Built by Vite, served for every
|
|
16
|
+
non-`/api` path. Uses the `@pplethai/components` design system.
|
|
17
|
+
- **`shared/`** — types shared by both sides (Zod schemas in `shared/api.ts`).
|
|
18
|
+
When you change an API shape, change it here and both sides update.
|
|
19
|
+
- **`migrations/`** — D1 (SQLite) schema, applied in order.
|
|
20
|
+
|
|
21
|
+
The frontend calls the backend with `api()` / `apiJson()` from `web/pple.ts`,
|
|
22
|
+
which attach the PPLE ID bearer token. Same origin, so paths are relative
|
|
23
|
+
(`/api/notes`).
|
|
24
|
+
|
|
25
|
+
## How auth works
|
|
26
|
+
|
|
27
|
+
- Inside PPLE Today, the mini-app SDK (`web/pple.ts`) captures the access
|
|
28
|
+
token from the launch URL. The Worker validates it against PPLE ID
|
|
29
|
+
(`src/auth.ts`).
|
|
30
|
+
- In a plain browser, the app shows the NOT_IN_APP screen — there is no
|
|
31
|
+
browser sign-in for mini-apps.
|
|
32
|
+
- Locally, `DEV_AUTH` in `.dev.vars` bypasses PPLE Today. **Never** set
|
|
33
|
+
`DEV_AUTH` in production; the platform never provisions it.
|
|
34
|
+
|
|
35
|
+
## Conventions
|
|
36
|
+
|
|
37
|
+
- **TypeScript is strict.** Run `npm run typecheck` after changes.
|
|
38
|
+
- **Validate input with the Zod schemas** in `shared/api.ts`.
|
|
39
|
+
- **Always bind D1 query parameters** (`?` + `.bind(...)`) — never build SQL
|
|
40
|
+
with string concatenation.
|
|
41
|
+
- **Notifications are content only** (title/body), plus an optional path-only
|
|
42
|
+
`linkPath` into this app. Never send phone numbers or user ids; the platform
|
|
43
|
+
resolves the audience. See `src/notify.ts`.
|
|
44
|
+
- **Mobile-first.** The app runs in a phone-sized WebView. Use the design
|
|
45
|
+
system's components and the layout classes in `web/app.css`.
|
|
46
|
+
- **Add a DB table** by writing a new `migrations/000N_*.sql` (never edit an
|
|
47
|
+
applied migration) and running `npm run db:migrate:local`.
|
|
48
|
+
|
|
49
|
+
## Before you finish
|
|
50
|
+
|
|
51
|
+
Run these and make sure they pass:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm run typecheck
|
|
55
|
+
npm test
|
|
56
|
+
npm run build
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`npm test` runs the smoke tests inside the real Workers runtime. If you add a
|
|
60
|
+
capability, add a smoke test for it in `test/`.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# PPLE Platform — Golden-path template
|
|
2
|
+
|
|
3
|
+
The starting point for a **Builder App** on the PPLE Platform. It's a
|
|
4
|
+
mobile-first mini-app that runs inside PPLE Today, built on the platform's
|
|
5
|
+
managed Cloudflare stack, wired to the People's Party identity, data, file,
|
|
6
|
+
and notification services out of the box.
|
|
7
|
+
|
|
8
|
+
You don't need to understand Cloudflare, OAuth, or SQL to use it — describe
|
|
9
|
+
what you want to your AI coding agent and edit the demo cards. See
|
|
10
|
+
[CLAUDE.md](CLAUDE.md) for how the agent should work in this repo.
|
|
11
|
+
|
|
12
|
+
## What's inside
|
|
13
|
+
|
|
14
|
+
| Capability | Where | Demo |
|
|
15
|
+
| ---------- | ----- | ---- |
|
|
16
|
+
| **API** | Hono Worker, `src/index.ts` | routes under `/api/*` |
|
|
17
|
+
| **Identity** | `src/auth.ts` + `web/pple.ts` | validates PPLE ID tokens; NOT_IN_APP screen; dev bypass |
|
|
18
|
+
| **Database** | D1, `src/db.ts` + `migrations/` | the Notes card |
|
|
19
|
+
| **Files** | R2, `src/files.ts` | the Files card |
|
|
20
|
+
| **Notifications** | `src/notify.ts` | the Notify card (audience-bound; optional `linkPath`) |
|
|
21
|
+
| **Design system** | `@pplethai/components` | every screen |
|
|
22
|
+
|
|
23
|
+
One Worker serves the API under `/api/*` and the React app (`web/`) for
|
|
24
|
+
everything else — same origin, no CORS.
|
|
25
|
+
|
|
26
|
+
## Getting started
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install
|
|
30
|
+
cp .dev.vars.example .dev.vars # local auth bypass
|
|
31
|
+
npm run db:migrate:local # set up the local database
|
|
32
|
+
npm run dev # http://localhost:5173
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
In local dev you're signed in automatically as a fake user (the `DEV_AUTH`
|
|
36
|
+
bypass), so you can build without PPLE Today. Opened in a plain browser after
|
|
37
|
+
deploy, the app shows a friendly "open in PPLE Today" screen; opened inside
|
|
38
|
+
PPLE Today, it signs the real user in via the mini-app SDK.
|
|
39
|
+
|
|
40
|
+
## Everyday commands
|
|
41
|
+
|
|
42
|
+
| Command | Does |
|
|
43
|
+
| ------- | ---- |
|
|
44
|
+
| `npm run dev` | Full-stack dev server (React + Worker) with hot reload |
|
|
45
|
+
| `npm run typecheck` | Type-check the app and the Worker |
|
|
46
|
+
| `npm test` | Run the smoke tests in the Workers runtime |
|
|
47
|
+
| `npm run build` | Build the deployable bundle |
|
|
48
|
+
| `npm run db:migrate:local` | Apply migrations to the local database |
|
|
49
|
+
| `pple deploy` | Ship it (the platform CLI; wraps `npm run deploy`) |
|
|
50
|
+
|
|
51
|
+
## Making it yours
|
|
52
|
+
|
|
53
|
+
- **Add a table:** create `migrations/0002_*.sql`, run `npm run db:migrate:local`,
|
|
54
|
+
add queries in `src/db.ts`.
|
|
55
|
+
- **Add an API route:** add it under `/api` in `src/index.ts`.
|
|
56
|
+
- **Change the UI:** edit `web/App.tsx` using `@pplethai/components`.
|
|
57
|
+
- **Send notifications:** the platform provisions `NOTIFY_API_KEY`; the helper
|
|
58
|
+
in `src/notify.ts` already speaks the audience-bound contract.
|
|
59
|
+
|
|
60
|
+
The demo cards (Notes / Files / Notify) are examples — replace them.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="th">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<!-- Mobile-first: fit the notch/safe areas of the PPLE Today WebView. -->
|
|
6
|
+
<meta
|
|
7
|
+
name="viewport"
|
|
8
|
+
content="width=device-width, initial-scale=1, viewport-fit=cover"
|
|
9
|
+
/>
|
|
10
|
+
<title>PPLE App</title>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div id="root"></div>
|
|
14
|
+
<script type="module" src="/web/main.tsx"></script>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- The example table behind the D1 read/write demo. Replace it with your own
|
|
2
|
+
-- schema: add a new file (0002_add_something.sql, …) and apply it with
|
|
3
|
+
-- `npm run db:migrate:local` (local) or `npm run db:migrate:remote` (deployed).
|
|
4
|
+
CREATE TABLE notes (
|
|
5
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
|
+
body TEXT NOT NULL,
|
|
7
|
+
-- ISO-8601 UTC (…T…Z) so the timestamp is unambiguous everywhere it's read.
|
|
8
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
|
|
9
|
+
);
|