@xbg.solutions/create-backend 1.0.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 (32) hide show
  1. package/bin/create-backend.js +3 -0
  2. package/lib/cli.d.ts +12 -0
  3. package/lib/cli.js +55 -0
  4. package/lib/cli.js.map +1 -0
  5. package/lib/commands/add-util.d.ts +9 -0
  6. package/lib/commands/add-util.js +119 -0
  7. package/lib/commands/add-util.js.map +1 -0
  8. package/lib/commands/init.d.ts +11 -0
  9. package/lib/commands/init.js +372 -0
  10. package/lib/commands/init.js.map +1 -0
  11. package/lib/commands/sync.d.ts +10 -0
  12. package/lib/commands/sync.js +161 -0
  13. package/lib/commands/sync.js.map +1 -0
  14. package/lib/utils-registry.d.ts +25 -0
  15. package/lib/utils-registry.js +187 -0
  16. package/lib/utils-registry.js.map +1 -0
  17. package/package.json +38 -0
  18. package/src/project-template/__examples__/README.md +559 -0
  19. package/src/project-template/__examples__/blog-platform.model.ts +528 -0
  20. package/src/project-template/__examples__/communications-usage.ts +175 -0
  21. package/src/project-template/__examples__/ecommerce-store.model.ts +1200 -0
  22. package/src/project-template/__examples__/saas-multi-tenant.model.ts +798 -0
  23. package/src/project-template/__examples__/user.model.ts +221 -0
  24. package/src/project-template/__scripts__/deploy.js +115 -0
  25. package/src/project-template/__scripts__/generate.js +122 -0
  26. package/src/project-template/__scripts__/setup.js +425 -0
  27. package/src/project-template/__scripts__/validate.js +325 -0
  28. package/src/project-template/firebase.json +32 -0
  29. package/src/project-template/firestore.rules +12 -0
  30. package/src/project-template/functions/jest.config.js +49 -0
  31. package/src/project-template/functions/src/index.ts +46 -0
  32. package/src/project-template/functions/tsconfig.json +38 -0
@@ -0,0 +1,528 @@
1
+ /**
2
+ * Blog Platform Data Model
3
+ *
4
+ * A complete blog platform with:
5
+ * - User authentication and profiles
6
+ * - Posts with categories and tags
7
+ * - Comments with threading
8
+ * - Social features (likes, follows)
9
+ * - Content moderation
10
+ *
11
+ * Features demonstrated:
12
+ * - One-to-many relationships (User → Posts)
13
+ * - Many-to-many relationships (Posts → Tags)
14
+ * - Self-referencing relationships (Comment replies)
15
+ * - Access control rules
16
+ * - Business rules and validation
17
+ * - Optimized indexes
18
+ *
19
+ * To use this model:
20
+ * 1. Copy to your project
21
+ * 2. Run: npm run generate __examples__/blog-platform.model.ts
22
+ * 3. Register controllers in functions/src/app.ts
23
+ * 4. Deploy: npm run deploy
24
+ */
25
+
26
+ import { DataModelSpecification } from '../functions/src/generator/types';
27
+
28
+ export const BlogPlatformModel: DataModelSpecification = {
29
+ entities: {
30
+ User: {
31
+ fields: {
32
+ email: {
33
+ type: 'email',
34
+ unique: true,
35
+ required: true,
36
+ },
37
+ username: {
38
+ type: 'string',
39
+ unique: true,
40
+ required: true,
41
+ },
42
+ displayName: {
43
+ type: 'string',
44
+ required: true,
45
+ },
46
+ bio: {
47
+ type: 'text',
48
+ required: false,
49
+ },
50
+ avatarUrl: {
51
+ type: 'url',
52
+ required: false,
53
+ },
54
+ isVerified: {
55
+ type: 'boolean',
56
+ default: false,
57
+ },
58
+ role: {
59
+ type: 'enum',
60
+ values: ['reader', 'author', 'moderator', 'admin'],
61
+ default: 'reader',
62
+ },
63
+ status: {
64
+ type: 'enum',
65
+ values: ['active', 'suspended', 'deleted'],
66
+ default: 'active',
67
+ },
68
+ lastLoginAt: {
69
+ type: 'timestamp',
70
+ required: false,
71
+ },
72
+ createdAt: {
73
+ type: 'timestamp',
74
+ required: true,
75
+ },
76
+ updatedAt: {
77
+ type: 'timestamp',
78
+ required: true,
79
+ },
80
+ },
81
+ relationships: {
82
+ posts: {
83
+ type: 'one-to-many',
84
+ entity: 'Post',
85
+ foreignKey: 'authorId',
86
+ cascadeDelete: false, // Keep posts when user is deleted (anonymize instead)
87
+ },
88
+ comments: {
89
+ type: 'one-to-many',
90
+ entity: 'Comment',
91
+ foreignKey: 'authorId',
92
+ cascadeDelete: false,
93
+ },
94
+ likedPosts: {
95
+ type: 'many-to-many',
96
+ entity: 'Post',
97
+ through: 'PostLike',
98
+ },
99
+ following: {
100
+ type: 'many-to-many',
101
+ entity: 'User',
102
+ through: 'UserFollow',
103
+ foreignKey: 'followerId',
104
+ relatedKey: 'followingId',
105
+ },
106
+ },
107
+ access: {
108
+ create: ['public'], // Public registration
109
+ read: ['public'],
110
+ update: ['self', 'admin'],
111
+ delete: ['self', 'admin'],
112
+ },
113
+ validation: {
114
+ email: 'Must be a valid email address',
115
+ username: 'Must be 3-30 characters, alphanumeric and underscores only',
116
+ displayName: 'Must be 1-100 characters',
117
+ },
118
+ businessRules: [
119
+ 'Email must be verified before user can publish posts',
120
+ 'Suspended users cannot create posts or comments',
121
+ 'Deleted users have their personal data anonymized',
122
+ 'Users cannot follow themselves',
123
+ ],
124
+ indexes: [
125
+ { fields: ['email'], unique: true },
126
+ { fields: ['username'], unique: true },
127
+ { fields: ['status'] },
128
+ { fields: ['createdAt'] },
129
+ ],
130
+ },
131
+
132
+ Post: {
133
+ fields: {
134
+ title: {
135
+ type: 'string',
136
+ required: true,
137
+ },
138
+ slug: {
139
+ type: 'string',
140
+ unique: true,
141
+ required: true,
142
+ },
143
+ content: {
144
+ type: 'text',
145
+ required: true,
146
+ },
147
+ excerpt: {
148
+ type: 'string',
149
+ required: false,
150
+ },
151
+ coverImageUrl: {
152
+ type: 'url',
153
+ required: false,
154
+ },
155
+ status: {
156
+ type: 'enum',
157
+ values: ['draft', 'published', 'archived', 'flagged'],
158
+ default: 'draft',
159
+ },
160
+ publishedAt: {
161
+ type: 'timestamp',
162
+ required: false,
163
+ },
164
+ authorId: {
165
+ type: 'reference',
166
+ entity: 'User',
167
+ required: true,
168
+ },
169
+ categoryId: {
170
+ type: 'reference',
171
+ entity: 'Category',
172
+ required: true,
173
+ },
174
+ viewCount: {
175
+ type: 'number',
176
+ default: 0,
177
+ },
178
+ likeCount: {
179
+ type: 'number',
180
+ default: 0,
181
+ },
182
+ commentCount: {
183
+ type: 'number',
184
+ default: 0,
185
+ },
186
+ readingTimeMinutes: {
187
+ type: 'number',
188
+ required: false,
189
+ },
190
+ isFeatured: {
191
+ type: 'boolean',
192
+ default: false,
193
+ },
194
+ createdAt: {
195
+ type: 'timestamp',
196
+ required: true,
197
+ },
198
+ updatedAt: {
199
+ type: 'timestamp',
200
+ required: true,
201
+ },
202
+ },
203
+ relationships: {
204
+ author: {
205
+ type: 'many-to-one',
206
+ entity: 'User',
207
+ },
208
+ category: {
209
+ type: 'many-to-one',
210
+ entity: 'Category',
211
+ },
212
+ comments: {
213
+ type: 'one-to-many',
214
+ entity: 'Comment',
215
+ foreignKey: 'postId',
216
+ cascadeDelete: true,
217
+ },
218
+ tags: {
219
+ type: 'many-to-many',
220
+ entity: 'Tag',
221
+ through: 'PostTag',
222
+ },
223
+ likes: {
224
+ type: 'many-to-many',
225
+ entity: 'User',
226
+ through: 'PostLike',
227
+ },
228
+ },
229
+ access: {
230
+ create: ['author', 'admin'],
231
+ read: ['public'], // Anyone can read published posts
232
+ update: ['self', 'admin'],
233
+ delete: ['self', 'admin'],
234
+ },
235
+ validation: {
236
+ title: 'Must be 5-200 characters',
237
+ slug: 'Must be URL-safe and unique',
238
+ content: 'Must not be empty',
239
+ },
240
+ businessRules: [
241
+ 'Slug is auto-generated from title',
242
+ 'Published posts cannot be unpublished (only archived)',
243
+ 'Posts must belong to an active category',
244
+ 'Reading time is calculated from content length',
245
+ 'Only verified users can publish posts',
246
+ ],
247
+ indexes: [
248
+ { fields: ['slug'], unique: true },
249
+ { fields: ['authorId', 'status'] },
250
+ { fields: ['categoryId', 'status'] },
251
+ { fields: ['status', 'publishedAt'] },
252
+ { fields: ['isFeatured', 'publishedAt'] },
253
+ ],
254
+ },
255
+
256
+ Comment: {
257
+ fields: {
258
+ content: {
259
+ type: 'text',
260
+ required: true,
261
+ },
262
+ postId: {
263
+ type: 'reference',
264
+ entity: 'Post',
265
+ required: true,
266
+ },
267
+ authorId: {
268
+ type: 'reference',
269
+ entity: 'User',
270
+ required: true,
271
+ },
272
+ parentId: {
273
+ type: 'reference',
274
+ entity: 'Comment',
275
+ required: false,
276
+ },
277
+ status: {
278
+ type: 'enum',
279
+ values: ['visible', 'flagged', 'deleted'],
280
+ default: 'visible',
281
+ },
282
+ isEdited: {
283
+ type: 'boolean',
284
+ default: false,
285
+ },
286
+ createdAt: {
287
+ type: 'timestamp',
288
+ required: true,
289
+ },
290
+ updatedAt: {
291
+ type: 'timestamp',
292
+ required: true,
293
+ },
294
+ },
295
+ relationships: {
296
+ post: {
297
+ type: 'many-to-one',
298
+ entity: 'Post',
299
+ },
300
+ author: {
301
+ type: 'many-to-one',
302
+ entity: 'User',
303
+ },
304
+ parent: {
305
+ type: 'many-to-one',
306
+ entity: 'Comment',
307
+ optional: true,
308
+ },
309
+ replies: {
310
+ type: 'one-to-many',
311
+ entity: 'Comment',
312
+ foreignKey: 'parentId',
313
+ cascadeDelete: true,
314
+ },
315
+ },
316
+ access: {
317
+ create: ['authenticated'],
318
+ read: ['public'],
319
+ update: ['self', 'moderator', 'admin'],
320
+ delete: ['self', 'moderator', 'admin'],
321
+ },
322
+ validation: {
323
+ content: 'Must be 1-2000 characters',
324
+ },
325
+ businessRules: [
326
+ 'Comments can be nested up to 3 levels deep',
327
+ 'Deleted comments show [deleted] placeholder',
328
+ 'Flagged comments are hidden from public view',
329
+ 'Users can edit comments within 5 minutes of posting',
330
+ ],
331
+ indexes: [
332
+ { fields: ['postId', 'status', 'createdAt'] },
333
+ { fields: ['authorId'] },
334
+ { fields: ['parentId'] },
335
+ ],
336
+ },
337
+
338
+ Category: {
339
+ fields: {
340
+ name: {
341
+ type: 'string',
342
+ unique: true,
343
+ required: true,
344
+ },
345
+ slug: {
346
+ type: 'string',
347
+ unique: true,
348
+ required: true,
349
+ },
350
+ description: {
351
+ type: 'text',
352
+ required: false,
353
+ },
354
+ color: {
355
+ type: 'string',
356
+ required: false,
357
+ },
358
+ icon: {
359
+ type: 'string',
360
+ required: false,
361
+ },
362
+ isActive: {
363
+ type: 'boolean',
364
+ default: true,
365
+ },
366
+ postCount: {
367
+ type: 'number',
368
+ default: 0,
369
+ },
370
+ order: {
371
+ type: 'number',
372
+ default: 0,
373
+ },
374
+ },
375
+ relationships: {
376
+ posts: {
377
+ type: 'one-to-many',
378
+ entity: 'Post',
379
+ foreignKey: 'categoryId',
380
+ },
381
+ },
382
+ access: {
383
+ create: ['admin'],
384
+ read: ['public'],
385
+ update: ['admin'],
386
+ delete: ['admin'],
387
+ },
388
+ validation: {
389
+ name: 'Must be 2-50 characters',
390
+ slug: 'Must be URL-safe',
391
+ },
392
+ businessRules: [
393
+ 'Categories with posts cannot be deleted',
394
+ 'At least one category must exist',
395
+ ],
396
+ indexes: [
397
+ { fields: ['slug'], unique: true },
398
+ { fields: ['isActive', 'order'] },
399
+ ],
400
+ },
401
+
402
+ Tag: {
403
+ fields: {
404
+ name: {
405
+ type: 'string',
406
+ unique: true,
407
+ required: true,
408
+ },
409
+ slug: {
410
+ type: 'string',
411
+ unique: true,
412
+ required: true,
413
+ },
414
+ postCount: {
415
+ type: 'number',
416
+ default: 0,
417
+ },
418
+ },
419
+ relationships: {
420
+ posts: {
421
+ type: 'many-to-many',
422
+ entity: 'Post',
423
+ through: 'PostTag',
424
+ },
425
+ },
426
+ access: {
427
+ create: ['author', 'admin'],
428
+ read: ['public'],
429
+ update: ['admin'],
430
+ delete: ['admin'],
431
+ },
432
+ validation: {
433
+ name: 'Must be 2-30 characters',
434
+ },
435
+ businessRules: [
436
+ 'Tags are automatically created when used in posts',
437
+ 'Tags with zero posts are periodically cleaned up',
438
+ ],
439
+ indexes: [
440
+ { fields: ['slug'], unique: true },
441
+ { fields: ['postCount'] },
442
+ ],
443
+ },
444
+
445
+ PostLike: {
446
+ fields: {
447
+ postId: {
448
+ type: 'reference',
449
+ entity: 'Post',
450
+ required: true,
451
+ },
452
+ userId: {
453
+ type: 'reference',
454
+ entity: 'User',
455
+ required: true,
456
+ },
457
+ createdAt: {
458
+ type: 'timestamp',
459
+ required: true,
460
+ },
461
+ },
462
+ access: {
463
+ create: ['authenticated'],
464
+ read: ['public'],
465
+ delete: ['self'],
466
+ },
467
+ businessRules: [
468
+ 'Users cannot like their own posts',
469
+ 'Users can only like a post once',
470
+ ],
471
+ indexes: [
472
+ { fields: ['postId', 'userId'], unique: true },
473
+ { fields: ['userId'] },
474
+ ],
475
+ },
476
+
477
+ UserFollow: {
478
+ fields: {
479
+ followerId: {
480
+ type: 'reference',
481
+ entity: 'User',
482
+ required: true,
483
+ },
484
+ followingId: {
485
+ type: 'reference',
486
+ entity: 'User',
487
+ required: true,
488
+ },
489
+ createdAt: {
490
+ type: 'timestamp',
491
+ required: true,
492
+ },
493
+ },
494
+ access: {
495
+ create: ['authenticated'],
496
+ read: ['public'],
497
+ delete: ['self'],
498
+ },
499
+ businessRules: [
500
+ 'Users cannot follow themselves',
501
+ 'Users can only follow once (no duplicates)',
502
+ ],
503
+ indexes: [
504
+ { fields: ['followerId', 'followingId'], unique: true },
505
+ { fields: ['followingId'] },
506
+ ],
507
+ },
508
+
509
+ PostTag: {
510
+ fields: {
511
+ postId: {
512
+ type: 'reference',
513
+ entity: 'Post',
514
+ required: true,
515
+ },
516
+ tagId: {
517
+ type: 'reference',
518
+ entity: 'Tag',
519
+ required: true,
520
+ },
521
+ },
522
+ indexes: [
523
+ { fields: ['postId', 'tagId'], unique: true },
524
+ { fields: ['tagId'] },
525
+ ],
526
+ },
527
+ },
528
+ };
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Communication Utilities Usage Examples
3
+ * Demonstrates how to use SNS, Email, SMS, and CRM connectors
4
+ */
5
+
6
+ import {
7
+ getSNSConnector,
8
+ getEmailConnector,
9
+ getSMSConnector,
10
+ getCRMConnector,
11
+ } from '../functions/src/utilities';
12
+
13
+ /**
14
+ * Example 1: Publish event to SNS
15
+ */
16
+ export async function exampleSNSPublish() {
17
+ const snsConnector = getSNSConnector();
18
+ if (!snsConnector) {
19
+ console.log('SNS connector not configured');
20
+ return;
21
+ }
22
+
23
+ await snsConnector.publishEvent(
24
+ process.env.SNS_USER_EVENTS_TOPIC!,
25
+ {
26
+ eventType: 'USER_REGISTERED',
27
+ entityType: 'User',
28
+ entityId: 'user-123',
29
+ data: {
30
+ email: 'user@example.com',
31
+ name: 'John Doe',
32
+ },
33
+ metadata: {
34
+ timestamp: new Date(),
35
+ correlationId: 'req-456',
36
+ userId: 'user-123',
37
+ source: 'web-app',
38
+ version: '1.0',
39
+ },
40
+ }
41
+ );
42
+ }
43
+
44
+ /**
45
+ * Example 2: Send transactional email
46
+ */
47
+ export async function exampleSendWelcomeEmail() {
48
+ const emailConnector = getEmailConnector();
49
+ if (!emailConnector) {
50
+ console.log('Email connector not configured');
51
+ return;
52
+ }
53
+
54
+ await emailConnector.sendTransactional({
55
+ to: [
56
+ {
57
+ email: 'user@example.com',
58
+ name: 'John Doe',
59
+ },
60
+ ],
61
+ templateId: 'welcome-email',
62
+ variables: {
63
+ firstName: 'John',
64
+ activationLink: 'https://app.example.com/activate/token123',
65
+ },
66
+ tags: ['welcome', 'onboarding'],
67
+ });
68
+ }
69
+
70
+ /**
71
+ * Example 3: Send verification SMS
72
+ */
73
+ export async function exampleSendVerificationSMS() {
74
+ const smsConnector = getSMSConnector();
75
+ if (!smsConnector) {
76
+ console.log('SMS connector not configured');
77
+ return;
78
+ }
79
+
80
+ const verificationCode = '123456';
81
+
82
+ await smsConnector.sendMessage({
83
+ to: '+1234567890',
84
+ message: `Your verification code is: ${verificationCode}. Valid for 10 minutes.`,
85
+ validityPeriod: 600, // 10 minutes
86
+ tags: ['verification', 'auth'],
87
+ });
88
+ }
89
+
90
+ /**
91
+ * Example 4: Sync user to CRM
92
+ */
93
+ export async function exampleSyncUserToCRM() {
94
+ const crmConnector = getCRMConnector();
95
+ if (!crmConnector) {
96
+ console.log('CRM connector not configured');
97
+ return;
98
+ }
99
+
100
+ await crmConnector.createContact({
101
+ email: 'user@example.com',
102
+ firstName: 'John',
103
+ lastName: 'Doe',
104
+ phone: '+1234567890',
105
+ company: 'Acme Inc',
106
+ jobTitle: 'Software Engineer',
107
+ customFields: {
108
+ signup_date: new Date().toISOString(),
109
+ user_tier: 'premium',
110
+ referral_source: 'organic',
111
+ },
112
+ tags: ['new-user', 'premium'],
113
+ source: 'web_app',
114
+ });
115
+ }
116
+
117
+ /**
118
+ * Example 5: Log activity in CRM
119
+ */
120
+ export async function exampleLogCRMActivity() {
121
+ const crmConnector = getCRMConnector();
122
+ if (!crmConnector) return;
123
+
124
+ await crmConnector.logActivity({
125
+ contactEmail: 'user@example.com',
126
+ type: 'purchase',
127
+ subject: 'Premium Plan Purchase',
128
+ description: 'User upgraded to premium plan for $99/month',
129
+ timestamp: new Date(),
130
+ customFields: {
131
+ plan_name: 'Premium',
132
+ plan_price: 99,
133
+ billing_cycle: 'monthly',
134
+ },
135
+ });
136
+ }
137
+
138
+ /**
139
+ * Example 6: Event-driven communication flow
140
+ * This shows how the event bus automatically triggers communications
141
+ */
142
+ export async function exampleEventDrivenFlow() {
143
+ // When a USER_CREATED event is published to the event bus,
144
+ // the communication subscribers automatically:
145
+ // 1. Stream the event to SNS
146
+ // 2. Send a welcome email
147
+ // 3. Create a contact in CRM
148
+
149
+ // No manual connector calls needed - just publish the event!
150
+ // See: functions/src/subscribers/communication-subscribers.ts
151
+ }
152
+
153
+ /**
154
+ * Example 7: Bulk operations
155
+ */
156
+ export async function exampleBulkOperations() {
157
+ const emailConnector = getEmailConnector();
158
+ if (!emailConnector) return;
159
+
160
+ const users = [
161
+ { email: 'user1@example.com', name: 'User 1' },
162
+ { email: 'user2@example.com', name: 'User 2' },
163
+ { email: 'user3@example.com', name: 'User 3' },
164
+ ];
165
+
166
+ await emailConnector.sendBulk({
167
+ emails: users.map((user) => ({
168
+ to: [user],
169
+ subject: 'Product Update',
170
+ htmlContent: '<h1>New Features Available!</h1>',
171
+ tags: ['product-update', 'marketing'],
172
+ })),
173
+ batchSize: 50,
174
+ });
175
+ }