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.
Files changed (110) hide show
  1. package/README.md +73 -0
  2. package/dist/auth/index.js +40 -0
  3. package/dist/cli/commands.js +389 -0
  4. package/dist/cli/index.js +403 -0
  5. package/dist/cli/mcp.js +209 -0
  6. package/dist/db/index.js +164 -0
  7. package/dist/index.js +17626 -0
  8. package/package.json +105 -0
  9. package/scripts/fix-api-paths.mjs +32 -0
  10. package/scripts/fix-imports.mjs +38 -0
  11. package/scripts/prefix-css.mjs +45 -0
  12. package/src/api/lib/cache.ts +45 -0
  13. package/src/api/lib/response.ts +40 -0
  14. package/src/api/middlewares/auth.ts +186 -0
  15. package/src/api/middlewares/cors.ts +10 -0
  16. package/src/api/middlewares/rbac.ts +85 -0
  17. package/src/api/routes/auth.ts +377 -0
  18. package/src/api/routes/collections.ts +205 -0
  19. package/src/api/routes/content.ts +175 -0
  20. package/src/api/routes/device.ts +160 -0
  21. package/src/api/routes/magic.ts +150 -0
  22. package/src/api/routes/mcp.ts +273 -0
  23. package/src/api/routes/oauth.ts +160 -0
  24. package/src/api/routes/settings.ts +43 -0
  25. package/src/api/routes/setup.ts +307 -0
  26. package/src/api/routes/tokens.ts +80 -0
  27. package/src/api/schemas/auth.ts +15 -0
  28. package/src/api/schemas/index.ts +51 -0
  29. package/src/api/schemas/tokens.ts +24 -0
  30. package/src/auth/index.ts +28 -0
  31. package/src/cli/commands.ts +217 -0
  32. package/src/cli/index.ts +21 -0
  33. package/src/cli/mcp.ts +210 -0
  34. package/src/cli/tests/cli.test.ts +40 -0
  35. package/src/cli/tests/create.test.ts +87 -0
  36. package/src/client/FlareAdminRouter.tsx +47 -0
  37. package/src/client/app.tsx +175 -0
  38. package/src/client/components/app-sidebar.tsx +227 -0
  39. package/src/client/components/collection-modal.tsx +215 -0
  40. package/src/client/components/content-list.tsx +247 -0
  41. package/src/client/components/dynamic-form.tsx +190 -0
  42. package/src/client/components/field-modal.tsx +221 -0
  43. package/src/client/components/settings/api-token-section.tsx +400 -0
  44. package/src/client/components/settings/general-section.tsx +224 -0
  45. package/src/client/components/settings/security-section.tsx +154 -0
  46. package/src/client/components/settings/seo-section.tsx +200 -0
  47. package/src/client/components/settings/signup-section.tsx +257 -0
  48. package/src/client/components/ui/accordion.tsx +78 -0
  49. package/src/client/components/ui/avatar.tsx +107 -0
  50. package/src/client/components/ui/badge.tsx +52 -0
  51. package/src/client/components/ui/button.tsx +60 -0
  52. package/src/client/components/ui/card.tsx +103 -0
  53. package/src/client/components/ui/checkbox.tsx +27 -0
  54. package/src/client/components/ui/collapsible.tsx +19 -0
  55. package/src/client/components/ui/dialog.tsx +162 -0
  56. package/src/client/components/ui/icon-picker.tsx +485 -0
  57. package/src/client/components/ui/icons-data.ts +8476 -0
  58. package/src/client/components/ui/input.tsx +20 -0
  59. package/src/client/components/ui/label.tsx +20 -0
  60. package/src/client/components/ui/popover.tsx +91 -0
  61. package/src/client/components/ui/select.tsx +204 -0
  62. package/src/client/components/ui/separator.tsx +23 -0
  63. package/src/client/components/ui/sheet.tsx +141 -0
  64. package/src/client/components/ui/sidebar.tsx +722 -0
  65. package/src/client/components/ui/skeleton.tsx +13 -0
  66. package/src/client/components/ui/sonner.tsx +47 -0
  67. package/src/client/components/ui/switch.tsx +30 -0
  68. package/src/client/components/ui/table.tsx +116 -0
  69. package/src/client/components/ui/tabs.tsx +80 -0
  70. package/src/client/components/ui/textarea.tsx +18 -0
  71. package/src/client/components/ui/tooltip.tsx +68 -0
  72. package/src/client/hooks/use-mobile.ts +19 -0
  73. package/src/client/index.css +149 -0
  74. package/src/client/index.ts +7 -0
  75. package/src/client/layouts/admin-layout.tsx +93 -0
  76. package/src/client/layouts/settings-layout.tsx +104 -0
  77. package/src/client/lib/api.ts +72 -0
  78. package/src/client/lib/utils.ts +6 -0
  79. package/src/client/main.tsx +10 -0
  80. package/src/client/pages/collection-detail.tsx +634 -0
  81. package/src/client/pages/collections.tsx +180 -0
  82. package/src/client/pages/dashboard.tsx +133 -0
  83. package/src/client/pages/device.tsx +66 -0
  84. package/src/client/pages/document-detail-page.tsx +139 -0
  85. package/src/client/pages/documents-page.tsx +103 -0
  86. package/src/client/pages/login.tsx +345 -0
  87. package/src/client/pages/settings.tsx +65 -0
  88. package/src/client/pages/setup.tsx +129 -0
  89. package/src/client/pages/signup.tsx +188 -0
  90. package/src/client/store/auth.ts +30 -0
  91. package/src/client/store/collections.ts +13 -0
  92. package/src/client/store/config.ts +12 -0
  93. package/src/client/store/fetcher.ts +30 -0
  94. package/src/client/store/router.ts +95 -0
  95. package/src/client/store/schema.ts +39 -0
  96. package/src/client/store/settings.ts +31 -0
  97. package/src/client/types.ts +34 -0
  98. package/src/db/dynamic.ts +70 -0
  99. package/src/db/index.ts +16 -0
  100. package/src/db/migrations/001_initial_schema.ts +57 -0
  101. package/src/db/migrations/002_auth_tables.ts +84 -0
  102. package/src/db/migrator.ts +61 -0
  103. package/src/db/schema.ts +142 -0
  104. package/src/index.ts +12 -0
  105. package/src/server/index.ts +66 -0
  106. package/src/types.ts +20 -0
  107. package/style.css.d.ts +8 -0
  108. package/tests/css.test.ts +21 -0
  109. package/tests/modular.test.ts +29 -0
  110. package/tsconfig.json +10 -0
@@ -0,0 +1,104 @@
1
+ import { useStore } from '@nanostores/react';
2
+ import { $router, navigate } from '../store/router';
3
+ import {
4
+ Settings as SettingsIcon,
5
+ Search as SearchIcon,
6
+ ShieldCheck as SecurityIcon,
7
+ Globe as SeoIcon,
8
+ Users as SignupIcon,
9
+ Key as ApiIcon,
10
+ ChevronRight as ChevronRightIcon,
11
+ } from 'lucide-react';
12
+ import { cn } from '../lib/utils';
13
+ import { Button } from '../components/ui/button';
14
+
15
+ export function SettingsSidebar() {
16
+ const page = useStore($router);
17
+
18
+ const nav = [
19
+ { label: 'General', route: 'settings_general', icon: SettingsIcon },
20
+ { label: 'SEO', route: 'settings_seo', icon: SeoIcon },
21
+ { label: 'Security', route: 'settings_security', icon: SecurityIcon },
22
+ { label: 'Signup', route: 'settings_signup', icon: SignupIcon },
23
+ { label: 'API Tokens', route: 'settings', icon: ApiIcon },
24
+ ];
25
+
26
+ return (
27
+ <div className="w-full lg:w-64 space-y-1 pr-4">
28
+ <div className="mb-8 px-3">
29
+ <h2 className="text-xl font-bold tracking-tight text-foreground flex items-center gap-2">
30
+ Settings
31
+ </h2>
32
+ <p className="text-[10px] uppercase tracking-[0.2em] font-semibold text-muted-foreground opacity-50 mt-1">
33
+ System Configuration
34
+ </p>
35
+ </div>
36
+
37
+ <div className="space-y-1">
38
+ {nav.map((item) => {
39
+ const isActive = page?.route === item.route;
40
+ return (
41
+ <Button
42
+ key={item.route}
43
+ variant="ghost"
44
+ className={cn(
45
+ 'w-full justify-start gap-3 h-10 px-3 text-xs font-semibold tracking-wide transition-all group relative overflow-hidden',
46
+ isActive
47
+ ? 'bg-primary/5 text-primary'
48
+ : 'text-muted-foreground hover:bg-accent hover:text-foreground',
49
+ )}
50
+ onClick={() => navigate(item.route)}
51
+ >
52
+ {isActive && (
53
+ <div className="absolute left-0 top-1/2 -translate-y-1/2 w-0.5 h-4 bg-primary rounded-full" />
54
+ )}
55
+ <item.icon
56
+ className={cn(
57
+ 'size-4 transition-colors',
58
+ isActive
59
+ ? 'text-primary'
60
+ : 'opacity-40 group-hover:opacity-100',
61
+ )}
62
+ />
63
+ {item.label}
64
+ <ChevronRightIcon
65
+ className={cn(
66
+ 'size-3 ml-auto opacity-0 -translate-x-1 transition-all',
67
+ isActive && 'opacity-20 translate-x-0',
68
+ )}
69
+ />
70
+ </Button>
71
+ );
72
+ })}
73
+ </div>
74
+ </div>
75
+ );
76
+ }
77
+
78
+ export function SettingsLayout({
79
+ children,
80
+ title,
81
+ subtitle,
82
+ }: {
83
+ children: React.ReactNode;
84
+ title: string;
85
+ subtitle: string;
86
+ }) {
87
+ return (
88
+ <div className="max-w-container mx-auto py-8 px-6 flex flex-col lg:flex-row gap-12">
89
+ <SettingsSidebar />
90
+ <div className="flex-1 space-y-8">
91
+ <div className="border-b border-border/50 pb-8 relative">
92
+ <h1 className="text-3xl font-black tracking-tight text-foreground">
93
+ {title}
94
+ </h1>
95
+ <p className="text-sm text-muted-foreground mt-2 font-medium">
96
+ {subtitle}
97
+ </p>
98
+ <div className="absolute -bottom-px left-0 w-12 h-px bg-primary" />
99
+ </div>
100
+ <div className="mt-8">{children}</div>
101
+ </div>
102
+ </div>
103
+ );
104
+ }
@@ -0,0 +1,72 @@
1
+ import ky from 'ky';
2
+ import { $auth } from '../store/auth';
3
+ import { navigate } from '../store/router';
4
+ import { $apiBaseUrl } from '../store/config';
5
+
6
+ /**
7
+ * Core API instance pre-configured with:
8
+ * 1. Automatic Bearer Token injection via beforeRequest hook
9
+ * 2. Automatic Zero-Config redirect via afterResponse hook
10
+ * 3. Standard error handling for non-2xx responses (built-in ky behavior)
11
+ */
12
+ export const api = ky.create({
13
+ prefix: $apiBaseUrl.get(),
14
+ hooks: {
15
+ beforeRequest: [
16
+ ({ request }) => {
17
+ const { token } = $auth.get();
18
+ if (token) {
19
+ request.headers.set('Authorization', `Bearer ${token}`);
20
+ }
21
+ }
22
+ ],
23
+ afterResponse: [
24
+ async ({ request, response }) => {
25
+ // Intercept Unauthorized (Session Expired)
26
+ if (response.status === 401 && !request.url.includes('/auth/')) {
27
+ $auth.set({ token: null, user: null });
28
+ if (!request.url.includes('/login')) {
29
+ navigate('login');
30
+ }
31
+ }
32
+
33
+ // Intercept Setup Required (Zero-Config flow)
34
+ if (response.status === 403) {
35
+ try {
36
+ const data: any = await response.clone().json();
37
+ if (data.code === 'SETUP_REQUIRED' && !request.url.includes('/setup')) {
38
+ navigate('setup');
39
+ }
40
+ } catch {
41
+ // Not JSON or parse error - ignore
42
+ }
43
+ }
44
+ }
45
+ ]
46
+ }
47
+ });
48
+
49
+ /**
50
+ * A legacy wrapper for components still expecting a standard Response object from apiFetch.
51
+ * Uses the ky 'api' instance under the hood.
52
+ *
53
+ * @deprecated Use the 'api' instance directly (e.g., api.get().json()) for new code.
54
+ */
55
+ export async function apiFetch(path: string, options: RequestInit = {}) {
56
+ // ky handles most RequestInit options directly
57
+ const { headers, ...rest } = options;
58
+
59
+ // Cast method to something ky accepts (strict string types)
60
+ const method = (options.method?.toLowerCase() || 'get') as any;
61
+
62
+ // We cast to any to allow the caller to treat it as a standard fetch Response
63
+ // which has .json(): Promise<any> instead of Promise<unknown>
64
+ return api(path, {
65
+ method,
66
+ headers,
67
+ ...rest,
68
+ // ky throws on non-2xx by default, but standard fetch/apiFetch does not.
69
+ // We set throwHttpErrors: false to maintain backward compatibility during the transition.
70
+ throwHttpErrors: false,
71
+ }) as any;
72
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import App from './app';
4
+ import './index.css';
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')!).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ );