@donotdev/functions 0.0.6 → 0.0.8
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/lib/firebase/index.js +8026 -32339
- package/lib/firebase/index.js.map +4 -4
- package/lib/shared/index.js +5973 -31507
- package/lib/shared/index.js.map +4 -4
- package/lib/vercel/api/index.js +4173 -30252
- package/lib/vercel/api/index.js.map +4 -4
- package/package.json +3 -3
- package/src/firebase/auth/getCustomClaims.ts +2 -1
- package/src/firebase/auth/getUserAuthStatus.ts +2 -1
- package/src/firebase/auth/removeCustomClaims.ts +2 -1
- package/src/firebase/auth/setCustomClaims.ts +3 -1
- package/src/firebase/baseFunction.ts +167 -65
- package/src/firebase/billing/cancelSubscription.ts +2 -1
- package/src/firebase/billing/changePlan.ts +1 -1
- package/src/firebase/billing/createCheckoutSession.ts +2 -2
- package/src/firebase/billing/createCustomerPortal.ts +2 -1
- package/src/firebase/billing/refreshSubscriptionStatus.ts +1 -1
- package/src/firebase/billing/webhookHandler.ts +3 -1
- package/src/firebase/config/constants.ts +12 -2
- package/src/firebase/crud/aggregate.ts +2 -2
- package/src/firebase/crud/create.ts +10 -12
- package/src/firebase/crud/delete.ts +9 -11
- package/src/firebase/crud/get.ts +21 -11
- package/src/firebase/crud/list.ts +86 -29
- package/src/firebase/crud/update.ts +9 -11
- package/src/firebase/helpers/githubAccessHelper.ts +2 -1
- package/src/firebase/helpers/githubTeamAccessHelper.ts +2 -1
- package/src/firebase/index.ts +7 -0
- package/src/firebase/oauth/disconnect.ts +1 -1
- package/src/firebase/oauth/exchangeToken.ts +4 -4
- package/src/firebase/oauth/getConnections.ts +1 -1
- package/src/firebase/oauth/githubAccess.ts +3 -2
- package/src/firebase/oauth/refreshToken.ts +4 -4
- package/src/firebase/registerCrudFunctions.ts +127 -0
- package/src/firebase/scheduled/checkExpiredSubscriptions.ts +3 -2
- package/src/shared/billing/webhookHandler.ts +1 -1
- package/src/shared/errorHandling.ts +1 -1
- package/src/shared/utils/external/subscription.ts +19 -6
- package/src/shared/utils/firebaseHelpers.ts +3 -1
- package/src/shared/utils/internal/idempotency.ts +2 -1
- package/src/shared/utils/internal/monitoring.ts +2 -1
- package/src/shared/utils/internal/rateLimiter.ts +2 -1
- package/src/shared/utils.ts +56 -7
- package/src/vercel/api/billing/cancel.ts +2 -1
- package/src/vercel/api/billing/change-plan.ts +1 -1
- package/src/vercel/api/billing/create-checkout-session.ts +2 -2
- package/src/vercel/api/billing/customer-portal.ts +2 -1
- package/src/vercel/api/billing/refresh-subscription-status.ts +1 -1
- package/src/vercel/api/crud/create.ts +2 -3
- package/src/vercel/api/crud/delete.ts +0 -1
- package/src/vercel/api/crud/get.ts +2 -3
- package/src/vercel/api/crud/list.ts +2 -3
- package/src/vercel/api/crud/update.ts +2 -3
- package/src/vercel/api/oauth/check-github-access.ts +1 -1
- package/src/vercel/api/oauth/grant-github-access.ts +1 -1
- package/src/vercel/api/oauth/revoke-github-access.ts +1 -1
package/src/firebase/crud/get.ts
CHANGED
|
@@ -10,15 +10,19 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import * as v from 'valibot';
|
|
13
|
-
import { HIDDEN_STATUSES } from '@donotdev/core/server';
|
|
14
13
|
|
|
15
|
-
import {
|
|
16
|
-
|
|
14
|
+
import {
|
|
15
|
+
filterVisibleFields,
|
|
16
|
+
hasRoleAccess,
|
|
17
|
+
HIDDEN_STATUSES,
|
|
18
|
+
} from '@donotdev/core/server';
|
|
19
|
+
import type { UserRole } from '@donotdev/core/server';
|
|
17
20
|
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
18
21
|
|
|
22
|
+
import { transformFirestoreData } from '../../shared/index.js';
|
|
19
23
|
import { DoNotDevError } from '../../shared/utils.js';
|
|
20
24
|
import { createBaseFunction } from '../baseFunction.js';
|
|
21
|
-
import {
|
|
25
|
+
import { CRUD_READ_CONFIG } from '../config/constants.js';
|
|
22
26
|
|
|
23
27
|
import type {
|
|
24
28
|
CallableFunction,
|
|
@@ -37,9 +41,10 @@ function getEntityLogicFactory(
|
|
|
37
41
|
) {
|
|
38
42
|
return async function getEntityLogic(
|
|
39
43
|
data: GetEntityRequest,
|
|
40
|
-
context: { uid: string; request: CallableRequest<any> }
|
|
44
|
+
context: { uid: string; userRole: UserRole; request: CallableRequest<any> }
|
|
41
45
|
) {
|
|
42
46
|
const { id } = data;
|
|
47
|
+
const { userRole } = context;
|
|
43
48
|
|
|
44
49
|
// Get the document reference
|
|
45
50
|
const db = getFirebaseAdminFirestore();
|
|
@@ -53,12 +58,14 @@ function getEntityLogicFactory(
|
|
|
53
58
|
throw new DoNotDevError('Entity not found', 'not-found');
|
|
54
59
|
}
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
const isAdmin = context.request.auth?.token?.isAdmin === true;
|
|
61
|
+
const isAdmin = hasRoleAccess(userRole, 'admin'); // Uses role hierarchy
|
|
58
62
|
|
|
59
63
|
// Hide drafts/deleted from non-admin users (security: hidden statuses never reach public)
|
|
60
64
|
const docData = doc.data();
|
|
61
|
-
if (
|
|
65
|
+
if (
|
|
66
|
+
!isAdmin &&
|
|
67
|
+
(HIDDEN_STATUSES as readonly string[]).includes(docData?.status)
|
|
68
|
+
) {
|
|
62
69
|
throw new DoNotDevError('Entity not found', 'not-found');
|
|
63
70
|
}
|
|
64
71
|
|
|
@@ -66,7 +73,7 @@ function getEntityLogicFactory(
|
|
|
66
73
|
const filteredData = filterVisibleFields(
|
|
67
74
|
docData || {},
|
|
68
75
|
documentSchema,
|
|
69
|
-
|
|
76
|
+
userRole
|
|
70
77
|
);
|
|
71
78
|
|
|
72
79
|
// Transform the document data back to the application format
|
|
@@ -81,12 +88,14 @@ function getEntityLogicFactory(
|
|
|
81
88
|
* Generic function to get entities from any Firestore collection
|
|
82
89
|
* @param collection - The Firestore collection name
|
|
83
90
|
* @param documentSchema - The Valibot schema for document validation
|
|
91
|
+
* @param requiredRole - Minimum role required for this operation
|
|
84
92
|
* @param customSchema - Optional custom request schema
|
|
85
93
|
* @returns Firebase callable function
|
|
86
94
|
*/
|
|
87
95
|
export const getEntity = (
|
|
88
96
|
collection: string,
|
|
89
97
|
documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>,
|
|
98
|
+
requiredRole: UserRole,
|
|
90
99
|
customSchema?: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>
|
|
91
100
|
): CallableFunction<GetEntityRequest, Promise<any>> => {
|
|
92
101
|
const requestSchema =
|
|
@@ -96,9 +105,10 @@ export const getEntity = (
|
|
|
96
105
|
});
|
|
97
106
|
|
|
98
107
|
return createBaseFunction(
|
|
99
|
-
|
|
108
|
+
CRUD_READ_CONFIG,
|
|
100
109
|
requestSchema,
|
|
101
110
|
'get_entity',
|
|
102
|
-
getEntityLogicFactory(collection, documentSchema)
|
|
111
|
+
getEntityLogicFactory(collection, documentSchema),
|
|
112
|
+
requiredRole
|
|
103
113
|
);
|
|
104
114
|
};
|
|
@@ -9,12 +9,25 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import
|
|
13
|
-
import { HIDDEN_STATUSES } from '@donotdev/core/server';
|
|
12
|
+
import * as v from 'valibot';
|
|
14
13
|
|
|
15
|
-
import
|
|
14
|
+
import {
|
|
15
|
+
filterVisibleFields,
|
|
16
|
+
hasRoleAccess,
|
|
17
|
+
HIDDEN_STATUSES,
|
|
18
|
+
} from '@donotdev/core/server';
|
|
19
|
+
import type { UserRole } from '@donotdev/core/server';
|
|
20
|
+
import { getFirebaseAdminFirestore, Query } from '@donotdev/firebase/server';
|
|
16
21
|
|
|
17
|
-
import
|
|
22
|
+
import { transformFirestoreData } from '../../shared/index.js';
|
|
23
|
+
import { DoNotDevError } from '../../shared/utils.js';
|
|
24
|
+
import { createBaseFunction } from '../baseFunction.js';
|
|
25
|
+
import { CRUD_READ_CONFIG } from '../config/constants.js';
|
|
26
|
+
|
|
27
|
+
import type {
|
|
28
|
+
CallableFunction,
|
|
29
|
+
CallableRequest,
|
|
30
|
+
} from 'firebase-functions/v2/https';
|
|
18
31
|
|
|
19
32
|
export interface ListEntityRequest {
|
|
20
33
|
where?: Array<[string, any, any]>;
|
|
@@ -26,15 +39,6 @@ export interface ListEntityRequest {
|
|
|
26
39
|
query: string;
|
|
27
40
|
};
|
|
28
41
|
}
|
|
29
|
-
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
30
|
-
|
|
31
|
-
import { transformFirestoreData } from '../../shared/index.js';
|
|
32
|
-
import { filterVisibleFields } from '../../shared/index.js';
|
|
33
|
-
import { DoNotDevError } from '../../shared/utils.js';
|
|
34
|
-
import { createBaseFunction } from '../baseFunction.js';
|
|
35
|
-
import { CRUD_CONFIG } from '../config/constants.js';
|
|
36
|
-
|
|
37
|
-
import type { CallableFunction } from 'firebase-functions/v2/https';
|
|
38
42
|
|
|
39
43
|
/**
|
|
40
44
|
* Generic business logic for listing entities
|
|
@@ -42,17 +46,21 @@ import type { CallableFunction } from 'firebase-functions/v2/https';
|
|
|
42
46
|
*/
|
|
43
47
|
function listEntitiesLogicFactory(
|
|
44
48
|
collection: string,
|
|
45
|
-
documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown
|
|
49
|
+
documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>,
|
|
50
|
+
listFields?: string[]
|
|
46
51
|
) {
|
|
47
52
|
return async function listEntitiesLogic(
|
|
48
53
|
data: ListEntityRequest,
|
|
49
|
-
context: {
|
|
54
|
+
context: {
|
|
55
|
+
uid: string;
|
|
56
|
+
userRole: UserRole;
|
|
57
|
+
request: CallableRequest<ListEntityRequest>;
|
|
58
|
+
}
|
|
50
59
|
) {
|
|
51
|
-
const { where = [], orderBy = [], limit
|
|
60
|
+
const { where = [], orderBy = [], limit, startAfterId, search } = data;
|
|
61
|
+
const { userRole } = context;
|
|
52
62
|
|
|
53
|
-
|
|
54
|
-
const user = context.request.auth;
|
|
55
|
-
const isAdmin = user?.token?.isAdmin === true;
|
|
63
|
+
const isAdmin = hasRoleAccess(userRole, 'admin'); // Uses role hierarchy
|
|
56
64
|
|
|
57
65
|
// Start with a Query (not a CollectionReference)
|
|
58
66
|
const db = getFirebaseAdminFirestore();
|
|
@@ -95,17 +103,61 @@ function listEntitiesLogicFactory(
|
|
|
95
103
|
query = query.startAfter(startAfterDoc);
|
|
96
104
|
}
|
|
97
105
|
|
|
98
|
-
// Apply limit
|
|
99
|
-
|
|
106
|
+
// Apply limit only if provided (no limit = fetch all)
|
|
107
|
+
if (limit !== undefined && limit > 0) {
|
|
108
|
+
query = query.limit(limit);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// DEBUG: Log query details
|
|
112
|
+
console.log(
|
|
113
|
+
`[listEntities] Collection: ${collection}, UserRole: ${userRole}, IsAdmin: ${isAdmin}`
|
|
114
|
+
);
|
|
115
|
+
console.log(
|
|
116
|
+
`[listEntities] Filters - where: ${JSON.stringify(where)}, orderBy: ${JSON.stringify(orderBy)}, limit: ${limit}`
|
|
117
|
+
);
|
|
100
118
|
|
|
101
119
|
// Execute the query
|
|
102
120
|
const snapshot = await query.get();
|
|
103
121
|
|
|
122
|
+
// DEBUG: Log result count
|
|
123
|
+
console.log(
|
|
124
|
+
`[listEntities] Query returned ${snapshot.docs.length} documents`
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// DEBUG: Log schema info
|
|
128
|
+
const schemaHasEntries = !!(documentSchema as any)?.entries;
|
|
129
|
+
console.log(`[listEntities] Schema has entries: ${schemaHasEntries}`);
|
|
130
|
+
|
|
104
131
|
// Filter document fields based on visibility and user role
|
|
105
|
-
const docs = snapshot.docs.map((doc: any) =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
132
|
+
const docs = snapshot.docs.map((doc: any) => {
|
|
133
|
+
const visibleData = filterVisibleFields(
|
|
134
|
+
doc.data() || {},
|
|
135
|
+
documentSchema,
|
|
136
|
+
userRole
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// If listFields specified, filter to only those fields (plus id always)
|
|
140
|
+
if (listFields && listFields.length > 0) {
|
|
141
|
+
const filtered: Record<string, any> = { id: doc.id };
|
|
142
|
+
for (const field of listFields) {
|
|
143
|
+
if (field in visibleData) {
|
|
144
|
+
filtered[field] = visibleData[field];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return filtered;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// No listFields restriction, return all visible fields
|
|
151
|
+
return {
|
|
152
|
+
id: doc.id,
|
|
153
|
+
...visibleData,
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// DEBUG: Log filtered docs
|
|
158
|
+
console.log(
|
|
159
|
+
`[listEntities] Filtered docs count: ${docs.length}, first doc keys: ${docs[0] ? Object.keys(docs[0]).join(', ') : 'N/A'}`
|
|
160
|
+
);
|
|
109
161
|
|
|
110
162
|
// Return the paginated result with metadata
|
|
111
163
|
return {
|
|
@@ -121,13 +173,17 @@ function listEntitiesLogicFactory(
|
|
|
121
173
|
* Generic function to list entities from any Firestore collection
|
|
122
174
|
* @param collection - The Firestore collection name
|
|
123
175
|
* @param documentSchema - The Valibot schema for document validation
|
|
176
|
+
* @param requiredRole - Minimum role required for this operation
|
|
124
177
|
* @param customSchema - Optional custom request schema
|
|
178
|
+
* @param listFields - Optional array of field names to include (plus id). If not provided, all visible fields are returned.
|
|
125
179
|
* @returns Firebase callable function
|
|
126
180
|
*/
|
|
127
181
|
export const listEntities = (
|
|
128
182
|
collection: string,
|
|
129
183
|
documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>,
|
|
130
|
-
|
|
184
|
+
requiredRole: UserRole,
|
|
185
|
+
customSchema?: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>,
|
|
186
|
+
listFields?: string[]
|
|
131
187
|
): CallableFunction<ListEntityRequest, Promise<any>> => {
|
|
132
188
|
const requestSchema =
|
|
133
189
|
customSchema ||
|
|
@@ -136,7 +192,7 @@ export const listEntities = (
|
|
|
136
192
|
orderBy: v.optional(
|
|
137
193
|
v.array(v.tuple([v.string(), v.picklist(['asc', 'desc'])]))
|
|
138
194
|
),
|
|
139
|
-
limit: v.optional(v.pipe(v.number(), v.minValue(1)
|
|
195
|
+
limit: v.optional(v.pipe(v.number(), v.minValue(1))),
|
|
140
196
|
startAfterId: v.optional(v.string()),
|
|
141
197
|
search: v.optional(
|
|
142
198
|
v.object({
|
|
@@ -147,9 +203,10 @@ export const listEntities = (
|
|
|
147
203
|
});
|
|
148
204
|
|
|
149
205
|
return createBaseFunction(
|
|
150
|
-
|
|
206
|
+
CRUD_READ_CONFIG,
|
|
151
207
|
requestSchema,
|
|
152
208
|
'list_entities',
|
|
153
|
-
listEntitiesLogicFactory(collection, documentSchema)
|
|
209
|
+
listEntitiesLogicFactory(collection, documentSchema, listFields),
|
|
210
|
+
requiredRole
|
|
154
211
|
);
|
|
155
212
|
};
|
|
@@ -11,19 +11,16 @@
|
|
|
11
11
|
|
|
12
12
|
import * as v from 'valibot';
|
|
13
13
|
|
|
14
|
-
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
15
14
|
import { DEFAULT_STATUS_VALUE } from '@donotdev/core/server';
|
|
15
|
+
import type { UserRole } from '@donotdev/core/server';
|
|
16
|
+
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
16
17
|
|
|
17
18
|
import {
|
|
18
19
|
prepareForFirestore,
|
|
19
20
|
transformFirestoreData,
|
|
20
21
|
} from '../../shared/index.js';
|
|
21
22
|
import { updateMetadata } from '../../shared/index.js';
|
|
22
|
-
import {
|
|
23
|
-
assertAdmin,
|
|
24
|
-
validateDocument,
|
|
25
|
-
DoNotDevError,
|
|
26
|
-
} from '../../shared/utils.js';
|
|
23
|
+
import { DoNotDevError, validateDocument } from '../../shared/utils.js';
|
|
27
24
|
import { createBaseFunction } from '../baseFunction.js';
|
|
28
25
|
import { CRUD_CONFIG } from '../config/constants.js';
|
|
29
26
|
|
|
@@ -48,12 +45,10 @@ function updateEntityLogicFactory(
|
|
|
48
45
|
) {
|
|
49
46
|
return async function updateEntityLogic(
|
|
50
47
|
data: UpdateEntityRequest,
|
|
51
|
-
context: { uid: string; request: CallableRequest<any> }
|
|
48
|
+
context: { uid: string; userRole: UserRole; request: CallableRequest<any> }
|
|
52
49
|
) {
|
|
53
50
|
const { id, payload, idempotencyKey } = data;
|
|
54
|
-
|
|
55
|
-
// Ensure the user is an admin
|
|
56
|
-
const uid = await assertAdmin(context.uid);
|
|
51
|
+
const { uid } = context;
|
|
57
52
|
|
|
58
53
|
// Idempotency check if key provided
|
|
59
54
|
if (idempotencyKey) {
|
|
@@ -129,12 +124,14 @@ function updateEntityLogicFactory(
|
|
|
129
124
|
* Generic function to update entities in any Firestore collection
|
|
130
125
|
* @param collection - The Firestore collection name
|
|
131
126
|
* @param documentSchema - The Valibot schema for document validation
|
|
127
|
+
* @param requiredRole - Minimum role required for this operation
|
|
132
128
|
* @param customSchema - Optional custom request schema
|
|
133
129
|
* @returns Firebase callable function
|
|
134
130
|
*/
|
|
135
131
|
export const updateEntity = (
|
|
136
132
|
collection: string,
|
|
137
133
|
documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>,
|
|
134
|
+
requiredRole: UserRole,
|
|
138
135
|
customSchema?: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>
|
|
139
136
|
): CallableFunction<UpdateEntityRequest, Promise<any>> => {
|
|
140
137
|
const requestSchema =
|
|
@@ -149,6 +146,7 @@ export const updateEntity = (
|
|
|
149
146
|
CRUD_CONFIG,
|
|
150
147
|
requestSchema,
|
|
151
148
|
'update_entity',
|
|
152
|
-
updateEntityLogicFactory(collection, documentSchema)
|
|
149
|
+
updateEntityLogicFactory(collection, documentSchema),
|
|
150
|
+
requiredRole
|
|
153
151
|
);
|
|
154
152
|
};
|
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { logger } from 'firebase-functions/v2';
|
|
13
|
+
|
|
12
14
|
import {
|
|
13
15
|
getFirebaseAdminAuth,
|
|
14
16
|
getFirebaseAdminFirestore,
|
|
15
17
|
} from '@donotdev/firebase/server';
|
|
16
|
-
import { logger } from 'firebase-functions/v2';
|
|
17
18
|
|
|
18
19
|
import { handleError } from '../../shared/errorHandling.js';
|
|
19
20
|
import { GitHubApiService } from '../../shared/index.js';
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { getFirebaseAdminAuth } from '@donotdev/firebase/server';
|
|
13
12
|
import { logger } from 'firebase-functions/v2';
|
|
14
13
|
|
|
14
|
+
import { getFirebaseAdminAuth } from '@donotdev/firebase/server';
|
|
15
|
+
|
|
15
16
|
import { handleError } from '../../shared/errorHandling.js';
|
|
16
17
|
import { GitHubApiService } from '../../shared/index.js';
|
|
17
18
|
|
package/src/firebase/index.ts
CHANGED
|
@@ -19,3 +19,10 @@ export * from './scheduled/index.js';
|
|
|
19
19
|
|
|
20
20
|
// Base function for creating Firebase functions
|
|
21
21
|
export { createBaseFunction } from './baseFunction.js';
|
|
22
|
+
|
|
23
|
+
// CRUD function registration utility
|
|
24
|
+
export {
|
|
25
|
+
registerCrudFunctions,
|
|
26
|
+
createCrudFunctions,
|
|
27
|
+
type CrudFunctions,
|
|
28
|
+
} from './registerCrudFunctions.js';
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
13
12
|
import { onCall, HttpsError } from 'firebase-functions/v2/https';
|
|
14
13
|
|
|
15
14
|
import { OAUTH_PARTNERS } from '@donotdev/core/server';
|
|
16
15
|
import type { OAuthPartnerId, OAuthPurpose } from '@donotdev/core/server';
|
|
16
|
+
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
17
17
|
|
|
18
18
|
import { assertAuthenticated } from '../../shared/utils.js';
|
|
19
19
|
|
|
@@ -9,10 +9,6 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
getFirebaseAdminFirestore,
|
|
14
|
-
FieldValue,
|
|
15
|
-
} from '@donotdev/firebase/server';
|
|
16
12
|
import { onCall, HttpsError } from 'firebase-functions/v2/https';
|
|
17
13
|
|
|
18
14
|
import {
|
|
@@ -20,6 +16,10 @@ import {
|
|
|
20
16
|
type ExchangeTokenRequest,
|
|
21
17
|
type OAuthPartnerId,
|
|
22
18
|
} from '@donotdev/core/server';
|
|
19
|
+
import {
|
|
20
|
+
getFirebaseAdminFirestore,
|
|
21
|
+
FieldValue,
|
|
22
|
+
} from '@donotdev/firebase/server';
|
|
23
23
|
|
|
24
24
|
import { assertAuthenticated } from '../../shared/utils.js';
|
|
25
25
|
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
13
12
|
import { onCall, HttpsError } from 'firebase-functions/v2/https';
|
|
14
13
|
|
|
15
14
|
import type { OAuthPurpose } from '@donotdev/core/server';
|
|
15
|
+
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
16
16
|
|
|
17
17
|
import { assertAuthenticated } from '../../shared/utils.js';
|
|
18
18
|
|
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { getFirebaseAdminAuth } from '@donotdev/firebase/server';
|
|
13
12
|
import { logger } from 'firebase-functions/v2';
|
|
14
|
-
import { onCall
|
|
13
|
+
import { onCall } from 'firebase-functions/v2/https';
|
|
15
14
|
import * as v from 'valibot';
|
|
16
15
|
|
|
17
16
|
import {
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
type RevokeGitHubAccessRequest,
|
|
23
22
|
type CheckGitHubAccessRequest,
|
|
24
23
|
} from '@donotdev/core/server';
|
|
24
|
+
import { getFirebaseAdminAuth } from '@donotdev/firebase/server';
|
|
25
25
|
|
|
26
26
|
import { handleError } from '../../shared/errorHandling.js';
|
|
27
27
|
import { GitHubApiService } from '../../shared/index.js';
|
|
@@ -29,6 +29,7 @@ import { assertAuthenticated } from '../../shared/utils.js';
|
|
|
29
29
|
import { AUTH_CONFIG } from '../config/constants.js';
|
|
30
30
|
import { githubPersonalAccessToken } from '../config/secrets.js';
|
|
31
31
|
|
|
32
|
+
import type { CallableRequest } from 'firebase-functions/v2/https';
|
|
32
33
|
import type { CallableFunction } from 'firebase-functions/v2/https';
|
|
33
34
|
|
|
34
35
|
const OAUTH_CONFIG = {
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
getFirebaseAdminFirestore,
|
|
14
|
-
FieldValue,
|
|
15
|
-
} from '@donotdev/firebase/server';
|
|
16
12
|
import { onCall, HttpsError } from 'firebase-functions/v2/https';
|
|
17
13
|
|
|
18
14
|
import {
|
|
19
15
|
OAUTH_PARTNERS,
|
|
20
16
|
type OAuthRefreshRequest,
|
|
21
17
|
} from '@donotdev/core/server';
|
|
18
|
+
import {
|
|
19
|
+
getFirebaseAdminFirestore,
|
|
20
|
+
FieldValue,
|
|
21
|
+
} from '@donotdev/firebase/server';
|
|
22
22
|
|
|
23
23
|
import { assertAuthenticated } from '../../shared/utils.js';
|
|
24
24
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// packages/functions/src/firebase/registerCrudFunctions.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Auto-register CRUD functions from entities
|
|
5
|
+
* @description Utility to automatically generate CRUD Cloud Functions for all entities
|
|
6
|
+
*
|
|
7
|
+
* @version 0.0.2
|
|
8
|
+
* @since 0.0.1
|
|
9
|
+
* @author AMBROISE PARK Consulting
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createSchemas } from '@donotdev/core/server';
|
|
13
|
+
import type { Entity } from '@donotdev/core/server';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
createEntity,
|
|
17
|
+
getEntity,
|
|
18
|
+
updateEntity,
|
|
19
|
+
deleteEntity,
|
|
20
|
+
listEntities,
|
|
21
|
+
} from './crud/index.js';
|
|
22
|
+
|
|
23
|
+
import type { HttpsFunction } from 'firebase-functions/v2/https';
|
|
24
|
+
|
|
25
|
+
interface RegisterOptions {
|
|
26
|
+
prefix?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** CRUD functions object returned by createCrudFunctions */
|
|
30
|
+
export type CrudFunctions = Record<string, HttpsFunction>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create CRUD functions for all entities (ESM-friendly)
|
|
34
|
+
* Returns an object of functions that can be spread/exported
|
|
35
|
+
*
|
|
36
|
+
* @param entities - Object of { key: Entity } (from `import * as entities from 'entities'`)
|
|
37
|
+
* @param options - Optional configuration
|
|
38
|
+
* @returns Object of CRUD functions keyed by name
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // ESM pattern - use with build-time export generation
|
|
43
|
+
* import * as entities from 'entities';
|
|
44
|
+
* import { createCrudFunctions } from '@donotdev/functions/firebase';
|
|
45
|
+
*
|
|
46
|
+
* export const crud = createCrudFunctions(entities);
|
|
47
|
+
* // Build process generates: export const { create_cars, ... } = crud;
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function createCrudFunctions(
|
|
51
|
+
entities: Record<string, Entity | unknown>,
|
|
52
|
+
options: RegisterOptions = {}
|
|
53
|
+
): CrudFunctions {
|
|
54
|
+
const { prefix = '' } = options;
|
|
55
|
+
const functions: CrudFunctions = {};
|
|
56
|
+
|
|
57
|
+
for (const [key, value] of Object.entries(entities)) {
|
|
58
|
+
if (!isEntity(value)) continue;
|
|
59
|
+
|
|
60
|
+
const entity = value as Entity;
|
|
61
|
+
const col = entity.collection;
|
|
62
|
+
const schemas = createSchemas(entity);
|
|
63
|
+
const access = entity.access;
|
|
64
|
+
|
|
65
|
+
functions[`${prefix}create_${col}`] = createEntity(
|
|
66
|
+
col,
|
|
67
|
+
schemas.create,
|
|
68
|
+
access.create
|
|
69
|
+
);
|
|
70
|
+
functions[`${prefix}get_${col}`] = getEntity(col, schemas.get, access.read);
|
|
71
|
+
// Use schemas.get for visibility filtering, entity.listFields for field selection
|
|
72
|
+
functions[`${prefix}list_${col}`] = listEntities(
|
|
73
|
+
col,
|
|
74
|
+
schemas.get,
|
|
75
|
+
access.read,
|
|
76
|
+
undefined,
|
|
77
|
+
entity.listFields
|
|
78
|
+
);
|
|
79
|
+
// Always create listCard - uses same schemas.get, field selection via listCardFields ?? listFields ?? undefined
|
|
80
|
+
functions[`${prefix}listCard_${col}`] = listEntities(
|
|
81
|
+
col,
|
|
82
|
+
schemas.get,
|
|
83
|
+
access.read,
|
|
84
|
+
undefined,
|
|
85
|
+
entity.listCardFields ?? entity.listFields ?? undefined
|
|
86
|
+
);
|
|
87
|
+
functions[`${prefix}update_${col}`] = updateEntity(
|
|
88
|
+
col,
|
|
89
|
+
schemas.update,
|
|
90
|
+
access.update
|
|
91
|
+
);
|
|
92
|
+
functions[`${prefix}delete_${col}`] = deleteEntity(col, access.delete);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return functions;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @deprecated Use createCrudFunctions() for ESM. This mutates target which doesn't work in ESM.
|
|
100
|
+
* Auto-register CRUD functions for all entities (CJS pattern)
|
|
101
|
+
*/
|
|
102
|
+
export function registerCrudFunctions(
|
|
103
|
+
entities: Record<string, Entity | unknown>,
|
|
104
|
+
target?: Record<string, any>,
|
|
105
|
+
options: RegisterOptions = {}
|
|
106
|
+
): CrudFunctions {
|
|
107
|
+
const functions = createCrudFunctions(entities, options);
|
|
108
|
+
|
|
109
|
+
// If target provided, mutate it (CJS compat)
|
|
110
|
+
if (target) {
|
|
111
|
+
Object.assign(target, functions);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return functions;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Type guard to check if a value is an Entity
|
|
119
|
+
*/
|
|
120
|
+
function isEntity(value: unknown): value is Entity {
|
|
121
|
+
return (
|
|
122
|
+
typeof value === 'object' &&
|
|
123
|
+
value !== null &&
|
|
124
|
+
'collection' in value &&
|
|
125
|
+
'fields' in value
|
|
126
|
+
);
|
|
127
|
+
}
|
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { logger } from 'firebase-functions/v2';
|
|
13
|
+
import { onSchedule } from 'firebase-functions/v2/scheduler';
|
|
14
|
+
|
|
12
15
|
import {
|
|
13
16
|
getFirebaseAdminAuth,
|
|
14
17
|
getFirebaseAdminFirestore,
|
|
15
18
|
} from '@donotdev/firebase/server';
|
|
16
|
-
import { logger } from 'firebase-functions/v2';
|
|
17
|
-
import { onSchedule } from 'firebase-functions/v2/scheduler';
|
|
18
19
|
|
|
19
20
|
import { handleError } from '../../shared/errorHandling.js';
|
|
20
21
|
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
import Stripe from 'stripe';
|
|
13
13
|
|
|
14
|
-
import type { StripeBackConfig } from '@donotdev/core/server';
|
|
15
14
|
import {
|
|
16
15
|
validateStripeBackConfig,
|
|
17
16
|
SUBSCRIPTION_STATUS,
|
|
18
17
|
SUBSCRIPTION_TIERS,
|
|
19
18
|
} from '@donotdev/core/server';
|
|
19
|
+
import type { StripeBackConfig } from '@donotdev/core/server';
|
|
20
20
|
|
|
21
21
|
import { handleError } from '../errorHandling.js';
|
|
22
22
|
import { logger } from '../logger.js';
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import * as v from 'valibot';
|
|
13
12
|
import { HttpsError } from 'firebase-functions/v2/https';
|
|
13
|
+
import * as v from 'valibot';
|
|
14
14
|
|
|
15
15
|
import type { ErrorCode } from '@donotdev/core/server';
|
|
16
16
|
import { EntityHookError } from '@donotdev/core/server';
|