@dalgoridim/headless-cms 0.5.0 → 0.5.1
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 +85 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ npm install @dalgoridim/headless-cms
|
|
|
26
26
|
npm install firebase firebase-admin cloudinary # Firestore + Cloudinary + Firebase
|
|
27
27
|
npm install pg # Postgres
|
|
28
28
|
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner # S3
|
|
29
|
+
npm install @react-oauth/google # Google sign-in (auth/google/client)
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
`react` / `react-dom` are peer deps; every backend SDK is an **optional** peer —
|
|
@@ -38,7 +39,7 @@ Server-only code never leaks into the client bundle. Import from the right entry
|
|
|
38
39
|
| Entry | Contents |
|
|
39
40
|
|---|---|
|
|
40
41
|
| `@dalgoridim/headless-cms` | Shared types only (safe anywhere) |
|
|
41
|
-
| `.../client` | `PageProvider`, `usePageContext
|
|
42
|
+
| `.../client` | `PageProvider`, `usePageContext` (field edits **+ collection add/remove/reorder**), `ContentEditSpan`, `EditableImage`, `useMarkdownEditor`, `CmsAuthProvider`, `useCmsAuth` |
|
|
42
43
|
| `.../server` | `createCmsHandlers`, `createAdminGate`, `resolveRelations` |
|
|
43
44
|
| `.../adapters/firestore` | `FirestoreDataAdapter` |
|
|
44
45
|
| `.../adapters/postgres` | `PostgresDataAdapter` (hybrid JSONB + typed tables) |
|
|
@@ -46,6 +47,8 @@ Server-only code never leaks into the client bundle. Import from the right entry
|
|
|
46
47
|
| `.../storage/{cloudinary,s3,local}/server` | **Server** signers — pull in SDKs, server-only |
|
|
47
48
|
| `.../auth/firebase` | `firebaseAuth` (server gate) |
|
|
48
49
|
| `.../auth/firebase/client` | `FirebaseAuthProvider`, `useFirebaseAuth` |
|
|
50
|
+
| `.../auth/google` | `googleAuth` (server gate), `verifyGoogleIdToken` — **Firebase-free** Google sign-in |
|
|
51
|
+
| `.../auth/google/client` | `GoogleAuthProvider`, `useGoogleAuth`, `GoogleSignInButton` |
|
|
49
52
|
| `.../auth/nextauth` | `nextAuthAuth`, `customAuth` |
|
|
50
53
|
|
|
51
54
|
## Core interfaces
|
|
@@ -214,6 +217,45 @@ function Editor({ initialValue, onSave }) {
|
|
|
214
217
|
}
|
|
215
218
|
```
|
|
216
219
|
|
|
220
|
+
## Editable collections (add / remove / reorder)
|
|
221
|
+
|
|
222
|
+
`ContentEditSpan` / `EditableImage` edit the **fields of one item**. To let an admin
|
|
223
|
+
change **which items exist** and **their order** (e.g. add a project, delete a tool,
|
|
224
|
+
drag to reorder), `usePageContext` exposes optimistic, self-persisting collection
|
|
225
|
+
ops. Hydrate the lists from the server via `initialCollections`:
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
<PageProvider
|
|
229
|
+
initialSections={sections}
|
|
230
|
+
initialCollections={{ projects: await data.fetchCollection("projects") }}
|
|
231
|
+
>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
import { usePageContext } from "@dalgoridim/headless-cms/client";
|
|
236
|
+
|
|
237
|
+
const { collections, createItem, deleteItem, reorderItems } = usePageContext();
|
|
238
|
+
|
|
239
|
+
// add — PUT (upsert) with a generated id; returns the new id
|
|
240
|
+
await createItem("projects", { title: "New project", order: collections.projects.length });
|
|
241
|
+
|
|
242
|
+
// remove — DELETE
|
|
243
|
+
await deleteItem("projects", id);
|
|
244
|
+
|
|
245
|
+
// reorder — PATCH each item's integer `order` to match the new sequence
|
|
246
|
+
await reorderItems("projects", ["id-c", "id-a", "id-b"]);
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
| Op | Persists via | Notes |
|
|
250
|
+
|---|---|---|
|
|
251
|
+
| `createItem(collection, data, opts?)` | `PUT {base}/{collection}/{id}` | `opts.id` to set the id, `opts.atStart` to prepend; returns the id |
|
|
252
|
+
| `deleteItem(collection, id)` | `DELETE {base}/{collection}/{id}` | |
|
|
253
|
+
| `reorderItems(collection, orderedIds)` | `PATCH {base}/{collection}/{id}` ×N | writes `{ order: index }` per item |
|
|
254
|
+
|
|
255
|
+
All three update `collections` optimistically and **roll back on failure** (with a
|
|
256
|
+
`notify` toast). Order is an `int` — register the collection with a typed `order`
|
|
257
|
+
column (see [Postgres](#postgres-hybrid)) so it sorts numerically.
|
|
258
|
+
|
|
217
259
|
## The Query language
|
|
218
260
|
|
|
219
261
|
A neutral, backend-agnostic query passed to `DataAdapter.fetchCollection`:
|
|
@@ -321,6 +363,48 @@ createCmsHandlers({
|
|
|
321
363
|
});
|
|
322
364
|
```
|
|
323
365
|
|
|
366
|
+
### Google sign-in (no Firebase)
|
|
367
|
+
|
|
368
|
+
`auth/google` is a Firebase-free way to do "Sign in with Google" — one OAuth client
|
|
369
|
+
ID, no service account. The server verifies the Google ID token **locally** against
|
|
370
|
+
Google's public keys (Node `crypto` + `fetch`, zero new deps) and grants admin only
|
|
371
|
+
to a verified email in your allowlist.
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
// server gate
|
|
375
|
+
import { googleAuth } from "@dalgoridim/headless-cms/auth/google";
|
|
376
|
+
|
|
377
|
+
export const { GET, PATCH, PUT, DELETE } = createCmsHandlers({
|
|
378
|
+
data,
|
|
379
|
+
auth: googleAuth({
|
|
380
|
+
clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
|
|
381
|
+
adminEmails: process.env.ADMIN_EMAILS!.split(","),
|
|
382
|
+
}),
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
```tsx
|
|
387
|
+
// client — built on @react-oauth/google (install it)
|
|
388
|
+
import {
|
|
389
|
+
GoogleAuthProvider,
|
|
390
|
+
GoogleSignInButton,
|
|
391
|
+
useGoogleAuth,
|
|
392
|
+
} from "@dalgoridim/headless-cms/auth/google/client";
|
|
393
|
+
|
|
394
|
+
<GoogleAuthProvider clientId={clientId} adminEmails={["you@example.com"]}>
|
|
395
|
+
<PageProvider …>{children}</PageProvider>
|
|
396
|
+
</GoogleAuthProvider>;
|
|
397
|
+
|
|
398
|
+
// anywhere inside the provider — renders the official Google button:
|
|
399
|
+
<GoogleSignInButton onError={() => toast.error("Sign-in failed")} />;
|
|
400
|
+
// useGoogleAuth() → { user, isAdmin, isEditing, toggleEdit, logout }
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
The button writes the ID token to an `adminToken` cookie that `googleAuth` reads;
|
|
404
|
+
`isAdmin` is optimistic on the client (via `adminEmails`) with the server gate
|
|
405
|
+
authoritative. `verifyGoogleIdToken(token, { clientId })` is also exported for
|
|
406
|
+
custom flows.
|
|
407
|
+
|
|
324
408
|
## Your content types stay unconstrained
|
|
325
409
|
|
|
326
410
|
The engine only adds addressing fields; your domain type `T` is never forced to
|