@fluid-app/fluid-cli-portal 0.1.12 → 0.1.14
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/dist/index.d.mts +1 -389
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +18 -1596
- package/dist/index.mjs.map +1 -1
- package/dist/vite-plugin.d.mts +5660 -1
- package/dist/vite-plugin.d.mts.map +1 -1
- package/dist/vite-plugin.mjs +71 -1
- package/dist/vite-plugin.mjs.map +1 -1
- package/package.json +5 -5
- package/templates/starter/package.json.template +2 -0
- package/templates/fullstack/.dockerignore +0 -9
- package/templates/fullstack/.env.example +0 -15
- package/templates/fullstack/.github/workflows/ci.yml +0 -47
- package/templates/fullstack/.github/workflows/deploy.yml +0 -54
- package/templates/fullstack/Dockerfile +0 -44
- package/templates/fullstack/README.md.template +0 -176
- package/templates/fullstack/drizzle/0000_initial.sql +0 -7
- package/templates/fullstack/drizzle/meta/0000_snapshot.json +0 -63
- package/templates/fullstack/drizzle/meta/_journal.json +0 -13
- package/templates/fullstack/drizzle.config.ts +0 -13
- package/templates/fullstack/esbuild.config.js +0 -14
- package/templates/fullstack/package.json.template +0 -57
- package/templates/fullstack/src/server/db/index.ts +0 -10
- package/templates/fullstack/src/server/db/migrate.ts +0 -12
- package/templates/fullstack/src/server/db/schema.ts +0 -14
- package/templates/fullstack/src/server/entry.ts +0 -59
- package/templates/fullstack/src/server/index.ts +0 -33
- package/templates/fullstack/src/server/routes/index.test.ts +0 -123
- package/templates/fullstack/src/server/routes/index.ts +0 -110
- package/templates/fullstack/src/server/routes/schemas.ts +0 -7
- package/templates/fullstack/src/test/setup.ts +0 -9
- package/templates/fullstack/vite.config.ts +0 -41
- package/templates/fullstack/vitest.config.ts +0 -9
|
@@ -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
|
-
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,57 +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-sdk": "{{sdkVersion}}",
|
|
23
|
-
"@tanstack/react-query": "^5.90.0",
|
|
24
|
-
"@hono/node-server": "^1.13.0",
|
|
25
|
-
"@libsql/client": "^0.17.0",
|
|
26
|
-
"drizzle-orm": "^0.44.0",
|
|
27
|
-
"hono": "^4.7.0",
|
|
28
|
-
"react": "^19.0.0",
|
|
29
|
-
"react-dom": "^19.0.0",
|
|
30
|
-
"react-hook-form": "^7.55.0",
|
|
31
|
-
"@hookform/resolvers": "^4.1.3",
|
|
32
|
-
"zod": "^3.24.0"
|
|
33
|
-
},
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@fluid-app/fluid-cli-portal": "{{sdkVersion}}",
|
|
36
|
-
"@hono/vite-dev-server": "^0.25.0",
|
|
37
|
-
"@tailwindcss/vite": "^4.0.0",
|
|
38
|
-
"esbuild": "^0.25.0",
|
|
39
|
-
"@types/node": "^22.0.0",
|
|
40
|
-
"@types/react": "^19.0.0",
|
|
41
|
-
"@types/react-dom": "^19.0.0",
|
|
42
|
-
"@vitejs/plugin-react": "^4.3.0",
|
|
43
|
-
"drizzle-kit": "^0.31.0",
|
|
44
|
-
"oxlint": "^1.51.0",
|
|
45
|
-
"tailwindcss": "^4.0.0",
|
|
46
|
-
"typescript": "^5.6.0",
|
|
47
|
-
"vite": "^6.0.0",
|
|
48
|
-
"vitest": "^4.0.0"
|
|
49
|
-
},
|
|
50
|
-
"engines": {
|
|
51
|
-
"node": ">=20"
|
|
52
|
-
},
|
|
53
|
-
"packageManager": "pnpm@10.4.1",
|
|
54
|
-
"pnpm": {
|
|
55
|
-
"onlyBuiltDependencies": ["esbuild"]
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -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);
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { Hono } from "hono";
|
|
2
|
-
import { secureHeaders } from "hono/secure-headers";
|
|
3
|
-
import { cors } from "hono/cors";
|
|
4
|
-
import { logger } from "hono/logger";
|
|
5
|
-
import { apiRoutes } from "./routes/index";
|
|
6
|
-
|
|
7
|
-
const app = new Hono();
|
|
8
|
-
|
|
9
|
-
// Security middleware
|
|
10
|
-
app.use("*", secureHeaders());
|
|
11
|
-
app.use(
|
|
12
|
-
"/api/*",
|
|
13
|
-
cors({
|
|
14
|
-
origin: process.env.ALLOWED_ORIGIN ?? "http://localhost:5173",
|
|
15
|
-
}),
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
// Logging
|
|
19
|
-
app.use("*", logger());
|
|
20
|
-
|
|
21
|
-
// Global error handler
|
|
22
|
-
app.onError((err, c) => {
|
|
23
|
-
console.error(err);
|
|
24
|
-
return c.json({ error: "Internal server error" }, 500);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// Health check
|
|
28
|
-
app.get("/api/health", (c) => c.json({ status: "ok" }));
|
|
29
|
-
|
|
30
|
-
// Mount API routes
|
|
31
|
-
app.route("/api", apiRoutes);
|
|
32
|
-
|
|
33
|
-
export default app;
|