@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/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/package.json +5 -6
- 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 -58
- 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,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;
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import app from "../index";
|
|
3
|
-
|
|
4
|
-
describe("API Routes", () => {
|
|
5
|
-
// Health check
|
|
6
|
-
it("GET /api/health returns 200", async () => {
|
|
7
|
-
const res = await app.request("/api/health");
|
|
8
|
-
expect(res.status).toBe(200);
|
|
9
|
-
const body = await res.json();
|
|
10
|
-
expect(body).toEqual({ status: "ok" });
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
// List todos (empty)
|
|
14
|
-
it("GET /api/todos returns empty array", async () => {
|
|
15
|
-
const res = await app.request("/api/todos");
|
|
16
|
-
expect(res.status).toBe(200);
|
|
17
|
-
const body = await res.json();
|
|
18
|
-
expect(body).toEqual([]);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Create todo
|
|
22
|
-
it("POST /api/todos creates a todo", async () => {
|
|
23
|
-
const res = await app.request("/api/todos", {
|
|
24
|
-
method: "POST",
|
|
25
|
-
headers: { "Content-Type": "application/json" },
|
|
26
|
-
body: JSON.stringify({ title: "Test todo" }),
|
|
27
|
-
});
|
|
28
|
-
expect(res.status).toBe(201);
|
|
29
|
-
const body = await res.json();
|
|
30
|
-
expect(body.title).toBe("Test todo");
|
|
31
|
-
expect(body.completed).toBe(false);
|
|
32
|
-
expect(body.id).toBeDefined();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// Create invalid (empty body)
|
|
36
|
-
it("POST /api/todos with empty body returns 400", async () => {
|
|
37
|
-
const res = await app.request("/api/todos", {
|
|
38
|
-
method: "POST",
|
|
39
|
-
headers: { "Content-Type": "application/json" },
|
|
40
|
-
body: JSON.stringify({}),
|
|
41
|
-
});
|
|
42
|
-
expect(res.status).toBe(400);
|
|
43
|
-
const body = await res.json();
|
|
44
|
-
expect(body.error).toBe("Validation failed");
|
|
45
|
-
expect(body.details).toBeDefined();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Create invalid (empty title)
|
|
49
|
-
it("POST /api/todos with empty title returns 400", async () => {
|
|
50
|
-
const res = await app.request("/api/todos", {
|
|
51
|
-
method: "POST",
|
|
52
|
-
headers: { "Content-Type": "application/json" },
|
|
53
|
-
body: JSON.stringify({ title: "" }),
|
|
54
|
-
});
|
|
55
|
-
expect(res.status).toBe(400);
|
|
56
|
-
const body = await res.json();
|
|
57
|
-
expect(body.error).toBe("Validation failed");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Toggle todo
|
|
61
|
-
it("PATCH /api/todos/:id toggles completion", async () => {
|
|
62
|
-
// Create first
|
|
63
|
-
const createRes = await app.request("/api/todos", {
|
|
64
|
-
method: "POST",
|
|
65
|
-
headers: { "Content-Type": "application/json" },
|
|
66
|
-
body: JSON.stringify({ title: "Toggle me" }),
|
|
67
|
-
});
|
|
68
|
-
const created = await createRes.json();
|
|
69
|
-
|
|
70
|
-
// Toggle
|
|
71
|
-
const res = await app.request(`/api/todos/${created.id}`, {
|
|
72
|
-
method: "PATCH",
|
|
73
|
-
});
|
|
74
|
-
expect(res.status).toBe(200);
|
|
75
|
-
const body = await res.json();
|
|
76
|
-
expect(body.completed).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Toggle non-existent
|
|
80
|
-
it("PATCH /api/todos/999 returns 404", async () => {
|
|
81
|
-
const res = await app.request("/api/todos/999", { method: "PATCH" });
|
|
82
|
-
expect(res.status).toBe(404);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Toggle bad ID
|
|
86
|
-
it("PATCH /api/todos/abc returns 400", async () => {
|
|
87
|
-
const res = await app.request("/api/todos/abc", { method: "PATCH" });
|
|
88
|
-
expect(res.status).toBe(400);
|
|
89
|
-
const body = await res.json();
|
|
90
|
-
expect(body.error).toBe("Invalid ID");
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// Delete todo
|
|
94
|
-
it("DELETE /api/todos/:id deletes a todo", async () => {
|
|
95
|
-
const createRes = await app.request("/api/todos", {
|
|
96
|
-
method: "POST",
|
|
97
|
-
headers: { "Content-Type": "application/json" },
|
|
98
|
-
body: JSON.stringify({ title: "Delete me" }),
|
|
99
|
-
});
|
|
100
|
-
const created = await createRes.json();
|
|
101
|
-
|
|
102
|
-
const res = await app.request(`/api/todos/${created.id}`, {
|
|
103
|
-
method: "DELETE",
|
|
104
|
-
});
|
|
105
|
-
expect(res.status).toBe(200);
|
|
106
|
-
const body = await res.json();
|
|
107
|
-
expect(body.success).toBe(true);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// Delete non-existent
|
|
111
|
-
it("DELETE /api/todos/999 returns 404", async () => {
|
|
112
|
-
const res = await app.request("/api/todos/999", { method: "DELETE" });
|
|
113
|
-
expect(res.status).toBe(404);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Delete bad ID
|
|
117
|
-
it("DELETE /api/todos/abc returns 400", async () => {
|
|
118
|
-
const res = await app.request("/api/todos/abc", { method: "DELETE" });
|
|
119
|
-
expect(res.status).toBe(400);
|
|
120
|
-
const body = await res.json();
|
|
121
|
-
expect(body.error).toBe("Invalid ID");
|
|
122
|
-
});
|
|
123
|
-
});
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { Hono } from "hono";
|
|
2
|
-
import { eq, sql } from "drizzle-orm";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { db } from "../db/index";
|
|
5
|
-
import { todos } from "../db/schema";
|
|
6
|
-
import { createTodoSchema, todoIdSchema } from "./schemas";
|
|
7
|
-
|
|
8
|
-
export const apiRoutes = new Hono();
|
|
9
|
-
|
|
10
|
-
// GET /api/todos — List all todos
|
|
11
|
-
apiRoutes.get("/todos", async (c) => {
|
|
12
|
-
try {
|
|
13
|
-
const allTodos = await db.select().from(todos).all();
|
|
14
|
-
return c.json(allTodos);
|
|
15
|
-
} catch (error) {
|
|
16
|
-
console.error(error);
|
|
17
|
-
return c.json({ error: "Internal server error" }, 500);
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// POST /api/todos — Create a new todo
|
|
22
|
-
apiRoutes.post("/todos", async (c) => {
|
|
23
|
-
try {
|
|
24
|
-
const body = await c.req.json();
|
|
25
|
-
const result = createTodoSchema.safeParse(body);
|
|
26
|
-
|
|
27
|
-
if (!result.success) {
|
|
28
|
-
return c.json(
|
|
29
|
-
{
|
|
30
|
-
error: "Validation failed",
|
|
31
|
-
details: z.flattenError(result.error).fieldErrors,
|
|
32
|
-
},
|
|
33
|
-
400,
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const newTodo = await db
|
|
38
|
-
.insert(todos)
|
|
39
|
-
.values({ title: result.data.title })
|
|
40
|
-
.returning()
|
|
41
|
-
.get();
|
|
42
|
-
return c.json(newTodo, 201);
|
|
43
|
-
} catch (error) {
|
|
44
|
-
console.error(error);
|
|
45
|
-
return c.json({ error: "Internal server error" }, 500);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// PATCH /api/todos/:id — Toggle a todo's completion status
|
|
50
|
-
apiRoutes.patch("/todos/:id", async (c) => {
|
|
51
|
-
try {
|
|
52
|
-
const idResult = todoIdSchema.safeParse(c.req.param("id"));
|
|
53
|
-
|
|
54
|
-
if (!idResult.success) {
|
|
55
|
-
return c.json({ error: "Invalid ID" }, 400);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const id = idResult.data;
|
|
59
|
-
const existing = await db
|
|
60
|
-
.select()
|
|
61
|
-
.from(todos)
|
|
62
|
-
.where(eq(todos.id, id))
|
|
63
|
-
.get();
|
|
64
|
-
|
|
65
|
-
if (!existing) {
|
|
66
|
-
return c.json({ error: "Todo not found" }, 404);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const updated = await db
|
|
70
|
-
.update(todos)
|
|
71
|
-
.set({
|
|
72
|
-
completed: !existing.completed,
|
|
73
|
-
updatedAt: sql`(current_timestamp)`,
|
|
74
|
-
})
|
|
75
|
-
.where(eq(todos.id, id))
|
|
76
|
-
.returning()
|
|
77
|
-
.get();
|
|
78
|
-
return c.json(updated);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
console.error(error);
|
|
81
|
-
return c.json({ error: "Internal server error" }, 500);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// DELETE /api/todos/:id — Delete a todo
|
|
86
|
-
apiRoutes.delete("/todos/:id", async (c) => {
|
|
87
|
-
try {
|
|
88
|
-
const idResult = todoIdSchema.safeParse(c.req.param("id"));
|
|
89
|
-
|
|
90
|
-
if (!idResult.success) {
|
|
91
|
-
return c.json({ error: "Invalid ID" }, 400);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const id = idResult.data;
|
|
95
|
-
const deleted = await db
|
|
96
|
-
.delete(todos)
|
|
97
|
-
.where(eq(todos.id, id))
|
|
98
|
-
.returning()
|
|
99
|
-
.get();
|
|
100
|
-
|
|
101
|
-
if (!deleted) {
|
|
102
|
-
return c.json({ error: "Todo not found" }, 404);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return c.json({ success: true });
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.error(error);
|
|
108
|
-
return c.json({ error: "Internal server error" }, 500);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "vite";
|
|
2
|
-
import react from "@vitejs/plugin-react";
|
|
3
|
-
import tailwindcss from "@tailwindcss/vite";
|
|
4
|
-
import devServer from "@hono/vite-dev-server";
|
|
5
|
-
import { fluidManifestPlugin } from "@fluid-app/portal-sdk/vite";
|
|
6
|
-
|
|
7
|
-
export default defineConfig({
|
|
8
|
-
plugins: [
|
|
9
|
-
react(),
|
|
10
|
-
tailwindcss(),
|
|
11
|
-
fluidManifestPlugin(),
|
|
12
|
-
devServer({
|
|
13
|
-
entry: "src/server/index.ts",
|
|
14
|
-
// Only send /api/* requests to Hono — let Vite handle everything else
|
|
15
|
-
// (static assets, SPA fallback, HMR, etc.)
|
|
16
|
-
exclude: [/^\/(?!api\/).*/, /^\/$/],
|
|
17
|
-
injectClientScript: false,
|
|
18
|
-
}),
|
|
19
|
-
],
|
|
20
|
-
build: {
|
|
21
|
-
target: "esnext",
|
|
22
|
-
outDir: "dist/public",
|
|
23
|
-
chunkSizeWarningLimit: 600,
|
|
24
|
-
rollupOptions: {
|
|
25
|
-
output: {
|
|
26
|
-
manualChunks(id) {
|
|
27
|
-
if (
|
|
28
|
-
id.includes("node_modules/react-dom") ||
|
|
29
|
-
id.includes("node_modules/react/") ||
|
|
30
|
-
id.includes("node_modules/scheduler")
|
|
31
|
-
) {
|
|
32
|
-
return "vendor";
|
|
33
|
-
}
|
|
34
|
-
if (id.includes("node_modules/@tanstack/react-query")) {
|
|
35
|
-
return "query";
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
});
|