ugly-app 0.1.521 → 0.1.523
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 +43 -17
- package/dist/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
package/README.md
CHANGED
|
@@ -236,7 +236,7 @@ export type AppPages = typeof pages;
|
|
|
236
236
|
|
|
237
237
|
### `bootstrapApp()`
|
|
238
238
|
|
|
239
|
-
The recommended entrypoint. Handles auth detection, socket creation,
|
|
239
|
+
The recommended entrypoint. Handles auth detection, socket creation, silent auto-login through the ugly.bot iframe (Mode A only), and provider wiring.
|
|
240
240
|
|
|
241
241
|
```tsx
|
|
242
242
|
// client/main.tsx
|
|
@@ -272,7 +272,9 @@ bootstrapApp({
|
|
|
272
272
|
| `strings?` | Localization config — when present, wraps the tree with `<StringsProvider>`. |
|
|
273
273
|
| `keyboard?: false` | Disable the framework `<KeyboardProvider>` wrapper. |
|
|
274
274
|
|
|
275
|
-
`bootstrapApp` reads `window.__AUTH_TOKEN__` (injected by the server). If absent, it renders unauthenticated and lets the router
|
|
275
|
+
`bootstrapApp` reads `window.__AUTH_TOKEN__` (injected by the server). If absent, it renders unauthenticated and lets the router's per-route auth guard surface the framework's `<AuthRoot>` (Mode A → `<LoginPopup>`; Mode B → magic-link form + optional Google button). If the token is present, it connects the socket, mounts `<AppProvider>`, and renders.
|
|
276
|
+
|
|
277
|
+
If `bootstrapApp` is loaded at `/auth/magic-link/verify` (Mode B), it renders the `<MagicLinkCallback>` component instead of the regular app shell — that path is the fallback target when the server route is shadowed by a static-assets handler.
|
|
276
278
|
|
|
277
279
|
### Routing — `createRouter()`
|
|
278
280
|
|
|
@@ -290,8 +292,8 @@ export const { RouterProvider, RouterView, useRouter } = createRouter({
|
|
|
290
292
|
|
|
291
293
|
`createRouter` returns:
|
|
292
294
|
|
|
293
|
-
- **`RouterProvider`** — props: `children`, `fallback?`, `
|
|
294
|
-
- **`RouterView`** — renders the active page with animated transitions. Props: `durationMs?`, `easing?`, `transitionComponent?` (replaces `ViewFlipper`), `renderPage?` (sync alternative to `allPages` loaders).
|
|
295
|
+
- **`RouterProvider`** — props: `children`, `fallback?`, `isAuthenticated?`. Manages route state, browser history, popups, and the silent auto-login iframe (`<AutoLoginGate>` — only in Mode A; skipped when `window.__UGLY_APP_AUTH_MODE__ === 'self'`). When `isAuthenticated()` returns false and the current route declares `auth: true`, the framework's `<AuthRoot>` is rendered in place of the page — apps cannot override this fallback.
|
|
296
|
+
- **`RouterView`** — renders the active page with animated transitions. Props: `durationMs?`, `easing?`, `transitionComponent?` (replaces `ViewFlipper`), `renderPage?` (sync alternative to `allPages` loaders, called with `RouterStateRaw`).
|
|
295
297
|
- **`useRouter()`** — returns the router context (see below).
|
|
296
298
|
|
|
297
299
|
### Page map — `lazyPage` / `lazyPageLoader`
|
|
@@ -378,13 +380,13 @@ const {
|
|
|
378
380
|
showPopup, // legacy popup API (prefer useRouter().openPopup)
|
|
379
381
|
hidePopup,
|
|
380
382
|
hideAllPopups,
|
|
381
|
-
runAsync, // runAsync(
|
|
383
|
+
runAsync, // runAsync(label, async () => { ... }, options?) — shows loading overlay while pending
|
|
382
384
|
splashDone, // mark a splash-screen step complete
|
|
383
385
|
localizer, // (key, params?) => string — alias for useLocalizer
|
|
384
386
|
} = useApp();
|
|
385
387
|
```
|
|
386
388
|
|
|
387
|
-
`useAppOptional()` returns `null` outside the provider; `useLocalizer()` returns a localizer that falls back to identity.
|
|
389
|
+
`useApp` is generic in `TAsyncOptions` so apps can pass an option type through to a custom `loadingOverlay` element. `useAppOptional()` returns `null` outside the provider; `useLocalizer()` returns a localizer that prefers `<StringsProvider>` data, falls back to the `AppProvider` localizer prop, and finally to identity.
|
|
388
390
|
|
|
389
391
|
### `Link` component
|
|
390
392
|
|
|
@@ -419,35 +421,59 @@ For pure HTTP (no WebSocket), use `createHttpClient({ requests, token?, baseUrl?
|
|
|
419
421
|
|
|
420
422
|
ugly-app uses HttpOnly cookies and server-side JWT injection — no `localStorage`, no client-side token handling.
|
|
421
423
|
|
|
422
|
-
|
|
424
|
+
Two modes, picked from the `auth` block in the project's `.uglyapp` config:
|
|
425
|
+
|
|
426
|
+
- **Mode A — `mode: 'uglybot'`** (default when no `auth` block is present). Auth is delegated to ugly.bot OAuth. AI / email / push proxies bill the end user's ugly.bot credits.
|
|
427
|
+
- **Mode B — `mode: 'self'`**. The app issues its own sessions via magic-link email and/or Google OAuth. AI / email / push proxies bill the developer's ugly.bot account using the project's `AI_PROXY_TOKEN`.
|
|
428
|
+
|
|
429
|
+
`window.__UGLY_APP_AUTH_MODE__` is injected into every page so client code (incl. `<AuthRoot>` and the router's `<AutoLoginGate>`) can branch correctly.
|
|
430
|
+
|
|
431
|
+
### Mode A — ugly.bot OAuth (default)
|
|
432
|
+
|
|
433
|
+
1. Unauthenticated user lands on a page. The router's `<AutoLoginGate>` opens a hidden iframe to `${UGLY_BOT_URL}/iframe-auth` to check for an existing platform session (4 s timeout).
|
|
434
|
+
2. If found, the iframe `postMessage`s an OAuth code back. The client POSTs to `/auth/verify`; the server exchanges it through `uglyBotAuthProvider`, sets the `auth_token` HttpOnly cookie, and reloads.
|
|
435
|
+
3. If not found (or timeout), per-route auth checks surface the framework's `<AuthRoot>` → `<LoginPopup>`, which opens `${UGLY_BOT_URL}/oauth` in a popup window.
|
|
436
|
+
4. On every authenticated page request, the server verifies the cookie token by calling `${UGLY_BOT_URL}/verify`, then injects `window.__AUTH_TOKEN__` into the HTML so the client can attach it to the WebSocket handshake.
|
|
423
437
|
|
|
424
|
-
|
|
425
|
-
2. If found, the iframe posts an OAuth code back; the client POSTs to `/auth/verify`; the server exchanges it through `uglyBotAuthProvider`, sets the `auth_token` HttpOnly cookie, and the page reloads authenticated.
|
|
426
|
-
3. If not found (or timeout after 4 s), `<RouterProvider>` shows `loginFallback` for auth-required routes. The default fallback is `<LoginPopup>`, which opens `https://ugly.bot/oauth` in a popup window.
|
|
427
|
-
4. On every authenticated request, the server verifies the cookie token by calling `${UGLY_BOT_URL}/verify` and injects `window.__AUTH_TOKEN__` into the HTML so the client can pass it on the WebSocket handshake.
|
|
438
|
+
### Mode B — Self-issued sessions
|
|
428
439
|
|
|
429
|
-
|
|
440
|
+
When `.uglyapp` sets `auth.mode: 'self'`, `buildAuth()` wires the magic-link provider as primary; if `providers.google.clientId` is configured it adds Google as an extra provider on the same router.
|
|
430
441
|
|
|
431
|
-
|
|
442
|
+
- `<AuthRoot>` renders `<MagicLinkForm>` (plus the Google button when configured) instead of `<LoginPopup>`.
|
|
443
|
+
- The `<AutoLoginGate>` is skipped entirely — the ugly.bot iframe would either silently fail or set a cookie that won't verify against the local session.
|
|
444
|
+
- `POST /auth/magic-link/request` accepts an email, mints a one-time token (default 15 min lifetime, configurable via `auth.magicLinkExpiresMin`), and emails a link.
|
|
445
|
+
- `GET /auth/magic-link/verify?token=…` validates the token, calls `onUserCreate` on first login, sets the session cookie, and redirects.
|
|
446
|
+
- Google login lives at `GET /auth/google/url` + `POST /auth/google/callback` when enabled.
|
|
447
|
+
|
|
448
|
+
### Token-in-URL embed
|
|
449
|
+
|
|
450
|
+
Any GET request with `?token=<JWT>` will, if the token verifies, set the cookie and 302-redirect to the same URL without the token parameter — useful for embedding any page in an iframe.
|
|
451
|
+
|
|
452
|
+
### Built-in routes
|
|
453
|
+
|
|
454
|
+
Mounted on every app:
|
|
432
455
|
|
|
433
456
|
| Endpoint | Description |
|
|
434
457
|
|----------|-------------|
|
|
435
458
|
| `POST /auth/verify` | Exchange an OAuth `code` for a session cookie. |
|
|
436
459
|
| `POST /auth/logout` | Clear the cookie. |
|
|
437
|
-
| `GET /auth/token` | Refresh and return the current token
|
|
460
|
+
| `GET /auth/token` | Refresh and return the current token. |
|
|
438
461
|
| `GET /auth/url` | Return the OAuth popup URL. |
|
|
462
|
+
| `POST /auth/magic-link/request` | Mode B only — send a magic-link email. |
|
|
463
|
+
| `GET /auth/magic-link/verify` | Mode B only — verify a magic-link token and set the cookie. |
|
|
464
|
+
| `GET /auth/google/url`, `POST /auth/google/callback` | Mode B only — Google OAuth, registered when configured. |
|
|
439
465
|
|
|
440
|
-
|
|
466
|
+
### Custom provider
|
|
441
467
|
|
|
442
468
|
```typescript
|
|
443
469
|
configurator.setAuth({
|
|
444
470
|
verify: async (code) => ({ userId: '...', token: 'platform-issued-jwt' }),
|
|
445
471
|
authUrl: (origin) => `https://my-oauth.example/authorize?origin=${origin}`,
|
|
446
|
-
registerRoutes: (router) => { /*
|
|
472
|
+
registerRoutes: (router, { db, onUserCreate }) => { /* extra routes */ },
|
|
447
473
|
});
|
|
448
474
|
```
|
|
449
475
|
|
|
450
|
-
|
|
476
|
+
### Server-side helpers (`ugly-app`)
|
|
451
477
|
|
|
452
478
|
- `verifyToken(token)` — verifies a token against ugly.bot and returns the `userId`.
|
|
453
479
|
- `getRequestUser(req)` — synchronous decode of the per-project `UGLY_PROJECT_TOKEN` cookie set by ugly.bot's wake-on-traffic gate. Returns `{ userId } | null` without a network round-trip; safe to use as the primary auth check in deployed-app handlers.
|
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.523";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
package/package.json
CHANGED
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.523";
|