@fluid-app/fluid-cli-portal 0.1.13 → 0.1.15

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fluid-app/fluid-cli-portal",
3
- "version": "0.1.13",
4
- "description": "Fluid CLI plugin for building and deploying portal applications",
3
+ "version": "0.1.15",
4
+ "description": "Fluid CLI plugin for building portal applications",
5
5
  "files": [
6
6
  "dist",
7
7
  "templates"
@@ -25,7 +25,6 @@
25
25
  "dependencies": {
26
26
  "chalk": "^5.3.0",
27
27
  "commander": "^12.0.0",
28
- "dotenv": "^17.3.1",
29
28
  "execa": "^8.0.0",
30
29
  "fs-extra": "^11.2.0",
31
30
  "handlebars": "^4.7.8",
@@ -33,7 +32,7 @@
33
32
  "p-limit": "^7.3.0",
34
33
  "prompts": "^2.4.2",
35
34
  "tsx": "^4.21.0",
36
- "@fluid-app/fluid-cli": "0.1.3"
35
+ "@fluid-app/fluid-cli": "0.1.4"
37
36
  },
38
37
  "devDependencies": {
39
38
  "@swc/core": "^1.15.18",
@@ -45,8 +44,8 @@
45
44
  "tsdown": "^0.21.0",
46
45
  "typescript": "^5",
47
46
  "vite": "^6.0.0",
48
- "@fluid-app/fluidos-api-client": "0.1.0",
49
- "@fluid-app/typescript-config": "0.0.0"
47
+ "@fluid-app/typescript-config": "0.0.0",
48
+ "@fluid-app/fluidos-api-client": "0.1.0"
50
49
  },
51
50
  "engines": {
52
51
  "node": ">=18.0.0"
@@ -1,9 +0,0 @@
1
- node_modules
2
- dist
3
- .env
4
- .env.*
5
- !.env.example
6
- .git
7
- *.db
8
- *.db-journal
9
- *.db-wal
@@ -1,15 +0,0 @@
1
- # ─── Development ─────────────────────────────────────────────────────────────
2
- VITE_API_URL=https://api.fluid.app
3
- DATABASE_URL=file:local.db
4
- DATABASE_AUTH_TOKEN=
5
-
6
- # Allowed CORS origin (defaults to http://localhost:5173 in development)
7
- ALLOWED_ORIGIN=
8
-
9
- # ─── Deployment (used by `fluid deploy`) ─────────────────────────────────────
10
- # Fluid company API key (required for deploy/destroy)
11
- FLUID_COMPANY_API_KEY=
12
-
13
- # Get your API token at: https://app.turso.tech/api-tokens
14
- TURSO_API_TOKEN=
15
- TURSO_ORG=
@@ -1,47 +0,0 @@
1
- name: CI
2
- on:
3
- pull_request:
4
- branches: [main]
5
-
6
- jobs:
7
- typecheck:
8
- name: Typecheck
9
- runs-on: ubuntu-latest
10
- steps:
11
- - uses: actions/checkout@v4
12
- - uses: pnpm/action-setup@v4
13
- - uses: actions/setup-node@v4
14
- with:
15
- node-version: 20
16
- cache: pnpm
17
- - run: pnpm install --frozen-lockfile
18
- - run: pnpm typecheck
19
-
20
- lint:
21
- name: Lint
22
- runs-on: ubuntu-latest
23
- steps:
24
- - uses: actions/checkout@v4
25
- - uses: pnpm/action-setup@v4
26
- - uses: actions/setup-node@v4
27
- with:
28
- node-version: 20
29
- cache: pnpm
30
- - run: pnpm install --frozen-lockfile
31
- - run: pnpm lint
32
-
33
- test:
34
- name: Test
35
- runs-on: ubuntu-latest
36
- env:
37
- DATABASE_URL: file:test.db
38
- steps:
39
- - uses: actions/checkout@v4
40
- - uses: pnpm/action-setup@v4
41
- - uses: actions/setup-node@v4
42
- with:
43
- node-version: 20
44
- cache: pnpm
45
- - run: pnpm install --frozen-lockfile
46
- - run: pnpm db:push
47
- - run: pnpm test
@@ -1,54 +0,0 @@
1
- name: Deploy
2
- on:
3
- push:
4
- branches: [main]
5
- workflow_dispatch:
6
-
7
- jobs:
8
- build:
9
- name: Build & Validate
10
- runs-on: ubuntu-latest
11
- steps:
12
- - uses: actions/checkout@v4
13
-
14
- - name: Enable corepack
15
- run: corepack enable
16
-
17
- - uses: actions/setup-node@v4
18
- with:
19
- node-version: 20
20
- cache: pnpm
21
-
22
- - name: Install dependencies
23
- run: pnpm install --frozen-lockfile
24
-
25
- - name: Build
26
- run: pnpm build
27
-
28
- deploy:
29
- name: Deploy to Cloud Run
30
- needs: [build]
31
- runs-on: ubuntu-latest
32
- steps:
33
- - uses: actions/checkout@v4
34
-
35
- - name: Authenticate to Google Cloud
36
- uses: google-github-actions/auth@v2
37
- with:
38
- credentials_json: ${{ secrets.GCP_SA_JSON }}
39
-
40
- - name: Set up Cloud SDK
41
- uses: google-github-actions/setup-gcloud@v2
42
-
43
- - name: Get service name
44
- id: meta
45
- run: echo "service=$(jq -r .name package.json | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g' | sed 's/^-\|-$//g')" >> "$GITHUB_OUTPUT"
46
-
47
- - name: Deploy to Cloud Run
48
- run: |
49
- gcloud run deploy ${{ steps.meta.outputs.service }} \
50
- --source . \
51
- --region ${{ vars.CLOUD_RUN_REGION || 'us-central1' }} \
52
- --project ${{ vars.GCP_PROJECT }} \
53
- --allow-unauthenticated \
54
- --set-env-vars="DATABASE_URL=${{ secrets.DATABASE_URL }},DATABASE_AUTH_TOKEN=${{ secrets.DATABASE_AUTH_TOKEN }},NODE_ENV=production"
@@ -1,44 +0,0 @@
1
- FROM node:20-slim AS builder
2
-
3
- WORKDIR /app
4
-
5
- # Copy package files
6
- COPY package.json pnpm-lock.yaml ./
7
-
8
- # Install dependencies
9
- RUN corepack enable && pnpm install --frozen-lockfile
10
-
11
- # Copy source code
12
- COPY . .
13
-
14
- # Build frontend (Vite) and server (esbuild)
15
- RUN pnpm run build
16
-
17
- # Collect production files into a staging directory for clean cross-stage copy
18
- RUN mkdir /staging && cp package.json pnpm-lock.yaml /staging/
19
-
20
- # --- Production stage ---
21
- FROM node:20-slim AS runner
22
-
23
- WORKDIR /app
24
-
25
- ENV NODE_ENV=production
26
- ENV PORT=8080
27
-
28
- # Copy build outputs and migration files
29
- COPY --from=builder /app/dist ./dist
30
- COPY --from=builder /app/drizzle ./drizzle
31
-
32
- # Copy package + lock files for production install
33
- COPY --from=builder /staging ./
34
-
35
- # Install production dependencies only (@libsql/client needs node_modules)
36
- RUN corepack enable && pnpm install --prod --frozen-lockfile
37
-
38
- RUN addgroup --system --gid 1001 appgroup && \
39
- adduser --system --uid 1001 --ingroup appgroup appuser
40
- USER appuser
41
-
42
- EXPOSE 8080
43
-
44
- CMD ["node", "dist/server/entry.js"]
@@ -1,176 +0,0 @@
1
- # {{projectName}}
2
-
3
- A Fluid Commerce Portal built with the **Fullstack** template — includes [Hono](https://hono.dev/) API server, [Drizzle ORM](https://orm.drizzle.team/), and SQLite for a complete frontend + backend setup.
4
-
5
- ## What's Included
6
-
7
- ### Frontend
8
-
9
- - [React 19](https://react.dev/) with [TypeScript](https://www.typescriptlang.org/)
10
- - [Vite](https://vite.dev/) for fast development and optimized builds
11
- - [Tailwind CSS 4](https://tailwindcss.com/) for utility-first styling
12
- - [TanStack Query](https://tanstack.com/query) for data fetching and caching
13
- - [Fluid Portal SDK](https://github.com/fluidcommerce) for Fluid Commerce integration
14
- - Pre-configured navigation, screen routing, and dashboard widgets
15
-
16
- ### Backend
17
-
18
- - [Hono](https://hono.dev/) — lightweight, fast API server integrated with Vite dev server
19
- - [Drizzle ORM](https://orm.drizzle.team/) — type-safe SQL with zero overhead
20
- - [libSQL](https://github.com/tursodatabase/libsql-client-ts) — SQLite-compatible database, works locally and with [Turso](https://turso.tech/)
21
- - Example CRUD routes (`/api/todos`) to get started quickly
22
-
23
- ## Getting Started
24
-
25
- ```bash
26
- # Install dependencies
27
- pnpm install
28
-
29
- # Push the database schema (creates the SQLite database)
30
- pnpm db:push
31
-
32
- # Start the development server
33
- pnpm dev
34
- ```
35
-
36
- Open [http://localhost:5173](http://localhost:5173) to view your portal. _(Port may differ if 5173 is in use — check the dev server output.)_
37
-
38
- ### Other Commands
39
-
40
- ```bash
41
- pnpm build # Build for production (frontend + server)
42
- pnpm start # Start the production server
43
- pnpm preview # Preview production build via Vite
44
- pnpm typecheck # Run TypeScript type checking
45
- pnpm lint # Run ESLint
46
- ```
47
-
48
- ## Database Setup
49
-
50
- This template uses SQLite with Drizzle ORM. The database is stored as a local file (`local.db`).
51
-
52
- ```bash
53
- # Create or update the database schema from your Drizzle schema definitions
54
- pnpm db:push
55
-
56
- # Open Drizzle Studio to browse and edit your data
57
- pnpm db:studio
58
-
59
- # Generate SQL migration files (for production workflows)
60
- pnpm db:generate
61
-
62
- # Run pending migrations
63
- pnpm db:migrate
64
- ```
65
-
66
- Edit the schema in `src/server/db/schema.ts`, then run `pnpm db:push` to apply changes during development.
67
-
68
- ## Project Structure
69
-
70
- ```
71
- src/
72
- ├── main.tsx # App entry with FluidProvider setup
73
- ├── App.tsx # Main app with screen routing
74
- ├── index.css # Global styles (Tailwind CSS)
75
- ├── fluid.config.ts # Fluid SDK configuration
76
- ├── portal.config.ts # Navigation and screen registration
77
- ├── navigation/
78
- │ └── index.tsx # Sidebar navigation component
79
- ├── screens/
80
- │ └── Dashboard.tsx # Dashboard with widgets
81
- └── server/
82
- ├── index.ts # Hono app (mounts routes, exported for dev + prod)
83
- ├── entry.ts # Production server entry (Node.js + static serving)
84
- ├── routes/
85
- │ └── index.ts # Example CRUD routes (/api/todos)
86
- └── db/
87
- ├── index.ts # Database connection
88
- ├── migrate.ts # Auto-migration on server start
89
- └── schema.ts # Drizzle schema definitions
90
- ```
91
-
92
- ## Adding API Routes
93
-
94
- Create a new route file in `src/server/routes/` and mount it in `src/server/index.ts`:
95
-
96
- ```typescript
97
- // src/server/routes/products.ts
98
- import { Hono } from "hono";
99
-
100
- export const productRoutes = new Hono();
101
-
102
- productRoutes.get("/", async (c) => {
103
- // your logic here
104
- return c.json({ products: [] });
105
- });
106
- ```
107
-
108
- ```typescript
109
- // src/server/index.ts
110
- import { productRoutes } from "./routes/products";
111
-
112
- app.route("/api/products", productRoutes);
113
- ```
114
-
115
- ## Using Turso (Cloud Database)
116
-
117
- This template uses `@libsql/client`, which works with both local SQLite files and [Turso](https://turso.tech/) cloud databases. To use Turso:
118
-
119
- 1. Create a Turso database: `turso db create my-app`
120
- 2. Get the connection URL: `turso db show my-app --url`
121
- 3. Create an auth token: `turso db tokens create my-app`
122
- 4. Set `DATABASE_URL` and `DATABASE_AUTH_TOKEN` in your `.env` file
123
-
124
- ## Docker
125
-
126
- Build and run with Docker:
127
-
128
- ```bash
129
- docker build -t {{projectName}} .
130
- docker run -p 8080:8080 -e DATABASE_URL=<your-turso-url> -e DATABASE_AUTH_TOKEN=<your-token> {{projectName}}
131
- ```
132
-
133
- Database migrations run automatically when the server starts, so the database schema is always up to date.
134
-
135
- ## CI/CD
136
-
137
- This project includes GitHub Actions workflows for automated checks and deployment.
138
-
139
- ### Pull Request Checks
140
-
141
- Every pull request runs typecheck and lint automatically. See `.github/workflows/ci.yml`.
142
-
143
- ### Deployment
144
-
145
- Pushes to `main` automatically deploy to Cloud Run. To set this up:
146
-
147
- 1. **Run initial deploy locally** (provisions Turso database + first Cloud Run deploy):
148
- ```bash
149
- fluid deploy
150
- ```
151
-
152
- 2. **Note the database credentials** from the deploy output (DATABASE_URL and DATABASE_AUTH_TOKEN)
153
-
154
- 3. **Add GitHub repository secrets** (Settings > Secrets and variables > Actions):
155
- - `GCP_SA_JSON` — GCP service account key JSON ([create one](https://cloud.google.com/iam/docs/creating-managing-service-account-keys))
156
- - `DATABASE_URL` — Turso database URL from step 1
157
- - `DATABASE_AUTH_TOKEN` — Turso auth token from step 1
158
-
159
- 4. **Add GitHub repository variables** (Settings > Secrets and variables > Actions > Variables):
160
- - `GCP_PROJECT` — Your GCP project ID
161
- - `CLOUD_RUN_REGION` — Cloud Run region (default: `us-central1`)
162
-
163
- 5. **Push to main** — deployment happens automatically
164
-
165
- ## Styling
166
-
167
- This project uses [Tailwind CSS 4](https://tailwindcss.com/) for styling. The global styles are imported in `src/index.css`. You can customize the theme and add utilities there.
168
-
169
- ## Learn More
170
-
171
- - [Fluid Portal SDK Documentation](https://github.com/fluidcommerce)
172
- - [Hono Documentation](https://hono.dev/)
173
- - [Drizzle ORM Documentation](https://orm.drizzle.team/)
174
- - [Vite Documentation](https://vite.dev/)
175
- - [React Documentation](https://react.dev/)
176
- - [Tailwind CSS Documentation](https://tailwindcss.com/)
@@ -1,7 +0,0 @@
1
- CREATE TABLE IF NOT EXISTS `todos` (
2
- `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
3
- `title` text NOT NULL,
4
- `completed` integer DEFAULT false NOT NULL,
5
- `created_at` text DEFAULT (current_timestamp) NOT NULL,
6
- `updated_at` text DEFAULT (current_timestamp) NOT NULL
7
- );
@@ -1,63 +0,0 @@
1
- {
2
- "id": "00000000-0000-0000-0000-000000000000",
3
- "prevId": "",
4
- "version": "6",
5
- "dialect": "sqlite",
6
- "tables": {
7
- "todos": {
8
- "name": "todos",
9
- "columns": {
10
- "id": {
11
- "name": "id",
12
- "type": "integer",
13
- "primaryKey": true,
14
- "notNull": true,
15
- "autoincrement": true
16
- },
17
- "title": {
18
- "name": "title",
19
- "type": "text",
20
- "primaryKey": false,
21
- "notNull": true,
22
- "autoincrement": false
23
- },
24
- "completed": {
25
- "name": "completed",
26
- "type": "integer",
27
- "primaryKey": false,
28
- "notNull": true,
29
- "autoincrement": false,
30
- "default": false
31
- },
32
- "created_at": {
33
- "name": "created_at",
34
- "type": "text",
35
- "primaryKey": false,
36
- "notNull": true,
37
- "autoincrement": false,
38
- "default": "(current_timestamp)"
39
- },
40
- "updated_at": {
41
- "name": "updated_at",
42
- "type": "text",
43
- "primaryKey": false,
44
- "notNull": true,
45
- "autoincrement": false,
46
- "default": "(current_timestamp)"
47
- }
48
- },
49
- "indexes": {},
50
- "foreignKeys": {},
51
- "compositePrimaryKeys": {},
52
- "uniqueConstraints": {},
53
- "checkConstraints": {}
54
- }
55
- },
56
- "enums": {},
57
- "views": {},
58
- "_meta": {
59
- "schemas": {},
60
- "tables": {},
61
- "columns": {}
62
- }
63
- }
@@ -1,13 +0,0 @@
1
- {
2
- "version": "7",
3
- "dialect": "sqlite",
4
- "entries": [
5
- {
6
- "idx": 0,
7
- "version": "7",
8
- "when": 1737000000000,
9
- "tag": "0000_initial",
10
- "breakpoints": true
11
- }
12
- ]
13
- }
@@ -1,13 +0,0 @@
1
- import { defineConfig } from "drizzle-kit";
2
-
3
- const url = process.env.DATABASE_URL ?? "file:local.db";
4
- const isLocal = url.startsWith("file:") || url === ":memory:";
5
-
6
- export default defineConfig({
7
- schema: "./src/server/db/schema.ts",
8
- out: "./drizzle",
9
- dialect: isLocal ? "sqlite" : "turso",
10
- dbCredentials: isLocal
11
- ? { url }
12
- : { url, authToken: process.env.DATABASE_AUTH_TOKEN },
13
- });
@@ -1,14 +0,0 @@
1
- import { build } from "esbuild";
2
-
3
- await build({
4
- entryPoints: ["src/server/entry.ts"],
5
- bundle: true,
6
- platform: "node",
7
- target: "node20",
8
- format: "esm",
9
- outfile: "dist/server/entry.js",
10
- external: ["@libsql/client", "drizzle-orm"],
11
- banner: {
12
- js: 'import { createRequire } from "module"; const require = createRequire(import.meta.url);',
13
- },
14
- });
@@ -1,58 +0,0 @@
1
- {
2
- "name": "{{projectName}}",
3
- "version": "0.1.0",
4
- "private": true,
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vite build && node esbuild.config.js",
9
- "start": "node dist/server/entry.js",
10
- "preview": "vite preview",
11
- "typecheck": "tsc --noEmit",
12
- "lint": "oxlint",
13
- "db:generate": "drizzle-kit generate",
14
- "db:migrate": "drizzle-kit migrate",
15
- "db:push": "drizzle-kit push",
16
- "db:studio": "drizzle-kit studio",
17
- "deploy": "fluid deploy",
18
- "test": "vitest run",
19
- "test:watch": "vitest"
20
- },
21
- "dependencies": {
22
- "@fluid-app/portal-preview": "{{sdkVersion}}",
23
- "@fluid-app/portal-sdk": "{{sdkVersion}}",
24
- "@tanstack/react-query": "^5.90.0",
25
- "@hono/node-server": "^1.13.0",
26
- "@libsql/client": "^0.17.0",
27
- "drizzle-orm": "^0.44.0",
28
- "hono": "^4.7.0",
29
- "react": "^19.0.0",
30
- "react-dom": "^19.0.0",
31
- "react-hook-form": "^7.55.0",
32
- "@hookform/resolvers": "^4.1.3",
33
- "zod": "^3.24.0"
34
- },
35
- "devDependencies": {
36
- "@fluid-app/fluid-cli-portal": "{{sdkVersion}}",
37
- "@hono/vite-dev-server": "^0.25.0",
38
- "@tailwindcss/vite": "^4.0.0",
39
- "esbuild": "^0.25.0",
40
- "@types/node": "^22.0.0",
41
- "@types/react": "^19.0.0",
42
- "@types/react-dom": "^19.0.0",
43
- "@vitejs/plugin-react": "^4.3.0",
44
- "drizzle-kit": "^0.31.0",
45
- "oxlint": "^1.51.0",
46
- "tailwindcss": "^4.0.0",
47
- "typescript": "^5.6.0",
48
- "vite": "^6.0.0",
49
- "vitest": "^4.0.0"
50
- },
51
- "engines": {
52
- "node": ">=20"
53
- },
54
- "packageManager": "pnpm@10.4.1",
55
- "pnpm": {
56
- "onlyBuiltDependencies": ["esbuild"]
57
- }
58
- }
@@ -1,10 +0,0 @@
1
- import { createClient } from "@libsql/client";
2
- import { drizzle } from "drizzle-orm/libsql";
3
- import * as schema from "./schema";
4
-
5
- const client = createClient({
6
- url: process.env.DATABASE_URL ?? "file:local.db",
7
- authToken: process.env.DATABASE_AUTH_TOKEN,
8
- });
9
-
10
- export const db = drizzle(client, { schema });
@@ -1,12 +0,0 @@
1
- import { dirname, resolve } from "node:path";
2
- import { fileURLToPath } from "node:url";
3
- import { migrate } from "drizzle-orm/libsql/migrator";
4
- import { db } from "./index";
5
-
6
- const __dirname = dirname(fileURLToPath(import.meta.url));
7
-
8
- export async function runMigrations() {
9
- await migrate(db, {
10
- migrationsFolder: resolve(__dirname, "../../../drizzle"),
11
- });
12
- }
@@ -1,14 +0,0 @@
1
- import { sqliteTable, integer, text } from "drizzle-orm/sqlite-core";
2
- import { sql } from "drizzle-orm";
3
-
4
- export const todos = sqliteTable("todos", {
5
- id: integer("id").primaryKey({ autoIncrement: true }),
6
- title: text("title").notNull(),
7
- completed: integer("completed", { mode: "boolean" }).notNull().default(false),
8
- createdAt: text("created_at")
9
- .notNull()
10
- .default(sql`(current_timestamp)`),
11
- updatedAt: text("updated_at")
12
- .notNull()
13
- .default(sql`(current_timestamp)`),
14
- });
@@ -1,59 +0,0 @@
1
- import { serve } from "@hono/node-server";
2
- import { serveStatic } from "@hono/node-server/serve-static";
3
- import { runMigrations } from "./db/migrate";
4
- import app from "./index";
5
-
6
- // Run database migrations before starting the server
7
- try {
8
- await runMigrations();
9
- } catch (error) {
10
- console.error("Failed to run database migrations:", error);
11
- console.error(
12
- "Ensure DATABASE_URL and DATABASE_AUTH_TOKEN are set correctly.",
13
- );
14
- process.exit(1);
15
- }
16
-
17
- // Cache immutable hashed assets for 1 year
18
- app.use("/assets/*", async (c, next) => {
19
- await next();
20
- c.header("Cache-Control", "public, max-age=31536000, immutable");
21
- });
22
-
23
- // Serve static frontend assets from dist/public/
24
- app.use("/*", serveStatic({ root: "./dist/public" }));
25
-
26
- // SPA fallback — serve index.html for all unmatched routes
27
- app.get("*", serveStatic({ root: "./dist/public", path: "index.html" }));
28
-
29
- const port = Number(process.env.PORT) || 8080;
30
-
31
- const server = serve({ fetch: app.fetch, port }, (info) => {
32
- console.log(`Server listening on http://localhost:${info.port}`);
33
- });
34
-
35
- server.on("error", (err: NodeJS.ErrnoException) => {
36
- if (err.code === "EADDRINUSE") {
37
- console.error(`\nPort ${port} is already in use.`);
38
- console.error(
39
- `Try: PORT=${port === 8080 ? 3001 : port + 1} node dist/server/entry.js\n`,
40
- );
41
- process.exit(1);
42
- }
43
- throw err;
44
- });
45
-
46
- // Graceful shutdown
47
- function shutdown() {
48
- console.log("Shutting down gracefully...");
49
- server.close();
50
- const forceExit = setTimeout(() => {
51
- console.error("Forcing shutdown after timeout");
52
- process.exit(1);
53
- }, 10_000);
54
- // Allow the process to exit naturally if close finishes before timeout
55
- forceExit.unref();
56
- }
57
-
58
- process.on("SIGTERM", shutdown);
59
- process.on("SIGINT", shutdown);