create-frootkit 1.2.9 → 1.2.11

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.
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/addons/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/addons/database.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAKA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOzE,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAC7C,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA4EvF"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAKA,OAAO,EAA4C,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOzE,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAC7C,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAiFvF"}
package/dist/scaffold.js CHANGED
@@ -3,7 +3,7 @@ import * as path from 'node:path';
3
3
  import { exec } from 'node:child_process';
4
4
  import { promisify } from 'node:util';
5
5
  import { generateFrootKitTemplate, DEFAULT_GITIGNORE } from './template.js';
6
- import { applyDatabaseAddon } from './addons/database.js';
6
+ import { applyDatabaseAddon, seedPhysicalDatabase } from './addons/database.js';
7
7
  import { applyLinterAddon } from './addons/linter.js';
8
8
  import { applyAgentAddon } from './addons/agent.js';
9
9
  import { applyDockerAddon } from './addons/docker.js';
@@ -37,6 +37,10 @@ export async function scaffoldProject(options) {
37
37
  await fs.mkdir(path.dirname(fullPath), { recursive: true });
38
38
  await fs.writeFile(fullPath, files[relPath], 'utf8');
39
39
  }
40
+ // Pre-seed physical SQLite database with complete schemas and actual data
41
+ if (db !== 'none') {
42
+ seedPhysicalDatabase(targetDir, db, template);
43
+ }
40
44
  writeSpinner.stop(`Created ${filePaths.length} project files`);
41
45
  let gitInitialized = false;
42
46
  if (gitInit) {
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-frootkit",
3
- "version": "1.2.9",
3
+ "version": "1.2.11",
4
4
  "description": "Interactive CLI for scaffolding high-performance FrootKit (Next.js Alternative) applications",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,8 +5,12 @@ import {
5
5
  parseCookies,
6
6
  createSessionCookie,
7
7
  clearSessionCookie,
8
- USERS_STORE,
9
- SESSIONS_STORE,
8
+ findUserByEmail,
9
+ findUserById,
10
+ saveUser,
11
+ findSession,
12
+ saveSession,
13
+ deleteSession,
10
14
  type User,
11
15
  } from '../../../lib/auth.js';
12
16
 
@@ -21,7 +25,7 @@ export async function GET(req: Request): Promise<Response> {
21
25
  );
22
26
  }
23
27
 
24
- const session = SESSIONS_STORE.get(sessionToken);
28
+ const session = await findSession(sessionToken);
25
29
  if (!session) {
26
30
  return Response.json(
27
31
  { authenticated: false, user: null },
@@ -29,8 +33,8 @@ export async function GET(req: Request): Promise<Response> {
29
33
  );
30
34
  }
31
35
 
32
- const userRecord = Array.from(USERS_STORE.values()).find((r) => r.user.id === session.userId);
33
- if (!userRecord) {
36
+ const user = await findUserById(session.userId);
37
+ if (!user) {
34
38
  return Response.json(
35
39
  { authenticated: false, user: null },
36
40
  { status: 401 }
@@ -38,7 +42,7 @@ export async function GET(req: Request): Promise<Response> {
38
42
  }
39
43
 
40
44
  return Response.json(
41
- { authenticated: true, user: userRecord.user },
45
+ { authenticated: true, user },
42
46
  { status: 200 }
43
47
  );
44
48
  }
@@ -51,7 +55,7 @@ export async function POST(req: Request): Promise<Response> {
51
55
  const cookies = parseCookies(req.headers.get('cookie'));
52
56
  const token = cookies['froot_session'];
53
57
  if (token) {
54
- SESSIONS_STORE.delete(token);
58
+ await deleteSession(token);
55
59
  }
56
60
 
57
61
  return new Response(null, {
@@ -87,7 +91,8 @@ export async function POST(req: Request): Promise<Response> {
87
91
  }
88
92
 
89
93
  if (action === 'signup') {
90
- if (USERS_STORE.has(email)) {
94
+ const existing = await findUserByEmail(email);
95
+ if (existing) {
91
96
  return Response.json({ error: 'User already exists' }, { status: 409 });
92
97
  }
93
98
 
@@ -100,10 +105,10 @@ export async function POST(req: Request): Promise<Response> {
100
105
  createdAt: new Date().toISOString(),
101
106
  };
102
107
 
103
- USERS_STORE.set(email, { user, passwordHash });
108
+ await saveUser(user, passwordHash);
104
109
 
105
110
  const token = createSessionToken();
106
- SESSIONS_STORE.set(token, {
111
+ await saveSession({
107
112
  id: 'sess_' + Math.random().toString(36).substring(2, 9),
108
113
  token,
109
114
  userId: user.id,
@@ -120,7 +125,7 @@ export async function POST(req: Request): Promise<Response> {
120
125
  }
121
126
 
122
127
  // Default: Login
123
- const record = USERS_STORE.get(email);
128
+ const record = await findUserByEmail(email);
124
129
  if (!record) {
125
130
  return Response.json({ error: 'Invalid credentials' }, { status: 401 });
126
131
  }
@@ -131,7 +136,7 @@ export async function POST(req: Request): Promise<Response> {
131
136
  }
132
137
 
133
138
  const token = createSessionToken();
134
- SESSIONS_STORE.set(token, {
139
+ await saveSession({
135
140
  id: 'sess_' + Math.random().toString(36).substring(2, 9),
136
141
  token,
137
142
  userId: record.user.id,
@@ -129,6 +129,129 @@ export function clearSessionCookie(): string {
129
129
  return 'froot_session=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0';
130
130
  }
131
131
 
132
+ /**
133
+ * Finds user record by email from DB (with fallback to in-memory).
134
+ */
135
+ export async function findUserByEmail(email: string): Promise<{ user: User; passwordHash: string } | null> {
136
+ try {
137
+ const { db } = await import('../db/index.js');
138
+ const { users } = await import('../db/schema.js');
139
+ const { eq } = await import('drizzle-orm');
140
+ const rows = await db.select().from(users).where(eq(users.email, email));
141
+ if (rows && rows.length > 0) {
142
+ const u = rows[0];
143
+ return {
144
+ user: {
145
+ id: u.id,
146
+ email: u.email,
147
+ name: u.name,
148
+ role: u.role as 'user' | 'admin',
149
+ createdAt: u.createdAt,
150
+ },
151
+ passwordHash: u.passwordHash,
152
+ };
153
+ }
154
+ } catch {}
155
+ return USERS_STORE.get(email) || null;
156
+ }
157
+
158
+ /**
159
+ * Finds user record by ID.
160
+ */
161
+ export async function findUserById(id: string): Promise<User | null> {
162
+ try {
163
+ const { db } = await import('../db/index.js');
164
+ const { users } = await import('../db/schema.js');
165
+ const { eq } = await import('drizzle-orm');
166
+ const rows = await db.select().from(users).where(eq(users.id, id));
167
+ if (rows && rows.length > 0) {
168
+ const u = rows[0];
169
+ return {
170
+ id: u.id,
171
+ email: u.email,
172
+ name: u.name,
173
+ role: u.role as 'user' | 'admin',
174
+ createdAt: u.createdAt,
175
+ };
176
+ }
177
+ } catch {}
178
+ for (const entry of USERS_STORE.values()) {
179
+ if (entry.user.id === id) return entry.user;
180
+ }
181
+ return null;
182
+ }
183
+
184
+ /**
185
+ * Saves a new user to DB and in-memory store.
186
+ */
187
+ export async function saveUser(user: User, passwordHash: string): Promise<void> {
188
+ try {
189
+ const { db } = await import('../db/index.js');
190
+ const { users } = await import('../db/schema.js');
191
+ await db.insert(users).values({
192
+ id: user.id,
193
+ email: user.email,
194
+ name: user.name,
195
+ role: user.role,
196
+ passwordHash,
197
+ createdAt: user.createdAt,
198
+ });
199
+ } catch {}
200
+ USERS_STORE.set(user.email, { user, passwordHash });
201
+ }
202
+
203
+ /**
204
+ * Saves a session token to DB and in-memory store.
205
+ */
206
+ export async function saveSession(session: Session): Promise<void> {
207
+ try {
208
+ const { db } = await import('../db/index.js');
209
+ const { sessions } = await import('../db/schema.js');
210
+ await db.insert(sessions).values({
211
+ id: session.id,
212
+ token: session.token,
213
+ userId: session.userId,
214
+ expiresAt: session.expiresAt,
215
+ });
216
+ } catch {}
217
+ SESSIONS_STORE.set(session.token, session);
218
+ }
219
+
220
+ /**
221
+ * Finds session by token.
222
+ */
223
+ export async function findSession(token: string): Promise<Session | null> {
224
+ try {
225
+ const { db } = await import('../db/index.js');
226
+ const { sessions } = await import('../db/schema.js');
227
+ const { eq } = await import('drizzle-orm');
228
+ const rows = await db.select().from(sessions).where(eq(sessions.token, token));
229
+ if (rows && rows.length > 0) {
230
+ const s = rows[0];
231
+ return {
232
+ id: s.id,
233
+ token: s.token,
234
+ userId: s.userId,
235
+ expiresAt: s.expiresAt,
236
+ };
237
+ }
238
+ } catch {}
239
+ return SESSIONS_STORE.get(token) || null;
240
+ }
241
+
242
+ /**
243
+ * Deletes a session by token.
244
+ */
245
+ export async function deleteSession(token: string): Promise<void> {
246
+ try {
247
+ const { db } = await import('../db/index.js');
248
+ const { sessions } = await import('../db/schema.js');
249
+ const { eq } = await import('drizzle-orm');
250
+ await db.delete(sessions).where(eq(sessions.token, token));
251
+ } catch {}
252
+ SESSIONS_STORE.delete(token);
253
+ }
254
+
132
255
  // Pre-populate demo user: admin@frootkit.dev / password123
133
256
  (async () => {
134
257
  const demoHash = await hashPassword('password123');
@@ -1,36 +1,12 @@
1
- import { POSTS } from '../../../data/posts.js';
2
-
3
- export async function GET(req: Request): Promise<Response> {
4
- const url = new URL(req.url);
5
- const tag = url.searchParams.get('tag');
6
- const q = url.searchParams.get('q')?.toLowerCase();
7
-
8
- let results = [...POSTS];
9
-
10
- if (tag) {
11
- results = results.filter((p) => p.tags.some((t) => t.toLowerCase() === tag.toLowerCase()));
12
- }
13
-
14
- if (q) {
15
- results = results.filter(
16
- (p) =>
17
- p.title.toLowerCase().includes(q) ||
18
- p.summary.toLowerCase().includes(q) ||
19
- p.category.toLowerCase().includes(q)
20
- );
21
- }
22
-
23
- return Response.json(
24
- {
25
- posts: results,
26
- total: results.length,
27
- timestamp: new Date().toISOString(),
1
+ import { getPosts } from '../../../data/posts.js';
2
+
3
+ export async function GET(): Promise<Response> {
4
+ const posts = await getPosts();
5
+ return Response.json(posts, {
6
+ status: 200,
7
+ headers: {
8
+ 'content-type': 'application/json',
9
+ 'cache-control': 'public, max-age=60',
28
10
  },
29
- {
30
- status: 200,
31
- headers: {
32
- 'content-type': 'application/json',
33
- },
34
- }
35
- );
11
+ });
36
12
  }
@@ -1,4 +1,4 @@
1
- import { POSTS } from '../../../data/posts.js';
1
+ import { getPostBySlug, POSTS } from '../../../data/posts.js';
2
2
  import { Navbar } from '../../../components/Navbar.js';
3
3
  import { Footer } from '../../../components/Footer.js';
4
4
 
@@ -6,9 +6,9 @@ export async function generateStaticParams() {
6
6
  return POSTS.map((post) => ({ slug: post.slug }));
7
7
  }
8
8
 
9
- export default function BlogPostPage({ params }: { params?: { slug?: string } }) {
9
+ export default async function BlogPostPage({ params }: { params?: { slug?: string } }) {
10
10
  const slug = params?.slug || 'welcome-to-frootkit';
11
- const post = POSTS.find((p) => p.slug === slug) || POSTS[0]!;
11
+ const post = (await getPostBySlug(slug)) || POSTS[0]!;
12
12
 
13
13
  return (
14
14
  <div class="min-h-screen bg-[#08090a] text-zinc-100 flex flex-col justify-between selection:bg-indigo-500/20 selection:text-indigo-300">
@@ -1,4 +1,4 @@
1
- import { POSTS } from '../data/posts.js';
1
+ import { getPosts, POSTS } from '../data/posts.js';
2
2
  import { Navbar } from '../components/Navbar.js';
3
3
  import { PostCard } from '../components/PostCard.js';
4
4
  import { Newsletter } from '../components/Newsletter.js';
@@ -9,8 +9,9 @@ export const metadata = {
9
9
  description: 'Deep dives into Preact SSR, fullstack TypeScript, and native styling.',
10
10
  };
11
11
 
12
- export default function BlogHomePage() {
13
- const [featuredPost, ...recentPosts] = POSTS;
12
+ export default async function BlogHomePage() {
13
+ const allPosts = await getPosts();
14
+ const [featuredPost, ...recentPosts] = allPosts;
14
15
 
15
16
  return (
16
17
  <div class="min-h-screen bg-[#08090a] text-zinc-100 flex flex-col justify-between selection:bg-indigo-500/20 selection:text-indigo-300">
@@ -73,3 +73,51 @@ export const POSTS: Post[] = [
73
73
  ],
74
74
  },
75
75
  ];
76
+
77
+ /**
78
+ * Returns all articles, querying the database if configured or falling back to static POSTS.
79
+ */
80
+ export async function getPosts(): Promise<Post[]> {
81
+ try {
82
+ const { db } = await import('../db/index.js');
83
+ const { posts, authors } = await import('../db/schema.js');
84
+ const dbPosts = await db.select().from(posts);
85
+ if (dbPosts && dbPosts.length > 0) {
86
+ const allAuthors = await db.select().from(authors);
87
+ const authorMap = new Map(allAuthors.map((a: any) => [a.id, a]));
88
+ return dbPosts.map((p: any) => {
89
+ const auth = authorMap.get(p.authorId) || {
90
+ name: 'Alex Rivera',
91
+ role: 'Staff Systems Architect',
92
+ avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&h=100&fit=crop&crop=faces',
93
+ };
94
+ return {
95
+ slug: p.slug,
96
+ title: p.title,
97
+ summary: p.summary,
98
+ date: 'Recent',
99
+ readTime: p.readTime,
100
+ category: p.category,
101
+ author: {
102
+ name: auth.name,
103
+ role: auth.role,
104
+ avatar: auth.avatar,
105
+ },
106
+ tags: ['FrootKit', 'Architecture', 'TypeScript'],
107
+ content: typeof p.content === 'string' ? JSON.parse(p.content) : p.content,
108
+ };
109
+ });
110
+ }
111
+ } catch {
112
+ // Database not attached, fallback
113
+ }
114
+ return POSTS;
115
+ }
116
+
117
+ /**
118
+ * Returns a single article by slug.
119
+ */
120
+ export async function getPostBySlug(slug: string): Promise<Post | undefined> {
121
+ const allPosts = await getPosts();
122
+ return allPosts.find((p) => p.slug === slug);
123
+ }
@@ -1,28 +1,12 @@
1
- import { PRODUCTS } from '../../../data/products.js';
2
-
3
- export async function GET(req: Request): Promise<Response> {
4
- const url = new URL(req.url);
5
- const category = url.searchParams.get('category');
6
- const limit = Number(url.searchParams.get('limit') || 20);
7
-
8
- let results = [...PRODUCTS];
9
- if (category) {
10
- results = results.filter((p) => p.category.toLowerCase() === category.toLowerCase());
11
- }
12
-
13
- results = results.slice(0, limit);
14
-
15
- return Response.json(
16
- {
17
- products: results,
18
- total: results.length,
19
- timestamp: new Date().toISOString(),
1
+ import { getProducts } from '../../../data/products.js';
2
+
3
+ export async function GET(): Promise<Response> {
4
+ const products = await getProducts();
5
+ return Response.json(products, {
6
+ status: 200,
7
+ headers: {
8
+ 'content-type': 'application/json',
9
+ 'cache-control': 'public, max-age=60',
20
10
  },
21
- {
22
- status: 200,
23
- headers: {
24
- 'content-type': 'application/json',
25
- },
26
- }
27
- );
11
+ });
28
12
  }
@@ -1,4 +1,4 @@
1
- import { PRODUCTS } from '../data/products.js';
1
+ import { getProducts, PRODUCTS } from '../data/products.js';
2
2
  import { Navbar } from '../components/Navbar.js';
3
3
  import { ProductCard } from '../components/ProductCard.js';
4
4
  import { Footer } from '../components/Footer.js';
@@ -8,7 +8,8 @@ export const metadata = {
8
8
  description: 'Precision mechanical keyboards, studio audio, and ergonomic setups.',
9
9
  };
10
10
 
11
- export default function StoreHomePage() {
11
+ export default async function StoreHomePage() {
12
+ const products = await getProducts();
12
13
  const categories = ['All Products', 'Keyboards', 'Audio', 'Peripherals', 'Desk Setup', 'Accessories'];
13
14
 
14
15
  return (
@@ -55,12 +56,12 @@ export default function StoreHomePage() {
55
56
  Featured Instruments
56
57
  </h2>
57
58
  <span class="text-xs text-zinc-500 tabular-nums">
58
- {PRODUCTS.length} products available
59
+ {products.length} products available
59
60
  </span>
60
61
  </div>
61
62
 
62
63
  <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
63
- {PRODUCTS.map((product) => (
64
+ {products.map((product) => (
64
65
  <ProductCard key={product.id} product={product} />
65
66
  ))}
66
67
  </div>
@@ -1,4 +1,4 @@
1
- import { PRODUCTS } from '../../../data/products.js';
1
+ import { getProduct, PRODUCTS } from '../../../data/products.js';
2
2
  import { Navbar } from '../../../components/Navbar.js';
3
3
  import { Footer } from '../../../components/Footer.js';
4
4
 
@@ -6,9 +6,9 @@ export async function generateStaticParams() {
6
6
  return PRODUCTS.map((p) => ({ id: p.id }));
7
7
  }
8
8
 
9
- export default function ProductDetailPage({ params }: { params?: { id?: string } }) {
9
+ export default async function ProductDetailPage({ params }: { params?: { id?: string } }) {
10
10
  const id = params?.id || 'nordic-mechanical-keyboard';
11
- const product = PRODUCTS.find((p) => p.id === id) || PRODUCTS[0]!;
11
+ const product = (await getProduct(id)) || PRODUCTS[0]!;
12
12
 
13
13
  return (
14
14
  <div class="min-h-screen bg-[#090a0f] text-zinc-100 flex flex-col justify-between selection:bg-white/20 selection:text-white">
@@ -19,8 +19,7 @@ export const PRODUCTS: Product[] = [
19
19
  category: 'Keyboards',
20
20
  rating: 4.9,
21
21
  reviewsCount: 128,
22
- // Clean minimal anodized aluminum keyboard
23
- image: 'https://images.unsplash.com/photo-1587829741301-dc798b83add3?w=800&auto=format&fit=crop&q=80',
22
+ image: 'https://images.unsplash.com/photo-1595225476474-87563907a212?w=800&auto=format&fit=crop&q=80',
24
23
  description: 'Aircraft-grade CNC anodized aluminum chassis with custom factory-lubed linear switches and sound-dampening silicone base.',
25
24
  features: ['Hot-swappable switches', 'Bluetooth 5.3 & 2.4GHz wireless', 'CNC Aluminum Unibody', 'PBT Dye-Sub Keycaps'],
26
25
  inStock: true,
@@ -32,8 +31,7 @@ export const PRODUCTS: Product[] = [
32
31
  category: 'Audio',
33
32
  rating: 4.8,
34
33
  reviewsCount: 94,
35
- // Clean matte dark studio headphones on neutral background (no bright yellow!)
36
- image: 'https://images.unsplash.com/photo-1546435770-a3e426bf472b?w=800&auto=format&fit=crop&q=80',
34
+ image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=800&auto=format&fit=crop&q=80',
37
35
  description: 'Precision 50mm dynamic drivers engineered for ultra-flat frequency response, critical mixing, and executive focus.',
38
36
  features: ['Planar Magnetic Drivers', 'Memory Foam Cushions', 'Detachable Braided Cable', 'Hard Travel Case'],
39
37
  inStock: true,
@@ -45,7 +43,6 @@ export const PRODUCTS: Product[] = [
45
43
  category: 'Peripherals',
46
44
  rating: 4.7,
47
45
  reviewsCount: 210,
48
- // Matte dark ergonomic mouse on neutral slate background
49
46
  image: 'https://images.unsplash.com/photo-1615663245857-ac93bb7c39e7?w=800&auto=format&fit=crop&q=80',
50
47
  description: 'Sculpted ergonomic contour designed to eliminate wrist fatigue during extended development sessions.',
51
48
  features: ['26,000 DPI Optical Sensor', '70-day Battery Life', 'Infinite Magnetic Scroll Wheel', 'Silent Click Switches'],
@@ -58,8 +55,7 @@ export const PRODUCTS: Product[] = [
58
55
  category: 'Desk Setup',
59
56
  rating: 5.0,
60
57
  reviewsCount: 62,
61
- // Clean handcrafted wooden desk setup
62
- image: 'https://images.unsplash.com/photo-1593062096033-9a26b09da705?w=800&auto=format&fit=crop&q=80',
58
+ image: 'https://images.unsplash.com/photo-1586775490184-b79f0621891f?w=800&auto=format&fit=crop&q=80',
63
59
  description: 'Handcrafted from single-origin American walnut with cork feet and an integrated aluminum tray for desk accessories.',
64
60
  features: ['Sustainable American Walnut', 'Holds up to 60 lbs', 'Integrated Cable Troughs', 'Hand-finished Matte Oil'],
65
61
  inStock: true,
@@ -71,8 +67,7 @@ export const PRODUCTS: Product[] = [
71
67
  category: 'Desk Setup',
72
68
  rating: 4.6,
73
69
  reviewsCount: 175,
74
- // Clean desk surface mat
75
- image: 'https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=800&auto=format&fit=crop&q=80',
70
+ image: 'https://images.unsplash.com/photo-1581291518857-4e27b48ff24e?w=800&auto=format&fit=crop&q=80',
76
71
  description: 'Water-resistant, stain-proof premium desk surface that provides effortless mouse tracking and protects your workspace.',
77
72
  features: ['Water-resistant PU Leather', 'Anti-slip Suede Backing', '90cm x 45cm Large Size', 'Easy to Clean'],
78
73
  inStock: true,
@@ -84,10 +79,53 @@ export const PRODUCTS: Product[] = [
84
79
  category: 'Accessories',
85
80
  rating: 4.9,
86
81
  reviewsCount: 340,
87
- // Minimalist braided cable / tech accessory
88
- image: 'https://images.unsplash.com/photo-1622434641406-a158123450f9?w=800&auto=format&fit=crop&q=80',
82
+ image: 'https://images.unsplash.com/photo-1583863788434-e58a36330cf0?w=800&auto=format&fit=crop&q=80',
89
83
  description: 'Ultra-durable Kevlar-reinforced charging cable supporting up to 240W Power Delivery and 40Gbps Thunderbolt data transfer.',
90
84
  features: ['240W Power Delivery', 'E-Marker Smart Chip', 'Braided Kevlar Weave', '2-meter Length'],
91
85
  inStock: true,
92
86
  },
93
87
  ];
88
+
89
+ /**
90
+ * Returns all products, querying the database if configured or falling back to static PRODUCTS.
91
+ */
92
+ export async function getProducts(): Promise<Product[]> {
93
+ try {
94
+ const { db } = await import('../db/index.js');
95
+ const { products } = await import('../db/schema.js');
96
+ const rows = await db.select().from(products);
97
+ if (rows && rows.length > 0) {
98
+ return rows.map((r: any) => ({
99
+ ...r,
100
+ features: typeof r.features === 'string' ? JSON.parse(r.features) : r.features,
101
+ inStock: Boolean(r.inStock),
102
+ }));
103
+ }
104
+ } catch {
105
+ // Database not attached, fallback
106
+ }
107
+ return PRODUCTS;
108
+ }
109
+
110
+ /**
111
+ * Returns a single product by ID from the database or static store.
112
+ */
113
+ export async function getProduct(id: string): Promise<Product | undefined> {
114
+ try {
115
+ const { db } = await import('../db/index.js');
116
+ const { products } = await import('../db/schema.js');
117
+ const { eq } = await import('drizzle-orm');
118
+ const rows = await db.select().from(products).where(eq(products.id, id));
119
+ if (rows && rows.length > 0) {
120
+ const r = rows[0];
121
+ return {
122
+ ...r,
123
+ features: typeof r.features === 'string' ? JSON.parse(r.features) : r.features,
124
+ inStock: Boolean(r.inStock),
125
+ };
126
+ }
127
+ } catch {
128
+ // Database not attached, fallback
129
+ }
130
+ return PRODUCTS.find((p) => p.id === id);
131
+ }