flarecms 0.1.0
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 +73 -0
- package/dist/auth/index.js +40 -0
- package/dist/cli/commands.js +389 -0
- package/dist/cli/index.js +403 -0
- package/dist/cli/mcp.js +209 -0
- package/dist/db/index.js +164 -0
- package/dist/index.js +17626 -0
- package/package.json +105 -0
- package/scripts/fix-api-paths.mjs +32 -0
- package/scripts/fix-imports.mjs +38 -0
- package/scripts/prefix-css.mjs +45 -0
- package/src/api/lib/cache.ts +45 -0
- package/src/api/lib/response.ts +40 -0
- package/src/api/middlewares/auth.ts +186 -0
- package/src/api/middlewares/cors.ts +10 -0
- package/src/api/middlewares/rbac.ts +85 -0
- package/src/api/routes/auth.ts +377 -0
- package/src/api/routes/collections.ts +205 -0
- package/src/api/routes/content.ts +175 -0
- package/src/api/routes/device.ts +160 -0
- package/src/api/routes/magic.ts +150 -0
- package/src/api/routes/mcp.ts +273 -0
- package/src/api/routes/oauth.ts +160 -0
- package/src/api/routes/settings.ts +43 -0
- package/src/api/routes/setup.ts +307 -0
- package/src/api/routes/tokens.ts +80 -0
- package/src/api/schemas/auth.ts +15 -0
- package/src/api/schemas/index.ts +51 -0
- package/src/api/schemas/tokens.ts +24 -0
- package/src/auth/index.ts +28 -0
- package/src/cli/commands.ts +217 -0
- package/src/cli/index.ts +21 -0
- package/src/cli/mcp.ts +210 -0
- package/src/cli/tests/cli.test.ts +40 -0
- package/src/cli/tests/create.test.ts +87 -0
- package/src/client/FlareAdminRouter.tsx +47 -0
- package/src/client/app.tsx +175 -0
- package/src/client/components/app-sidebar.tsx +227 -0
- package/src/client/components/collection-modal.tsx +215 -0
- package/src/client/components/content-list.tsx +247 -0
- package/src/client/components/dynamic-form.tsx +190 -0
- package/src/client/components/field-modal.tsx +221 -0
- package/src/client/components/settings/api-token-section.tsx +400 -0
- package/src/client/components/settings/general-section.tsx +224 -0
- package/src/client/components/settings/security-section.tsx +154 -0
- package/src/client/components/settings/seo-section.tsx +200 -0
- package/src/client/components/settings/signup-section.tsx +257 -0
- package/src/client/components/ui/accordion.tsx +78 -0
- package/src/client/components/ui/avatar.tsx +107 -0
- package/src/client/components/ui/badge.tsx +52 -0
- package/src/client/components/ui/button.tsx +60 -0
- package/src/client/components/ui/card.tsx +103 -0
- package/src/client/components/ui/checkbox.tsx +27 -0
- package/src/client/components/ui/collapsible.tsx +19 -0
- package/src/client/components/ui/dialog.tsx +162 -0
- package/src/client/components/ui/icon-picker.tsx +485 -0
- package/src/client/components/ui/icons-data.ts +8476 -0
- package/src/client/components/ui/input.tsx +20 -0
- package/src/client/components/ui/label.tsx +20 -0
- package/src/client/components/ui/popover.tsx +91 -0
- package/src/client/components/ui/select.tsx +204 -0
- package/src/client/components/ui/separator.tsx +23 -0
- package/src/client/components/ui/sheet.tsx +141 -0
- package/src/client/components/ui/sidebar.tsx +722 -0
- package/src/client/components/ui/skeleton.tsx +13 -0
- package/src/client/components/ui/sonner.tsx +47 -0
- package/src/client/components/ui/switch.tsx +30 -0
- package/src/client/components/ui/table.tsx +116 -0
- package/src/client/components/ui/tabs.tsx +80 -0
- package/src/client/components/ui/textarea.tsx +18 -0
- package/src/client/components/ui/tooltip.tsx +68 -0
- package/src/client/hooks/use-mobile.ts +19 -0
- package/src/client/index.css +149 -0
- package/src/client/index.ts +7 -0
- package/src/client/layouts/admin-layout.tsx +93 -0
- package/src/client/layouts/settings-layout.tsx +104 -0
- package/src/client/lib/api.ts +72 -0
- package/src/client/lib/utils.ts +6 -0
- package/src/client/main.tsx +10 -0
- package/src/client/pages/collection-detail.tsx +634 -0
- package/src/client/pages/collections.tsx +180 -0
- package/src/client/pages/dashboard.tsx +133 -0
- package/src/client/pages/device.tsx +66 -0
- package/src/client/pages/document-detail-page.tsx +139 -0
- package/src/client/pages/documents-page.tsx +103 -0
- package/src/client/pages/login.tsx +345 -0
- package/src/client/pages/settings.tsx +65 -0
- package/src/client/pages/setup.tsx +129 -0
- package/src/client/pages/signup.tsx +188 -0
- package/src/client/store/auth.ts +30 -0
- package/src/client/store/collections.ts +13 -0
- package/src/client/store/config.ts +12 -0
- package/src/client/store/fetcher.ts +30 -0
- package/src/client/store/router.ts +95 -0
- package/src/client/store/schema.ts +39 -0
- package/src/client/store/settings.ts +31 -0
- package/src/client/types.ts +34 -0
- package/src/db/dynamic.ts +70 -0
- package/src/db/index.ts +16 -0
- package/src/db/migrations/001_initial_schema.ts +57 -0
- package/src/db/migrations/002_auth_tables.ts +84 -0
- package/src/db/migrator.ts +61 -0
- package/src/db/schema.ts +142 -0
- package/src/index.ts +12 -0
- package/src/server/index.ts +66 -0
- package/src/types.ts +20 -0
- package/style.css.d.ts +8 -0
- package/tests/css.test.ts +21 -0
- package/tests/modular.test.ts +29 -0
- package/tsconfig.json +10 -0
package/dist/db/index.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
|
|
18
|
+
// src/db/index.ts
|
|
19
|
+
import { Kysely as Kysely3 } from "kysely";
|
|
20
|
+
import { D1Dialect } from "kysely-d1";
|
|
21
|
+
|
|
22
|
+
// src/db/dynamic.ts
|
|
23
|
+
import { sql } from "kysely";
|
|
24
|
+
var FIELD_TYPE_MAP = {
|
|
25
|
+
text: "TEXT",
|
|
26
|
+
number: "REAL",
|
|
27
|
+
integer: "INTEGER",
|
|
28
|
+
boolean: "INTEGER",
|
|
29
|
+
json: "TEXT",
|
|
30
|
+
date: "TEXT"
|
|
31
|
+
};
|
|
32
|
+
async function createCollectionTable(db, slug) {
|
|
33
|
+
const tableName = `ec_${slug}`;
|
|
34
|
+
await sql.raw(`
|
|
35
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
36
|
+
id TEXT PRIMARY KEY,
|
|
37
|
+
slug TEXT NOT NULL,
|
|
38
|
+
status TEXT NOT NULL DEFAULT 'draft',
|
|
39
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
40
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
41
|
+
)
|
|
42
|
+
`).execute(db);
|
|
43
|
+
await sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS idx_${tableName}_slug ON ${tableName} (slug)`).execute(db);
|
|
44
|
+
}
|
|
45
|
+
async function addFieldToTable(db, collectionSlug, fieldSlug, type) {
|
|
46
|
+
const tableName = `ec_${collectionSlug}`;
|
|
47
|
+
const columnType = FIELD_TYPE_MAP[type] || "TEXT";
|
|
48
|
+
await sql.raw(`ALTER TABLE ${tableName} ADD COLUMN ${fieldSlug} ${columnType}`).execute(db);
|
|
49
|
+
}
|
|
50
|
+
async function ensureUniqueSlug(db, collectionName, baseSlug, excludeId) {
|
|
51
|
+
let slug = baseSlug;
|
|
52
|
+
let counter = 0;
|
|
53
|
+
let exists = true;
|
|
54
|
+
while (exists) {
|
|
55
|
+
const currentSlug = counter === 0 ? slug : `${slug}-${counter}`;
|
|
56
|
+
let query = db.selectFrom(`ec_${collectionName}`).select("id").where("slug", "=", currentSlug).where("status", "!=", "deleted");
|
|
57
|
+
if (excludeId) {
|
|
58
|
+
query = query.where("id", "!=", excludeId);
|
|
59
|
+
}
|
|
60
|
+
const collision = await query.executeTakeFirst();
|
|
61
|
+
if (!collision) {
|
|
62
|
+
return currentSlug;
|
|
63
|
+
}
|
|
64
|
+
counter++;
|
|
65
|
+
if (counter > 100)
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
return `${slug}-${Math.random().toString(36).substring(2, 7)}`;
|
|
69
|
+
}
|
|
70
|
+
// src/db/migrator.ts
|
|
71
|
+
import { sql as sql4 } from "kysely";
|
|
72
|
+
|
|
73
|
+
// src/db/migrations/001_initial_schema.ts
|
|
74
|
+
var exports_001_initial_schema = {};
|
|
75
|
+
__export(exports_001_initial_schema, {
|
|
76
|
+
up: () => up,
|
|
77
|
+
down: () => down
|
|
78
|
+
});
|
|
79
|
+
import { sql as sql2 } from "kysely";
|
|
80
|
+
async function up(db) {
|
|
81
|
+
await db.schema.createTable("options").ifNotExists().addColumn("name", "text", (col) => col.primaryKey()).addColumn("value", "text", (col) => col.notNull()).execute();
|
|
82
|
+
await db.schema.createTable("fc_users").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("email", "text", (col) => col.unique().notNull()).addColumn("password", "text").addColumn("role", "text", (col) => col.notNull().defaultTo("admin")).addColumn("disabled", "integer", (col) => col.defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(sql2`CURRENT_TIMESTAMP`)).addColumn("updated_at", "text", (col) => col.defaultTo(sql2`CURRENT_TIMESTAMP`)).execute();
|
|
83
|
+
await db.schema.createTable("fc_collections").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.unique().notNull()).addColumn("label", "text", (col) => col.notNull()).addColumn("label_singular", "text").addColumn("description", "text").addColumn("icon", "text").addColumn("is_public", "integer", (col) => col.defaultTo(0)).addColumn("features", "text").addColumn("url_pattern", "text").addColumn("created_at", "text", (col) => col.defaultTo(sql2`CURRENT_TIMESTAMP`)).addColumn("updated_at", "text", (col) => col.defaultTo(sql2`CURRENT_TIMESTAMP`)).execute();
|
|
84
|
+
await db.schema.createTable("fc_fields").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("collection_id", "text", (col) => col.notNull()).addColumn("label", "text", (col) => col.notNull()).addColumn("slug", "text", (col) => col.notNull()).addColumn("type", "text", (col) => col.notNull()).addColumn("required", "integer", (col) => col.defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(sql2`CURRENT_TIMESTAMP`)).execute();
|
|
85
|
+
}
|
|
86
|
+
async function down(db) {
|
|
87
|
+
await db.schema.dropTable("options").ifExists().execute();
|
|
88
|
+
await db.schema.dropTable("fc_users").ifExists().execute();
|
|
89
|
+
await db.schema.dropTable("fc_collections").ifExists().execute();
|
|
90
|
+
await db.schema.dropTable("fc_fields").ifExists().execute();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/db/migrations/002_auth_tables.ts
|
|
94
|
+
var exports_002_auth_tables = {};
|
|
95
|
+
__export(exports_002_auth_tables, {
|
|
96
|
+
up: () => up2,
|
|
97
|
+
down: () => down2
|
|
98
|
+
});
|
|
99
|
+
import { sql as sql3 } from "kysely";
|
|
100
|
+
async function up2(db) {
|
|
101
|
+
await db.schema.createTable("fc_sessions").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("expires_at", "text", (col) => col.notNull()).execute();
|
|
102
|
+
await db.schema.createTable("fc_passkeys").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("name", "text").addColumn("public_key", "text", (col) => col.notNull()).addColumn("counter", "integer", (col) => col.notNull()).addColumn("device_type", "text", (col) => col.notNull()).addColumn("backed_up", "integer", (col) => col.notNull()).addColumn("transports", "text").addColumn("created_at", "text", (col) => col.defaultTo(sql3`CURRENT_TIMESTAMP`)).addColumn("last_used_at", "text").execute();
|
|
103
|
+
await db.schema.createTable("fc_api_tokens").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("name", "text", (col) => col.notNull()).addColumn("hash", "text", (col) => col.notNull()).addColumn("scopes", "text", (col) => col.notNull()).addColumn("expires_at", "text").addColumn("last_used_at", "text").addColumn("created_at", "text", (col) => col.defaultTo(sql3`CURRENT_TIMESTAMP`)).execute();
|
|
104
|
+
await db.schema.createTable("fc_oauth_accounts").ifNotExists().addColumn("provider_id", "text", (col) => col.notNull()).addColumn("provider_user_id", "text", (col) => col.notNull()).addColumn("user_id", "text", (col) => col.notNull()).addPrimaryKeyConstraint("pk_oauth_accounts", ["provider_id", "provider_user_id"]).execute();
|
|
105
|
+
await db.schema.createTable("fc_verification_tokens").ifNotExists().addColumn("identifier", "text", (col) => col.notNull()).addColumn("token", "text", (col) => col.notNull()).addColumn("expires_at", "text", (col) => col.notNull()).addPrimaryKeyConstraint("pk_verification_tokens", ["identifier", "token"]).execute();
|
|
106
|
+
await db.schema.createTable("fc_device_codes").ifNotExists().addColumn("device_code", "text", (col) => col.primaryKey()).addColumn("user_code", "text", (col) => col.notNull().unique()).addColumn("client_id", "text", (col) => col.notNull()).addColumn("user_id", "text").addColumn("scopes", "text", (col) => col.notNull()).addColumn("expires_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(sql3`CURRENT_TIMESTAMP`)).execute();
|
|
107
|
+
}
|
|
108
|
+
async function down2(db) {
|
|
109
|
+
await db.schema.dropTable("fc_sessions").ifExists().execute();
|
|
110
|
+
await db.schema.dropTable("fc_passkeys").ifExists().execute();
|
|
111
|
+
await db.schema.dropTable("fc_api_tokens").ifExists().execute();
|
|
112
|
+
await db.schema.dropTable("fc_oauth_accounts").ifExists().execute();
|
|
113
|
+
await db.schema.dropTable("fc_verification_tokens").ifExists().execute();
|
|
114
|
+
await db.schema.dropTable("fc_device_codes").ifExists().execute();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/db/migrator.ts
|
|
118
|
+
var STATIC_MIGRATIONS = {
|
|
119
|
+
"001_initial_schema": exports_001_initial_schema,
|
|
120
|
+
"002_auth_tables": exports_002_auth_tables
|
|
121
|
+
};
|
|
122
|
+
async function runMigrations(db) {
|
|
123
|
+
await sql4`
|
|
124
|
+
CREATE TABLE IF NOT EXISTS _fc_migrations (
|
|
125
|
+
name TEXT PRIMARY KEY,
|
|
126
|
+
executed_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
127
|
+
)
|
|
128
|
+
`.execute(db);
|
|
129
|
+
const executed = await db.selectFrom("_fc_migrations").select("name").execute();
|
|
130
|
+
const executedNames = new Set(executed.map((m) => m.name));
|
|
131
|
+
const results = [];
|
|
132
|
+
const migrationNames = Object.keys(STATIC_MIGRATIONS).sort();
|
|
133
|
+
for (const name of migrationNames) {
|
|
134
|
+
if (executedNames.has(name)) {
|
|
135
|
+
results.push({ name, status: "Skipped" });
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
const migration = STATIC_MIGRATIONS[name];
|
|
140
|
+
await migration.up(db);
|
|
141
|
+
await db.insertInto("_fc_migrations").values({ name }).execute();
|
|
142
|
+
results.push({ name, status: "Success" });
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error(`Migration ${name} failed:`, err);
|
|
145
|
+
throw err;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return results;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/db/index.ts
|
|
152
|
+
var createDb = (d1) => {
|
|
153
|
+
return new Kysely3({
|
|
154
|
+
dialect: new D1Dialect({ database: d1 })
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
export {
|
|
158
|
+
runMigrations,
|
|
159
|
+
ensureUniqueSlug,
|
|
160
|
+
createDb,
|
|
161
|
+
createCollectionTable,
|
|
162
|
+
addFieldToTable,
|
|
163
|
+
FIELD_TYPE_MAP
|
|
164
|
+
};
|