@donotdev/functions 0.0.6 → 0.0.7
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 +80 -25
- 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 +4 -5
- 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
60
|
const { where = [], orderBy = [], limit = 50, 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();
|
|
@@ -98,14 +106,56 @@ function listEntitiesLogicFactory(
|
|
|
98
106
|
// Apply limit for page size
|
|
99
107
|
query = query.limit(limit);
|
|
100
108
|
|
|
109
|
+
// DEBUG: Log query details
|
|
110
|
+
console.log(
|
|
111
|
+
`[listEntities] Collection: ${collection}, UserRole: ${userRole}, IsAdmin: ${isAdmin}`
|
|
112
|
+
);
|
|
113
|
+
console.log(
|
|
114
|
+
`[listEntities] Filters - where: ${JSON.stringify(where)}, orderBy: ${JSON.stringify(orderBy)}, limit: ${limit}`
|
|
115
|
+
);
|
|
116
|
+
|
|
101
117
|
// Execute the query
|
|
102
118
|
const snapshot = await query.get();
|
|
103
119
|
|
|
120
|
+
// DEBUG: Log result count
|
|
121
|
+
console.log(
|
|
122
|
+
`[listEntities] Query returned ${snapshot.docs.length} documents`
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// DEBUG: Log schema info
|
|
126
|
+
const schemaHasEntries = !!(documentSchema as any)?.entries;
|
|
127
|
+
console.log(`[listEntities] Schema has entries: ${schemaHasEntries}`);
|
|
128
|
+
|
|
104
129
|
// Filter document fields based on visibility and user role
|
|
105
|
-
const docs = snapshot.docs.map((doc: any) =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
130
|
+
const docs = snapshot.docs.map((doc: any) => {
|
|
131
|
+
const visibleData = filterVisibleFields(
|
|
132
|
+
doc.data() || {},
|
|
133
|
+
documentSchema,
|
|
134
|
+
userRole
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// If listFields specified, filter to only those fields (plus id always)
|
|
138
|
+
if (listFields && listFields.length > 0) {
|
|
139
|
+
const filtered: Record<string, any> = { id: doc.id };
|
|
140
|
+
for (const field of listFields) {
|
|
141
|
+
if (field in visibleData) {
|
|
142
|
+
filtered[field] = visibleData[field];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return filtered;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// No listFields restriction, return all visible fields
|
|
149
|
+
return {
|
|
150
|
+
id: doc.id,
|
|
151
|
+
...visibleData,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// DEBUG: Log filtered docs
|
|
156
|
+
console.log(
|
|
157
|
+
`[listEntities] Filtered docs count: ${docs.length}, first doc keys: ${docs[0] ? Object.keys(docs[0]).join(', ') : 'N/A'}`
|
|
158
|
+
);
|
|
109
159
|
|
|
110
160
|
// Return the paginated result with metadata
|
|
111
161
|
return {
|
|
@@ -121,13 +171,17 @@ function listEntitiesLogicFactory(
|
|
|
121
171
|
* Generic function to list entities from any Firestore collection
|
|
122
172
|
* @param collection - The Firestore collection name
|
|
123
173
|
* @param documentSchema - The Valibot schema for document validation
|
|
174
|
+
* @param requiredRole - Minimum role required for this operation
|
|
124
175
|
* @param customSchema - Optional custom request schema
|
|
176
|
+
* @param listFields - Optional array of field names to include (plus id). If not provided, all visible fields are returned.
|
|
125
177
|
* @returns Firebase callable function
|
|
126
178
|
*/
|
|
127
179
|
export const listEntities = (
|
|
128
180
|
collection: string,
|
|
129
181
|
documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>,
|
|
130
|
-
|
|
182
|
+
requiredRole: UserRole,
|
|
183
|
+
customSchema?: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>,
|
|
184
|
+
listFields?: string[]
|
|
131
185
|
): CallableFunction<ListEntityRequest, Promise<any>> => {
|
|
132
186
|
const requestSchema =
|
|
133
187
|
customSchema ||
|
|
@@ -147,9 +201,10 @@ export const listEntities = (
|
|
|
147
201
|
});
|
|
148
202
|
|
|
149
203
|
return createBaseFunction(
|
|
150
|
-
|
|
204
|
+
CRUD_READ_CONFIG,
|
|
151
205
|
requestSchema,
|
|
152
206
|
'list_entities',
|
|
153
|
-
listEntitiesLogicFactory(collection, documentSchema)
|
|
207
|
+
listEntitiesLogicFactory(collection, documentSchema, listFields),
|
|
208
|
+
requiredRole
|
|
154
209
|
);
|
|
155
210
|
};
|
|
@@ -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';
|
|
@@ -9,15 +9,14 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
getFirebaseAdminAuth,
|
|
14
|
-
getFirebaseAdminFirestore,
|
|
15
|
-
} from '@donotdev/firebase/server';
|
|
16
|
-
|
|
17
12
|
import type {
|
|
18
13
|
SubscriptionStatus,
|
|
19
14
|
SubscriptionClaims,
|
|
20
15
|
} from '@donotdev/core/server';
|
|
16
|
+
import {
|
|
17
|
+
getFirebaseAdminAuth,
|
|
18
|
+
getFirebaseAdminFirestore,
|
|
19
|
+
} from '@donotdev/firebase/server';
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
22
|
* Maps Stripe price ID to subscription tier
|
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { onCall
|
|
12
|
+
import { onCall } from 'firebase-functions/v2/https';
|
|
13
13
|
import * as v from 'valibot';
|
|
14
14
|
|
|
15
15
|
import { withSchemaValidation } from './schemaValidation.js';
|
|
16
16
|
|
|
17
|
+
import type { CallableRequest } from 'firebase-functions/v2/https';
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* Creates a Firebase onCall function with schema validation
|
|
19
21
|
*
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
13
12
|
import { logger } from 'firebase-functions/v2';
|
|
14
13
|
|
|
14
|
+
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
15
|
+
|
|
15
16
|
export interface IdempotencyResult<T = any> {
|
|
16
17
|
result: T;
|
|
17
18
|
processedAt: string;
|