@l4yercak3/cli 1.2.16 → 1.2.18
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/docs/INTEGRATION_PATHS_ARCHITECTURE.md +1543 -0
- package/package.json +1 -1
- package/src/commands/spread.js +101 -6
- package/src/detectors/database-detector.js +245 -0
- package/src/detectors/index.js +17 -4
- package/src/generators/api-only/client.js +683 -0
- package/src/generators/api-only/index.js +96 -0
- package/src/generators/api-only/types.js +618 -0
- package/src/generators/api-only/webhooks.js +377 -0
- package/src/generators/index.js +88 -2
- package/src/generators/mcp-guide-generator.js +256 -0
- package/src/generators/quickstart/components/index.js +1699 -0
- package/src/generators/quickstart/database/convex.js +1257 -0
- package/src/generators/quickstart/database/index.js +34 -0
- package/src/generators/quickstart/database/supabase.js +1132 -0
- package/src/generators/quickstart/hooks/index.js +1047 -0
- package/src/generators/quickstart/index.js +151 -0
- package/src/generators/quickstart/pages/index.js +1466 -0
- package/src/mcp/registry/domains/benefits.js +798 -0
- package/src/mcp/registry/index.js +2 -0
- package/tests/database-detector.test.js +221 -0
- package/tests/generators-index.test.js +215 -3
|
@@ -0,0 +1,1132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supabase Database Generator
|
|
3
|
+
* Generates Supabase schema, migrations, and client for L4YERCAK3 integration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { ensureDir, writeFileWithBackup, checkFileOverwrite } = require('../../../utils/file-utils');
|
|
9
|
+
|
|
10
|
+
class SupabaseGenerator {
|
|
11
|
+
/**
|
|
12
|
+
* Generate Supabase database files
|
|
13
|
+
* @param {Object} options - Generation options
|
|
14
|
+
* @returns {Promise<Object>} - Generated file paths
|
|
15
|
+
*/
|
|
16
|
+
async generate(options) {
|
|
17
|
+
const { projectPath, features = [] } = options;
|
|
18
|
+
|
|
19
|
+
const results = {
|
|
20
|
+
schema: null,
|
|
21
|
+
client: null,
|
|
22
|
+
types: null,
|
|
23
|
+
middleware: null,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const supabaseDir = path.join(projectPath, 'supabase');
|
|
27
|
+
const migrationsDir = path.join(supabaseDir, 'migrations');
|
|
28
|
+
ensureDir(supabaseDir);
|
|
29
|
+
ensureDir(migrationsDir);
|
|
30
|
+
|
|
31
|
+
// Generate initial migration
|
|
32
|
+
results.schema = await this.generateMigration(migrationsDir, features);
|
|
33
|
+
|
|
34
|
+
// Generate Supabase client
|
|
35
|
+
results.client = await this.generateClient(projectPath, options);
|
|
36
|
+
|
|
37
|
+
// Generate TypeScript types
|
|
38
|
+
if (options.isTypeScript) {
|
|
39
|
+
results.types = await this.generateTypes(projectPath);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Generate middleware for auth
|
|
43
|
+
if (features.includes('oauth')) {
|
|
44
|
+
results.middleware = await this.generateMiddleware(projectPath, options);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async generateMigration(migrationsDir, _features) {
|
|
51
|
+
const timestamp = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14);
|
|
52
|
+
const outputPath = path.join(migrationsDir, `${timestamp}_l4yercak3_schema.sql`);
|
|
53
|
+
|
|
54
|
+
const action = await checkFileOverwrite(outputPath);
|
|
55
|
+
if (action === 'skip') {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const content = `-- L4YERCAK3 Integration Schema
|
|
60
|
+
-- Auto-generated by @l4yercak3/cli
|
|
61
|
+
--
|
|
62
|
+
-- This schema follows the ontology-first pattern, mirroring
|
|
63
|
+
-- L4YERCAK3's universal object structure for seamless sync.
|
|
64
|
+
|
|
65
|
+
-- Enable required extensions
|
|
66
|
+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
67
|
+
|
|
68
|
+
-- ============================================
|
|
69
|
+
-- ONTOLOGY: Universal object storage
|
|
70
|
+
-- ============================================
|
|
71
|
+
|
|
72
|
+
CREATE TYPE sync_status AS ENUM (
|
|
73
|
+
'synced',
|
|
74
|
+
'pending_push',
|
|
75
|
+
'pending_pull',
|
|
76
|
+
'conflict',
|
|
77
|
+
'local_only'
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
CREATE TABLE objects (
|
|
81
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
82
|
+
l4yercak3_id TEXT UNIQUE,
|
|
83
|
+
organization_id TEXT NOT NULL,
|
|
84
|
+
|
|
85
|
+
-- Core fields
|
|
86
|
+
type TEXT NOT NULL,
|
|
87
|
+
subtype TEXT,
|
|
88
|
+
name TEXT NOT NULL,
|
|
89
|
+
status TEXT NOT NULL,
|
|
90
|
+
|
|
91
|
+
-- Type-specific data
|
|
92
|
+
custom_properties JSONB DEFAULT '{}',
|
|
93
|
+
|
|
94
|
+
-- Sync tracking
|
|
95
|
+
sync_status sync_status DEFAULT 'local_only',
|
|
96
|
+
synced_at TIMESTAMPTZ,
|
|
97
|
+
local_version INTEGER DEFAULT 1,
|
|
98
|
+
remote_version INTEGER,
|
|
99
|
+
|
|
100
|
+
-- Timestamps
|
|
101
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
102
|
+
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
103
|
+
deleted_at TIMESTAMPTZ
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
-- Indexes for objects
|
|
107
|
+
CREATE INDEX idx_objects_l4yercak3_id ON objects(l4yercak3_id);
|
|
108
|
+
CREATE INDEX idx_objects_type ON objects(type);
|
|
109
|
+
CREATE INDEX idx_objects_type_status ON objects(type, status);
|
|
110
|
+
CREATE INDEX idx_objects_type_subtype ON objects(type, subtype);
|
|
111
|
+
CREATE INDEX idx_objects_sync_status ON objects(sync_status);
|
|
112
|
+
CREATE INDEX idx_objects_organization ON objects(organization_id);
|
|
113
|
+
CREATE INDEX idx_objects_updated ON objects(updated_at DESC);
|
|
114
|
+
|
|
115
|
+
-- Object relationships
|
|
116
|
+
CREATE TABLE object_links (
|
|
117
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
118
|
+
l4yercak3_id TEXT,
|
|
119
|
+
from_object_id UUID REFERENCES objects(id) ON DELETE CASCADE,
|
|
120
|
+
to_object_id UUID REFERENCES objects(id) ON DELETE CASCADE,
|
|
121
|
+
link_type TEXT NOT NULL,
|
|
122
|
+
metadata JSONB,
|
|
123
|
+
sync_status TEXT DEFAULT 'local_only',
|
|
124
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
CREATE INDEX idx_object_links_from ON object_links(from_object_id);
|
|
128
|
+
CREATE INDEX idx_object_links_to ON object_links(to_object_id);
|
|
129
|
+
CREATE INDEX idx_object_links_from_type ON object_links(from_object_id, link_type);
|
|
130
|
+
|
|
131
|
+
-- ============================================
|
|
132
|
+
-- AUTHENTICATION: Local user management
|
|
133
|
+
-- ============================================
|
|
134
|
+
|
|
135
|
+
CREATE TABLE frontend_users (
|
|
136
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
137
|
+
l4yercak3_contact_id TEXT,
|
|
138
|
+
l4yercak3_frontend_user_id TEXT,
|
|
139
|
+
organization_id TEXT NOT NULL,
|
|
140
|
+
|
|
141
|
+
-- Core identity
|
|
142
|
+
email TEXT UNIQUE NOT NULL,
|
|
143
|
+
email_verified BOOLEAN DEFAULT FALSE,
|
|
144
|
+
name TEXT,
|
|
145
|
+
first_name TEXT,
|
|
146
|
+
last_name TEXT,
|
|
147
|
+
image TEXT,
|
|
148
|
+
phone TEXT,
|
|
149
|
+
|
|
150
|
+
-- Local auth
|
|
151
|
+
password_hash TEXT,
|
|
152
|
+
|
|
153
|
+
-- OAuth accounts stored as JSONB array
|
|
154
|
+
oauth_accounts JSONB DEFAULT '[]',
|
|
155
|
+
|
|
156
|
+
-- App-specific
|
|
157
|
+
role TEXT DEFAULT 'user',
|
|
158
|
+
preferences JSONB DEFAULT '{}',
|
|
159
|
+
|
|
160
|
+
-- Sync
|
|
161
|
+
sync_status TEXT DEFAULT 'pending_push',
|
|
162
|
+
synced_at TIMESTAMPTZ,
|
|
163
|
+
|
|
164
|
+
-- Timestamps
|
|
165
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
166
|
+
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
167
|
+
last_login_at TIMESTAMPTZ
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
CREATE INDEX idx_frontend_users_email ON frontend_users(email);
|
|
171
|
+
CREATE INDEX idx_frontend_users_l4yercak3_contact ON frontend_users(l4yercak3_contact_id);
|
|
172
|
+
CREATE INDEX idx_frontend_users_organization ON frontend_users(organization_id);
|
|
173
|
+
|
|
174
|
+
-- Sessions
|
|
175
|
+
CREATE TABLE sessions (
|
|
176
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
177
|
+
user_id UUID REFERENCES frontend_users(id) ON DELETE CASCADE,
|
|
178
|
+
session_token TEXT UNIQUE NOT NULL,
|
|
179
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
180
|
+
user_agent TEXT,
|
|
181
|
+
ip_address TEXT,
|
|
182
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
CREATE INDEX idx_sessions_token ON sessions(session_token);
|
|
186
|
+
CREATE INDEX idx_sessions_user ON sessions(user_id);
|
|
187
|
+
|
|
188
|
+
-- ============================================
|
|
189
|
+
-- STRIPE: Local payment handling
|
|
190
|
+
-- ============================================
|
|
191
|
+
|
|
192
|
+
CREATE TABLE stripe_customers (
|
|
193
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
194
|
+
frontend_user_id UUID REFERENCES frontend_users(id) ON DELETE CASCADE,
|
|
195
|
+
stripe_customer_id TEXT UNIQUE NOT NULL,
|
|
196
|
+
l4yercak3_contact_id TEXT,
|
|
197
|
+
email TEXT NOT NULL,
|
|
198
|
+
name TEXT,
|
|
199
|
+
default_payment_method_id TEXT,
|
|
200
|
+
sync_status TEXT DEFAULT 'local_only',
|
|
201
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
CREATE INDEX idx_stripe_customers_stripe_id ON stripe_customers(stripe_customer_id);
|
|
205
|
+
CREATE INDEX idx_stripe_customers_user ON stripe_customers(frontend_user_id);
|
|
206
|
+
|
|
207
|
+
CREATE TABLE stripe_payments (
|
|
208
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
209
|
+
stripe_payment_intent_id TEXT UNIQUE NOT NULL,
|
|
210
|
+
stripe_customer_id TEXT,
|
|
211
|
+
frontend_user_id UUID REFERENCES frontend_users(id),
|
|
212
|
+
|
|
213
|
+
-- Payment details
|
|
214
|
+
amount INTEGER NOT NULL,
|
|
215
|
+
currency TEXT NOT NULL,
|
|
216
|
+
status TEXT NOT NULL,
|
|
217
|
+
payment_method TEXT,
|
|
218
|
+
|
|
219
|
+
-- What was purchased
|
|
220
|
+
metadata JSONB NOT NULL,
|
|
221
|
+
|
|
222
|
+
-- L4YERCAK3 sync
|
|
223
|
+
l4yercak3_order_id TEXT,
|
|
224
|
+
l4yercak3_invoice_id TEXT,
|
|
225
|
+
sync_status TEXT DEFAULT 'pending_push',
|
|
226
|
+
synced_at TIMESTAMPTZ,
|
|
227
|
+
|
|
228
|
+
-- Timestamps
|
|
229
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
230
|
+
completed_at TIMESTAMPTZ
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
CREATE INDEX idx_stripe_payments_stripe_id ON stripe_payments(stripe_payment_intent_id);
|
|
234
|
+
CREATE INDEX idx_stripe_payments_customer ON stripe_payments(stripe_customer_id);
|
|
235
|
+
CREATE INDEX idx_stripe_payments_user ON stripe_payments(frontend_user_id);
|
|
236
|
+
CREATE INDEX idx_stripe_payments_sync ON stripe_payments(sync_status);
|
|
237
|
+
|
|
238
|
+
CREATE TABLE stripe_subscriptions (
|
|
239
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
240
|
+
stripe_subscription_id TEXT UNIQUE NOT NULL,
|
|
241
|
+
stripe_customer_id TEXT NOT NULL,
|
|
242
|
+
frontend_user_id UUID REFERENCES frontend_users(id),
|
|
243
|
+
|
|
244
|
+
status TEXT NOT NULL,
|
|
245
|
+
price_id TEXT NOT NULL,
|
|
246
|
+
product_id TEXT NOT NULL,
|
|
247
|
+
|
|
248
|
+
current_period_start TIMESTAMPTZ NOT NULL,
|
|
249
|
+
current_period_end TIMESTAMPTZ NOT NULL,
|
|
250
|
+
cancel_at_period_end BOOLEAN DEFAULT FALSE,
|
|
251
|
+
|
|
252
|
+
l4yercak3_subscription_id TEXT,
|
|
253
|
+
sync_status TEXT DEFAULT 'local_only',
|
|
254
|
+
|
|
255
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
256
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
CREATE INDEX idx_stripe_subscriptions_stripe_id ON stripe_subscriptions(stripe_subscription_id);
|
|
260
|
+
CREATE INDEX idx_stripe_subscriptions_user ON stripe_subscriptions(frontend_user_id);
|
|
261
|
+
|
|
262
|
+
-- ============================================
|
|
263
|
+
-- SYNC: Job tracking
|
|
264
|
+
-- ============================================
|
|
265
|
+
|
|
266
|
+
CREATE TYPE sync_direction AS ENUM ('push', 'pull', 'bidirectional');
|
|
267
|
+
CREATE TYPE sync_job_status AS ENUM ('pending', 'running', 'completed', 'failed');
|
|
268
|
+
|
|
269
|
+
CREATE TABLE sync_jobs (
|
|
270
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
271
|
+
entity_type TEXT NOT NULL,
|
|
272
|
+
direction sync_direction NOT NULL,
|
|
273
|
+
status sync_job_status DEFAULT 'pending',
|
|
274
|
+
|
|
275
|
+
cursor TEXT,
|
|
276
|
+
processed_count INTEGER DEFAULT 0,
|
|
277
|
+
total_count INTEGER,
|
|
278
|
+
|
|
279
|
+
error_message TEXT,
|
|
280
|
+
error_details JSONB,
|
|
281
|
+
|
|
282
|
+
started_at TIMESTAMPTZ DEFAULT NOW(),
|
|
283
|
+
completed_at TIMESTAMPTZ
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
CREATE INDEX idx_sync_jobs_status ON sync_jobs(status);
|
|
287
|
+
CREATE INDEX idx_sync_jobs_entity ON sync_jobs(entity_type);
|
|
288
|
+
|
|
289
|
+
CREATE TABLE sync_conflicts (
|
|
290
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
291
|
+
object_id UUID REFERENCES objects(id) ON DELETE CASCADE,
|
|
292
|
+
local_version JSONB,
|
|
293
|
+
remote_version JSONB,
|
|
294
|
+
conflict_type TEXT NOT NULL,
|
|
295
|
+
resolved_at TIMESTAMPTZ,
|
|
296
|
+
resolution TEXT,
|
|
297
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
CREATE INDEX idx_sync_conflicts_object ON sync_conflicts(object_id);
|
|
301
|
+
CREATE INDEX idx_sync_conflicts_unresolved ON sync_conflicts(resolved_at) WHERE resolved_at IS NULL;
|
|
302
|
+
|
|
303
|
+
-- ============================================
|
|
304
|
+
-- TRIGGERS: Auto-update timestamps
|
|
305
|
+
-- ============================================
|
|
306
|
+
|
|
307
|
+
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
308
|
+
RETURNS TRIGGER AS $$
|
|
309
|
+
BEGIN
|
|
310
|
+
NEW.updated_at = NOW();
|
|
311
|
+
RETURN NEW;
|
|
312
|
+
END;
|
|
313
|
+
$$ LANGUAGE plpgsql;
|
|
314
|
+
|
|
315
|
+
CREATE TRIGGER objects_updated_at
|
|
316
|
+
BEFORE UPDATE ON objects
|
|
317
|
+
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
318
|
+
|
|
319
|
+
CREATE TRIGGER frontend_users_updated_at
|
|
320
|
+
BEFORE UPDATE ON frontend_users
|
|
321
|
+
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
322
|
+
|
|
323
|
+
CREATE TRIGGER stripe_subscriptions_updated_at
|
|
324
|
+
BEFORE UPDATE ON stripe_subscriptions
|
|
325
|
+
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
326
|
+
|
|
327
|
+
-- ============================================
|
|
328
|
+
-- ROW LEVEL SECURITY
|
|
329
|
+
-- ============================================
|
|
330
|
+
|
|
331
|
+
ALTER TABLE objects ENABLE ROW LEVEL SECURITY;
|
|
332
|
+
ALTER TABLE object_links ENABLE ROW LEVEL SECURITY;
|
|
333
|
+
ALTER TABLE frontend_users ENABLE ROW LEVEL SECURITY;
|
|
334
|
+
ALTER TABLE sessions ENABLE ROW LEVEL SECURITY;
|
|
335
|
+
ALTER TABLE stripe_customers ENABLE ROW LEVEL SECURITY;
|
|
336
|
+
ALTER TABLE stripe_payments ENABLE ROW LEVEL SECURITY;
|
|
337
|
+
ALTER TABLE stripe_subscriptions ENABLE ROW LEVEL SECURITY;
|
|
338
|
+
|
|
339
|
+
-- Objects: users can read/write their own organization's objects
|
|
340
|
+
CREATE POLICY objects_select ON objects
|
|
341
|
+
FOR SELECT USING (true); -- Adjust based on your auth requirements
|
|
342
|
+
|
|
343
|
+
CREATE POLICY objects_insert ON objects
|
|
344
|
+
FOR INSERT WITH CHECK (true);
|
|
345
|
+
|
|
346
|
+
CREATE POLICY objects_update ON objects
|
|
347
|
+
FOR UPDATE USING (true);
|
|
348
|
+
|
|
349
|
+
CREATE POLICY objects_delete ON objects
|
|
350
|
+
FOR DELETE USING (true);
|
|
351
|
+
|
|
352
|
+
-- Frontend users: users can read/update their own profile
|
|
353
|
+
CREATE POLICY frontend_users_select ON frontend_users
|
|
354
|
+
FOR SELECT USING (true);
|
|
355
|
+
|
|
356
|
+
CREATE POLICY frontend_users_update ON frontend_users
|
|
357
|
+
FOR UPDATE USING (auth.uid()::text = id::text);
|
|
358
|
+
|
|
359
|
+
-- Sessions: users can manage their own sessions
|
|
360
|
+
CREATE POLICY sessions_all ON sessions
|
|
361
|
+
FOR ALL USING (true);
|
|
362
|
+
|
|
363
|
+
-- Stripe: users can view their own payment data
|
|
364
|
+
CREATE POLICY stripe_customers_select ON stripe_customers
|
|
365
|
+
FOR SELECT USING (true);
|
|
366
|
+
|
|
367
|
+
CREATE POLICY stripe_payments_select ON stripe_payments
|
|
368
|
+
FOR SELECT USING (true);
|
|
369
|
+
|
|
370
|
+
CREATE POLICY stripe_subscriptions_select ON stripe_subscriptions
|
|
371
|
+
FOR SELECT USING (true);
|
|
372
|
+
`;
|
|
373
|
+
|
|
374
|
+
return writeFileWithBackup(outputPath, content, action);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async generateClient(projectPath, options) {
|
|
378
|
+
const { isTypeScript } = options;
|
|
379
|
+
|
|
380
|
+
// Determine output directory
|
|
381
|
+
let outputDir;
|
|
382
|
+
if (fs.existsSync(path.join(projectPath, 'src'))) {
|
|
383
|
+
outputDir = path.join(projectPath, 'src', 'lib', 'supabase');
|
|
384
|
+
} else {
|
|
385
|
+
outputDir = path.join(projectPath, 'lib', 'supabase');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
ensureDir(outputDir);
|
|
389
|
+
|
|
390
|
+
const extension = isTypeScript ? 'ts' : 'js';
|
|
391
|
+
const outputPath = path.join(outputDir, `client.${extension}`);
|
|
392
|
+
|
|
393
|
+
const action = await checkFileOverwrite(outputPath);
|
|
394
|
+
if (action === 'skip') {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const content = isTypeScript
|
|
399
|
+
? this.generateTypeScriptClient()
|
|
400
|
+
: this.generateJavaScriptClient();
|
|
401
|
+
|
|
402
|
+
return writeFileWithBackup(outputPath, content, action);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
generateTypeScriptClient() {
|
|
406
|
+
return `/**
|
|
407
|
+
* Supabase Client
|
|
408
|
+
* Auto-generated by @l4yercak3/cli
|
|
409
|
+
*/
|
|
410
|
+
|
|
411
|
+
import { createClient } from '@supabase/supabase-js';
|
|
412
|
+
import type { Database } from './types';
|
|
413
|
+
|
|
414
|
+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
|
415
|
+
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
|
416
|
+
|
|
417
|
+
if (!supabaseUrl || !supabaseAnonKey) {
|
|
418
|
+
throw new Error('Missing Supabase environment variables');
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Client-side Supabase client
|
|
422
|
+
export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey);
|
|
423
|
+
|
|
424
|
+
// Server-side Supabase client (with service role key)
|
|
425
|
+
export function createServerClient() {
|
|
426
|
+
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
427
|
+
if (!serviceKey) {
|
|
428
|
+
throw new Error('SUPABASE_SERVICE_ROLE_KEY is required for server operations');
|
|
429
|
+
}
|
|
430
|
+
return createClient<Database>(supabaseUrl, serviceKey);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ============ Object Helpers ============
|
|
434
|
+
|
|
435
|
+
export async function getObjects(type: string, options?: {
|
|
436
|
+
status?: string;
|
|
437
|
+
subtype?: string;
|
|
438
|
+
limit?: number;
|
|
439
|
+
}) {
|
|
440
|
+
let query = supabase
|
|
441
|
+
.from('objects')
|
|
442
|
+
.select('*')
|
|
443
|
+
.eq('type', type)
|
|
444
|
+
.is('deleted_at', null)
|
|
445
|
+
.order('updated_at', { ascending: false });
|
|
446
|
+
|
|
447
|
+
if (options?.status) {
|
|
448
|
+
query = query.eq('status', options.status);
|
|
449
|
+
}
|
|
450
|
+
if (options?.subtype) {
|
|
451
|
+
query = query.eq('subtype', options.subtype);
|
|
452
|
+
}
|
|
453
|
+
if (options?.limit) {
|
|
454
|
+
query = query.limit(options.limit);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return query;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export async function getObject(id: string) {
|
|
461
|
+
return supabase
|
|
462
|
+
.from('objects')
|
|
463
|
+
.select('*')
|
|
464
|
+
.eq('id', id)
|
|
465
|
+
.single();
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export async function createObject(data: {
|
|
469
|
+
type: string;
|
|
470
|
+
name: string;
|
|
471
|
+
status: string;
|
|
472
|
+
subtype?: string;
|
|
473
|
+
custom_properties?: Record<string, unknown>;
|
|
474
|
+
organization_id: string;
|
|
475
|
+
}) {
|
|
476
|
+
return supabase
|
|
477
|
+
.from('objects')
|
|
478
|
+
.insert({
|
|
479
|
+
...data,
|
|
480
|
+
sync_status: 'local_only',
|
|
481
|
+
local_version: 1,
|
|
482
|
+
})
|
|
483
|
+
.select()
|
|
484
|
+
.single();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export async function updateObject(id: string, data: Partial<{
|
|
488
|
+
name: string;
|
|
489
|
+
status: string;
|
|
490
|
+
subtype: string;
|
|
491
|
+
custom_properties: Record<string, unknown>;
|
|
492
|
+
}>) {
|
|
493
|
+
// First get the current object to increment version
|
|
494
|
+
const { data: existing } = await supabase
|
|
495
|
+
.from('objects')
|
|
496
|
+
.select('local_version, sync_status')
|
|
497
|
+
.eq('id', id)
|
|
498
|
+
.single();
|
|
499
|
+
|
|
500
|
+
const updates: Record<string, unknown> = {
|
|
501
|
+
...data,
|
|
502
|
+
local_version: (existing?.local_version || 0) + 1,
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// Mark as pending push if previously synced
|
|
506
|
+
if (existing?.sync_status === 'synced') {
|
|
507
|
+
updates.sync_status = 'pending_push';
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return supabase
|
|
511
|
+
.from('objects')
|
|
512
|
+
.update(updates)
|
|
513
|
+
.eq('id', id)
|
|
514
|
+
.select()
|
|
515
|
+
.single();
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export async function deleteObject(id: string) {
|
|
519
|
+
// Soft delete
|
|
520
|
+
return supabase
|
|
521
|
+
.from('objects')
|
|
522
|
+
.update({
|
|
523
|
+
deleted_at: new Date().toISOString(),
|
|
524
|
+
sync_status: 'pending_push',
|
|
525
|
+
})
|
|
526
|
+
.eq('id', id);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// ============ User Helpers ============
|
|
530
|
+
|
|
531
|
+
export async function getUserByEmail(email: string) {
|
|
532
|
+
return supabase
|
|
533
|
+
.from('frontend_users')
|
|
534
|
+
.select('*')
|
|
535
|
+
.eq('email', email)
|
|
536
|
+
.single();
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export async function createUser(data: {
|
|
540
|
+
email: string;
|
|
541
|
+
name?: string;
|
|
542
|
+
first_name?: string;
|
|
543
|
+
last_name?: string;
|
|
544
|
+
organization_id: string;
|
|
545
|
+
role?: string;
|
|
546
|
+
}) {
|
|
547
|
+
return supabase
|
|
548
|
+
.from('frontend_users')
|
|
549
|
+
.insert({
|
|
550
|
+
...data,
|
|
551
|
+
email_verified: false,
|
|
552
|
+
oauth_accounts: [],
|
|
553
|
+
role: data.role || 'user',
|
|
554
|
+
sync_status: 'pending_push',
|
|
555
|
+
})
|
|
556
|
+
.select()
|
|
557
|
+
.single();
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// ============ Sync Helpers ============
|
|
561
|
+
|
|
562
|
+
export async function getPendingSync(direction: 'push' | 'pull') {
|
|
563
|
+
const status = direction === 'push' ? 'pending_push' : 'pending_pull';
|
|
564
|
+
return supabase
|
|
565
|
+
.from('objects')
|
|
566
|
+
.select('*')
|
|
567
|
+
.eq('sync_status', status);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
export async function markSynced(id: string, l4yercak3Id: string) {
|
|
571
|
+
return supabase
|
|
572
|
+
.from('objects')
|
|
573
|
+
.update({
|
|
574
|
+
l4yercak3_id: l4yercak3Id,
|
|
575
|
+
sync_status: 'synced',
|
|
576
|
+
synced_at: new Date().toISOString(),
|
|
577
|
+
})
|
|
578
|
+
.eq('id', id);
|
|
579
|
+
}
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
generateJavaScriptClient() {
|
|
584
|
+
return `/**
|
|
585
|
+
* Supabase Client
|
|
586
|
+
* Auto-generated by @l4yercak3/cli
|
|
587
|
+
*/
|
|
588
|
+
|
|
589
|
+
const { createClient } = require('@supabase/supabase-js');
|
|
590
|
+
|
|
591
|
+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
592
|
+
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
593
|
+
|
|
594
|
+
if (!supabaseUrl || !supabaseAnonKey) {
|
|
595
|
+
throw new Error('Missing Supabase environment variables');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Client-side Supabase client
|
|
599
|
+
const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
|
600
|
+
|
|
601
|
+
// Server-side Supabase client
|
|
602
|
+
function createServerClient() {
|
|
603
|
+
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
604
|
+
if (!serviceKey) {
|
|
605
|
+
throw new Error('SUPABASE_SERVICE_ROLE_KEY is required for server operations');
|
|
606
|
+
}
|
|
607
|
+
return createClient(supabaseUrl, serviceKey);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// ============ Object Helpers ============
|
|
611
|
+
|
|
612
|
+
async function getObjects(type, options = {}) {
|
|
613
|
+
let query = supabase
|
|
614
|
+
.from('objects')
|
|
615
|
+
.select('*')
|
|
616
|
+
.eq('type', type)
|
|
617
|
+
.is('deleted_at', null)
|
|
618
|
+
.order('updated_at', { ascending: false });
|
|
619
|
+
|
|
620
|
+
if (options.status) {
|
|
621
|
+
query = query.eq('status', options.status);
|
|
622
|
+
}
|
|
623
|
+
if (options.subtype) {
|
|
624
|
+
query = query.eq('subtype', options.subtype);
|
|
625
|
+
}
|
|
626
|
+
if (options.limit) {
|
|
627
|
+
query = query.limit(options.limit);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return query;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
async function getObject(id) {
|
|
634
|
+
return supabase
|
|
635
|
+
.from('objects')
|
|
636
|
+
.select('*')
|
|
637
|
+
.eq('id', id)
|
|
638
|
+
.single();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
async function createObject(data) {
|
|
642
|
+
return supabase
|
|
643
|
+
.from('objects')
|
|
644
|
+
.insert({
|
|
645
|
+
...data,
|
|
646
|
+
sync_status: 'local_only',
|
|
647
|
+
local_version: 1,
|
|
648
|
+
})
|
|
649
|
+
.select()
|
|
650
|
+
.single();
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
async function updateObject(id, data) {
|
|
654
|
+
const { data: existing } = await supabase
|
|
655
|
+
.from('objects')
|
|
656
|
+
.select('local_version, sync_status')
|
|
657
|
+
.eq('id', id)
|
|
658
|
+
.single();
|
|
659
|
+
|
|
660
|
+
const updates = {
|
|
661
|
+
...data,
|
|
662
|
+
local_version: (existing?.local_version || 0) + 1,
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
if (existing?.sync_status === 'synced') {
|
|
666
|
+
updates.sync_status = 'pending_push';
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return supabase
|
|
670
|
+
.from('objects')
|
|
671
|
+
.update(updates)
|
|
672
|
+
.eq('id', id)
|
|
673
|
+
.select()
|
|
674
|
+
.single();
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
async function deleteObject(id) {
|
|
678
|
+
return supabase
|
|
679
|
+
.from('objects')
|
|
680
|
+
.update({
|
|
681
|
+
deleted_at: new Date().toISOString(),
|
|
682
|
+
sync_status: 'pending_push',
|
|
683
|
+
})
|
|
684
|
+
.eq('id', id);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// ============ User Helpers ============
|
|
688
|
+
|
|
689
|
+
async function getUserByEmail(email) {
|
|
690
|
+
return supabase
|
|
691
|
+
.from('frontend_users')
|
|
692
|
+
.select('*')
|
|
693
|
+
.eq('email', email)
|
|
694
|
+
.single();
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
async function createUser(data) {
|
|
698
|
+
return supabase
|
|
699
|
+
.from('frontend_users')
|
|
700
|
+
.insert({
|
|
701
|
+
...data,
|
|
702
|
+
email_verified: false,
|
|
703
|
+
oauth_accounts: [],
|
|
704
|
+
role: data.role || 'user',
|
|
705
|
+
sync_status: 'pending_push',
|
|
706
|
+
})
|
|
707
|
+
.select()
|
|
708
|
+
.single();
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// ============ Sync Helpers ============
|
|
712
|
+
|
|
713
|
+
async function getPendingSync(direction) {
|
|
714
|
+
const status = direction === 'push' ? 'pending_push' : 'pending_pull';
|
|
715
|
+
return supabase
|
|
716
|
+
.from('objects')
|
|
717
|
+
.select('*')
|
|
718
|
+
.eq('sync_status', status);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
async function markSynced(id, l4yercak3Id) {
|
|
722
|
+
return supabase
|
|
723
|
+
.from('objects')
|
|
724
|
+
.update({
|
|
725
|
+
l4yercak3_id: l4yercak3Id,
|
|
726
|
+
sync_status: 'synced',
|
|
727
|
+
synced_at: new Date().toISOString(),
|
|
728
|
+
})
|
|
729
|
+
.eq('id', id);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
module.exports = {
|
|
733
|
+
supabase,
|
|
734
|
+
createServerClient,
|
|
735
|
+
getObjects,
|
|
736
|
+
getObject,
|
|
737
|
+
createObject,
|
|
738
|
+
updateObject,
|
|
739
|
+
deleteObject,
|
|
740
|
+
getUserByEmail,
|
|
741
|
+
createUser,
|
|
742
|
+
getPendingSync,
|
|
743
|
+
markSynced,
|
|
744
|
+
};
|
|
745
|
+
`;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
async generateTypes(projectPath) {
|
|
749
|
+
// Determine output directory
|
|
750
|
+
let outputDir;
|
|
751
|
+
if (fs.existsSync(path.join(projectPath, 'src'))) {
|
|
752
|
+
outputDir = path.join(projectPath, 'src', 'lib', 'supabase');
|
|
753
|
+
} else {
|
|
754
|
+
outputDir = path.join(projectPath, 'lib', 'supabase');
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
ensureDir(outputDir);
|
|
758
|
+
|
|
759
|
+
const outputPath = path.join(outputDir, 'types.ts');
|
|
760
|
+
|
|
761
|
+
const action = await checkFileOverwrite(outputPath);
|
|
762
|
+
if (action === 'skip') {
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const content = `/**
|
|
767
|
+
* Supabase Database Types
|
|
768
|
+
* Auto-generated by @l4yercak3/cli
|
|
769
|
+
*
|
|
770
|
+
* Note: For full type safety, generate types from your database using:
|
|
771
|
+
* npx supabase gen types typescript --project-id YOUR_PROJECT_ID > src/lib/supabase/types.ts
|
|
772
|
+
*/
|
|
773
|
+
|
|
774
|
+
export type Json =
|
|
775
|
+
| string
|
|
776
|
+
| number
|
|
777
|
+
| boolean
|
|
778
|
+
| null
|
|
779
|
+
| { [key: string]: Json | undefined }
|
|
780
|
+
| Json[];
|
|
781
|
+
|
|
782
|
+
export type SyncStatus =
|
|
783
|
+
| 'synced'
|
|
784
|
+
| 'pending_push'
|
|
785
|
+
| 'pending_pull'
|
|
786
|
+
| 'conflict'
|
|
787
|
+
| 'local_only';
|
|
788
|
+
|
|
789
|
+
export interface Database {
|
|
790
|
+
public: {
|
|
791
|
+
Tables: {
|
|
792
|
+
objects: {
|
|
793
|
+
Row: {
|
|
794
|
+
id: string;
|
|
795
|
+
l4yercak3_id: string | null;
|
|
796
|
+
organization_id: string;
|
|
797
|
+
type: string;
|
|
798
|
+
subtype: string | null;
|
|
799
|
+
name: string;
|
|
800
|
+
status: string;
|
|
801
|
+
custom_properties: Json;
|
|
802
|
+
sync_status: SyncStatus;
|
|
803
|
+
synced_at: string | null;
|
|
804
|
+
local_version: number;
|
|
805
|
+
remote_version: number | null;
|
|
806
|
+
created_at: string;
|
|
807
|
+
updated_at: string;
|
|
808
|
+
deleted_at: string | null;
|
|
809
|
+
};
|
|
810
|
+
Insert: {
|
|
811
|
+
id?: string;
|
|
812
|
+
l4yercak3_id?: string | null;
|
|
813
|
+
organization_id: string;
|
|
814
|
+
type: string;
|
|
815
|
+
subtype?: string | null;
|
|
816
|
+
name: string;
|
|
817
|
+
status: string;
|
|
818
|
+
custom_properties?: Json;
|
|
819
|
+
sync_status?: SyncStatus;
|
|
820
|
+
synced_at?: string | null;
|
|
821
|
+
local_version?: number;
|
|
822
|
+
remote_version?: number | null;
|
|
823
|
+
created_at?: string;
|
|
824
|
+
updated_at?: string;
|
|
825
|
+
deleted_at?: string | null;
|
|
826
|
+
};
|
|
827
|
+
Update: {
|
|
828
|
+
id?: string;
|
|
829
|
+
l4yercak3_id?: string | null;
|
|
830
|
+
organization_id?: string;
|
|
831
|
+
type?: string;
|
|
832
|
+
subtype?: string | null;
|
|
833
|
+
name?: string;
|
|
834
|
+
status?: string;
|
|
835
|
+
custom_properties?: Json;
|
|
836
|
+
sync_status?: SyncStatus;
|
|
837
|
+
synced_at?: string | null;
|
|
838
|
+
local_version?: number;
|
|
839
|
+
remote_version?: number | null;
|
|
840
|
+
created_at?: string;
|
|
841
|
+
updated_at?: string;
|
|
842
|
+
deleted_at?: string | null;
|
|
843
|
+
};
|
|
844
|
+
};
|
|
845
|
+
object_links: {
|
|
846
|
+
Row: {
|
|
847
|
+
id: string;
|
|
848
|
+
l4yercak3_id: string | null;
|
|
849
|
+
from_object_id: string;
|
|
850
|
+
to_object_id: string;
|
|
851
|
+
link_type: string;
|
|
852
|
+
metadata: Json | null;
|
|
853
|
+
sync_status: string;
|
|
854
|
+
created_at: string;
|
|
855
|
+
};
|
|
856
|
+
Insert: {
|
|
857
|
+
id?: string;
|
|
858
|
+
l4yercak3_id?: string | null;
|
|
859
|
+
from_object_id: string;
|
|
860
|
+
to_object_id: string;
|
|
861
|
+
link_type: string;
|
|
862
|
+
metadata?: Json | null;
|
|
863
|
+
sync_status?: string;
|
|
864
|
+
created_at?: string;
|
|
865
|
+
};
|
|
866
|
+
Update: {
|
|
867
|
+
id?: string;
|
|
868
|
+
l4yercak3_id?: string | null;
|
|
869
|
+
from_object_id?: string;
|
|
870
|
+
to_object_id?: string;
|
|
871
|
+
link_type?: string;
|
|
872
|
+
metadata?: Json | null;
|
|
873
|
+
sync_status?: string;
|
|
874
|
+
created_at?: string;
|
|
875
|
+
};
|
|
876
|
+
};
|
|
877
|
+
frontend_users: {
|
|
878
|
+
Row: {
|
|
879
|
+
id: string;
|
|
880
|
+
l4yercak3_contact_id: string | null;
|
|
881
|
+
l4yercak3_frontend_user_id: string | null;
|
|
882
|
+
organization_id: string;
|
|
883
|
+
email: string;
|
|
884
|
+
email_verified: boolean;
|
|
885
|
+
name: string | null;
|
|
886
|
+
first_name: string | null;
|
|
887
|
+
last_name: string | null;
|
|
888
|
+
image: string | null;
|
|
889
|
+
phone: string | null;
|
|
890
|
+
password_hash: string | null;
|
|
891
|
+
oauth_accounts: Json;
|
|
892
|
+
role: string;
|
|
893
|
+
preferences: Json;
|
|
894
|
+
sync_status: string;
|
|
895
|
+
synced_at: string | null;
|
|
896
|
+
created_at: string;
|
|
897
|
+
updated_at: string;
|
|
898
|
+
last_login_at: string | null;
|
|
899
|
+
};
|
|
900
|
+
Insert: {
|
|
901
|
+
id?: string;
|
|
902
|
+
l4yercak3_contact_id?: string | null;
|
|
903
|
+
l4yercak3_frontend_user_id?: string | null;
|
|
904
|
+
organization_id: string;
|
|
905
|
+
email: string;
|
|
906
|
+
email_verified?: boolean;
|
|
907
|
+
name?: string | null;
|
|
908
|
+
first_name?: string | null;
|
|
909
|
+
last_name?: string | null;
|
|
910
|
+
image?: string | null;
|
|
911
|
+
phone?: string | null;
|
|
912
|
+
password_hash?: string | null;
|
|
913
|
+
oauth_accounts?: Json;
|
|
914
|
+
role?: string;
|
|
915
|
+
preferences?: Json;
|
|
916
|
+
sync_status?: string;
|
|
917
|
+
synced_at?: string | null;
|
|
918
|
+
created_at?: string;
|
|
919
|
+
updated_at?: string;
|
|
920
|
+
last_login_at?: string | null;
|
|
921
|
+
};
|
|
922
|
+
Update: {
|
|
923
|
+
id?: string;
|
|
924
|
+
l4yercak3_contact_id?: string | null;
|
|
925
|
+
l4yercak3_frontend_user_id?: string | null;
|
|
926
|
+
organization_id?: string;
|
|
927
|
+
email?: string;
|
|
928
|
+
email_verified?: boolean;
|
|
929
|
+
name?: string | null;
|
|
930
|
+
first_name?: string | null;
|
|
931
|
+
last_name?: string | null;
|
|
932
|
+
image?: string | null;
|
|
933
|
+
phone?: string | null;
|
|
934
|
+
password_hash?: string | null;
|
|
935
|
+
oauth_accounts?: Json;
|
|
936
|
+
role?: string;
|
|
937
|
+
preferences?: Json;
|
|
938
|
+
sync_status?: string;
|
|
939
|
+
synced_at?: string | null;
|
|
940
|
+
created_at?: string;
|
|
941
|
+
updated_at?: string;
|
|
942
|
+
last_login_at?: string | null;
|
|
943
|
+
};
|
|
944
|
+
};
|
|
945
|
+
sessions: {
|
|
946
|
+
Row: {
|
|
947
|
+
id: string;
|
|
948
|
+
user_id: string;
|
|
949
|
+
session_token: string;
|
|
950
|
+
expires_at: string;
|
|
951
|
+
user_agent: string | null;
|
|
952
|
+
ip_address: string | null;
|
|
953
|
+
created_at: string;
|
|
954
|
+
};
|
|
955
|
+
Insert: {
|
|
956
|
+
id?: string;
|
|
957
|
+
user_id: string;
|
|
958
|
+
session_token: string;
|
|
959
|
+
expires_at: string;
|
|
960
|
+
user_agent?: string | null;
|
|
961
|
+
ip_address?: string | null;
|
|
962
|
+
created_at?: string;
|
|
963
|
+
};
|
|
964
|
+
Update: {
|
|
965
|
+
id?: string;
|
|
966
|
+
user_id?: string;
|
|
967
|
+
session_token?: string;
|
|
968
|
+
expires_at?: string;
|
|
969
|
+
user_agent?: string | null;
|
|
970
|
+
ip_address?: string | null;
|
|
971
|
+
created_at?: string;
|
|
972
|
+
};
|
|
973
|
+
};
|
|
974
|
+
stripe_customers: {
|
|
975
|
+
Row: {
|
|
976
|
+
id: string;
|
|
977
|
+
frontend_user_id: string;
|
|
978
|
+
stripe_customer_id: string;
|
|
979
|
+
l4yercak3_contact_id: string | null;
|
|
980
|
+
email: string;
|
|
981
|
+
name: string | null;
|
|
982
|
+
default_payment_method_id: string | null;
|
|
983
|
+
sync_status: string;
|
|
984
|
+
created_at: string;
|
|
985
|
+
};
|
|
986
|
+
Insert: {
|
|
987
|
+
id?: string;
|
|
988
|
+
frontend_user_id: string;
|
|
989
|
+
stripe_customer_id: string;
|
|
990
|
+
l4yercak3_contact_id?: string | null;
|
|
991
|
+
email: string;
|
|
992
|
+
name?: string | null;
|
|
993
|
+
default_payment_method_id?: string | null;
|
|
994
|
+
sync_status?: string;
|
|
995
|
+
created_at?: string;
|
|
996
|
+
};
|
|
997
|
+
Update: {
|
|
998
|
+
id?: string;
|
|
999
|
+
frontend_user_id?: string;
|
|
1000
|
+
stripe_customer_id?: string;
|
|
1001
|
+
l4yercak3_contact_id?: string | null;
|
|
1002
|
+
email?: string;
|
|
1003
|
+
name?: string | null;
|
|
1004
|
+
default_payment_method_id?: string | null;
|
|
1005
|
+
sync_status?: string;
|
|
1006
|
+
created_at?: string;
|
|
1007
|
+
};
|
|
1008
|
+
};
|
|
1009
|
+
stripe_payments: {
|
|
1010
|
+
Row: {
|
|
1011
|
+
id: string;
|
|
1012
|
+
stripe_payment_intent_id: string;
|
|
1013
|
+
stripe_customer_id: string | null;
|
|
1014
|
+
frontend_user_id: string | null;
|
|
1015
|
+
amount: number;
|
|
1016
|
+
currency: string;
|
|
1017
|
+
status: string;
|
|
1018
|
+
payment_method: string | null;
|
|
1019
|
+
metadata: Json;
|
|
1020
|
+
l4yercak3_order_id: string | null;
|
|
1021
|
+
l4yercak3_invoice_id: string | null;
|
|
1022
|
+
sync_status: string;
|
|
1023
|
+
synced_at: string | null;
|
|
1024
|
+
created_at: string;
|
|
1025
|
+
completed_at: string | null;
|
|
1026
|
+
};
|
|
1027
|
+
Insert: {
|
|
1028
|
+
id?: string;
|
|
1029
|
+
stripe_payment_intent_id: string;
|
|
1030
|
+
stripe_customer_id?: string | null;
|
|
1031
|
+
frontend_user_id?: string | null;
|
|
1032
|
+
amount: number;
|
|
1033
|
+
currency: string;
|
|
1034
|
+
status: string;
|
|
1035
|
+
payment_method?: string | null;
|
|
1036
|
+
metadata: Json;
|
|
1037
|
+
l4yercak3_order_id?: string | null;
|
|
1038
|
+
l4yercak3_invoice_id?: string | null;
|
|
1039
|
+
sync_status?: string;
|
|
1040
|
+
synced_at?: string | null;
|
|
1041
|
+
created_at?: string;
|
|
1042
|
+
completed_at?: string | null;
|
|
1043
|
+
};
|
|
1044
|
+
Update: {
|
|
1045
|
+
id?: string;
|
|
1046
|
+
stripe_payment_intent_id?: string;
|
|
1047
|
+
stripe_customer_id?: string | null;
|
|
1048
|
+
frontend_user_id?: string | null;
|
|
1049
|
+
amount?: number;
|
|
1050
|
+
currency?: string;
|
|
1051
|
+
status?: string;
|
|
1052
|
+
payment_method?: string | null;
|
|
1053
|
+
metadata?: Json;
|
|
1054
|
+
l4yercak3_order_id?: string | null;
|
|
1055
|
+
l4yercak3_invoice_id?: string | null;
|
|
1056
|
+
sync_status?: string;
|
|
1057
|
+
synced_at?: string | null;
|
|
1058
|
+
created_at?: string;
|
|
1059
|
+
completed_at?: string | null;
|
|
1060
|
+
};
|
|
1061
|
+
};
|
|
1062
|
+
};
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
`;
|
|
1066
|
+
|
|
1067
|
+
return writeFileWithBackup(outputPath, content, action);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
async generateMiddleware(projectPath, options) {
|
|
1071
|
+
const { isTypeScript } = options;
|
|
1072
|
+
|
|
1073
|
+
const extension = isTypeScript ? 'ts' : 'js';
|
|
1074
|
+
const outputPath = path.join(projectPath, `middleware.${extension}`);
|
|
1075
|
+
|
|
1076
|
+
const action = await checkFileOverwrite(outputPath);
|
|
1077
|
+
if (action === 'skip') {
|
|
1078
|
+
return null;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const content = isTypeScript
|
|
1082
|
+
? `/**
|
|
1083
|
+
* Supabase Auth Middleware
|
|
1084
|
+
* Auto-generated by @l4yercak3/cli
|
|
1085
|
+
*/
|
|
1086
|
+
|
|
1087
|
+
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs';
|
|
1088
|
+
import { NextResponse } from 'next/server';
|
|
1089
|
+
import type { NextRequest } from 'next/server';
|
|
1090
|
+
|
|
1091
|
+
export async function middleware(req: NextRequest) {
|
|
1092
|
+
const res = NextResponse.next();
|
|
1093
|
+
const supabase = createMiddlewareClient({ req, res });
|
|
1094
|
+
await supabase.auth.getSession();
|
|
1095
|
+
return res;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
export const config = {
|
|
1099
|
+
matcher: [
|
|
1100
|
+
'/((?!_next/static|_next/image|favicon.ico|public).*)',
|
|
1101
|
+
],
|
|
1102
|
+
};
|
|
1103
|
+
`
|
|
1104
|
+
: `/**
|
|
1105
|
+
* Supabase Auth Middleware
|
|
1106
|
+
* Auto-generated by @l4yercak3/cli
|
|
1107
|
+
*/
|
|
1108
|
+
|
|
1109
|
+
const { createMiddlewareClient } = require('@supabase/auth-helpers-nextjs');
|
|
1110
|
+
const { NextResponse } = require('next/server');
|
|
1111
|
+
|
|
1112
|
+
async function middleware(req) {
|
|
1113
|
+
const res = NextResponse.next();
|
|
1114
|
+
const supabase = createMiddlewareClient({ req, res });
|
|
1115
|
+
await supabase.auth.getSession();
|
|
1116
|
+
return res;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const config = {
|
|
1120
|
+
matcher: [
|
|
1121
|
+
'/((?!_next/static|_next/image|favicon.ico|public).*)',
|
|
1122
|
+
],
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
module.exports = { middleware, config };
|
|
1126
|
+
`;
|
|
1127
|
+
|
|
1128
|
+
return writeFileWithBackup(outputPath, content, action);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
module.exports = new SupabaseGenerator();
|