@clipin/convex-wearables 0.0.1

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 (143) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +616 -0
  3. package/dist/client/_generated/_ignore.d.ts +1 -0
  4. package/dist/client/_generated/_ignore.d.ts.map +1 -0
  5. package/dist/client/_generated/_ignore.js +4 -0
  6. package/dist/client/_generated/_ignore.js.map +1 -0
  7. package/dist/client/index.d.ts +244 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +555 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/client/types.d.ts +689 -0
  12. package/dist/client/types.d.ts.map +1 -0
  13. package/dist/client/types.js +112 -0
  14. package/dist/client/types.js.map +1 -0
  15. package/dist/component/_generated/_ignore.d.ts +1 -0
  16. package/dist/component/_generated/_ignore.d.ts.map +1 -0
  17. package/dist/component/_generated/_ignore.js +4 -0
  18. package/dist/component/_generated/_ignore.js.map +1 -0
  19. package/dist/component/_generated/api.d.ts +13 -0
  20. package/dist/component/_generated/api.d.ts.map +1 -0
  21. package/dist/component/_generated/api.js +14 -0
  22. package/dist/component/_generated/api.js.map +1 -0
  23. package/dist/component/_generated/dataModel.d.ts +28 -0
  24. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  25. package/dist/component/_generated/dataModel.js +11 -0
  26. package/dist/component/_generated/dataModel.js.map +1 -0
  27. package/dist/component/_generated/server.d.ts +23 -0
  28. package/dist/component/_generated/server.d.ts.map +1 -0
  29. package/dist/component/_generated/server.js +18 -0
  30. package/dist/component/_generated/server.js.map +1 -0
  31. package/dist/component/backfillJobs.d.ts +121 -0
  32. package/dist/component/backfillJobs.d.ts.map +1 -0
  33. package/dist/component/backfillJobs.js +233 -0
  34. package/dist/component/backfillJobs.js.map +1 -0
  35. package/dist/component/connections.d.ts +159 -0
  36. package/dist/component/connections.d.ts.map +1 -0
  37. package/dist/component/connections.js +288 -0
  38. package/dist/component/connections.js.map +1 -0
  39. package/dist/component/convex.config.d.ts +3 -0
  40. package/dist/component/convex.config.d.ts.map +1 -0
  41. package/dist/component/convex.config.js +6 -0
  42. package/dist/component/convex.config.js.map +1 -0
  43. package/dist/component/dataPoints.d.ts +81 -0
  44. package/dist/component/dataPoints.d.ts.map +1 -0
  45. package/dist/component/dataPoints.js +258 -0
  46. package/dist/component/dataPoints.js.map +1 -0
  47. package/dist/component/dataSources.d.ts +56 -0
  48. package/dist/component/dataSources.d.ts.map +1 -0
  49. package/dist/component/dataSources.js +95 -0
  50. package/dist/component/dataSources.js.map +1 -0
  51. package/dist/component/events.d.ts +203 -0
  52. package/dist/component/events.d.ts.map +1 -0
  53. package/dist/component/events.js +251 -0
  54. package/dist/component/events.js.map +1 -0
  55. package/dist/component/garminBackfill.d.ts +40 -0
  56. package/dist/component/garminBackfill.d.ts.map +1 -0
  57. package/dist/component/garminBackfill.js +296 -0
  58. package/dist/component/garminBackfill.js.map +1 -0
  59. package/dist/component/garminWebhooks.d.ts +17 -0
  60. package/dist/component/garminWebhooks.d.ts.map +1 -0
  61. package/dist/component/garminWebhooks.js +505 -0
  62. package/dist/component/garminWebhooks.js.map +1 -0
  63. package/dist/component/httpHandlers.d.ts +32 -0
  64. package/dist/component/httpHandlers.d.ts.map +1 -0
  65. package/dist/component/httpHandlers.js +131 -0
  66. package/dist/component/httpHandlers.js.map +1 -0
  67. package/dist/component/lifecycle.d.ts +12 -0
  68. package/dist/component/lifecycle.d.ts.map +1 -0
  69. package/dist/component/lifecycle.js +79 -0
  70. package/dist/component/lifecycle.js.map +1 -0
  71. package/dist/component/menstrualCycles.d.ts +98 -0
  72. package/dist/component/menstrualCycles.d.ts.map +1 -0
  73. package/dist/component/menstrualCycles.js +112 -0
  74. package/dist/component/menstrualCycles.js.map +1 -0
  75. package/dist/component/oauthActions.d.ts +52 -0
  76. package/dist/component/oauthActions.d.ts.map +1 -0
  77. package/dist/component/oauthActions.js +208 -0
  78. package/dist/component/oauthActions.js.map +1 -0
  79. package/dist/component/oauthStates.d.ts +47 -0
  80. package/dist/component/oauthStates.d.ts.map +1 -0
  81. package/dist/component/oauthStates.js +77 -0
  82. package/dist/component/oauthStates.js.map +1 -0
  83. package/dist/component/providerSettings.d.ts +15 -0
  84. package/dist/component/providerSettings.d.ts.map +1 -0
  85. package/dist/component/providerSettings.js +57 -0
  86. package/dist/component/providerSettings.js.map +1 -0
  87. package/dist/component/providers/garmin.d.ts +306 -0
  88. package/dist/component/providers/garmin.d.ts.map +1 -0
  89. package/dist/component/providers/garmin.js +675 -0
  90. package/dist/component/providers/garmin.js.map +1 -0
  91. package/dist/component/providers/oauth.d.ts +42 -0
  92. package/dist/component/providers/oauth.d.ts.map +1 -0
  93. package/dist/component/providers/oauth.js +181 -0
  94. package/dist/component/providers/oauth.js.map +1 -0
  95. package/dist/component/providers/polar.d.ts +6 -0
  96. package/dist/component/providers/polar.d.ts.map +1 -0
  97. package/dist/component/providers/polar.js +175 -0
  98. package/dist/component/providers/polar.js.map +1 -0
  99. package/dist/component/providers/registry.d.ts +14 -0
  100. package/dist/component/providers/registry.d.ts.map +1 -0
  101. package/dist/component/providers/registry.js +32 -0
  102. package/dist/component/providers/registry.js.map +1 -0
  103. package/dist/component/providers/strava.d.ts +45 -0
  104. package/dist/component/providers/strava.d.ts.map +1 -0
  105. package/dist/component/providers/strava.js +182 -0
  106. package/dist/component/providers/strava.js.map +1 -0
  107. package/dist/component/providers/suunto.d.ts +5 -0
  108. package/dist/component/providers/suunto.d.ts.map +1 -0
  109. package/dist/component/providers/suunto.js +502 -0
  110. package/dist/component/providers/suunto.js.map +1 -0
  111. package/dist/component/providers/types.d.ts +139 -0
  112. package/dist/component/providers/types.d.ts.map +1 -0
  113. package/dist/component/providers/types.js +5 -0
  114. package/dist/component/providers/types.js.map +1 -0
  115. package/dist/component/providers/whoop.d.ts +4 -0
  116. package/dist/component/providers/whoop.d.ts.map +1 -0
  117. package/dist/component/providers/whoop.js +439 -0
  118. package/dist/component/providers/whoop.js.map +1 -0
  119. package/dist/component/schema.d.ts +429 -0
  120. package/dist/component/schema.d.ts.map +1 -0
  121. package/dist/component/schema.js +282 -0
  122. package/dist/component/schema.js.map +1 -0
  123. package/dist/component/sdkPush.d.ts +143 -0
  124. package/dist/component/sdkPush.d.ts.map +1 -0
  125. package/dist/component/sdkPush.js +338 -0
  126. package/dist/component/sdkPush.js.map +1 -0
  127. package/dist/component/summaries.d.ts +129 -0
  128. package/dist/component/summaries.d.ts.map +1 -0
  129. package/dist/component/summaries.js +129 -0
  130. package/dist/component/summaries.js.map +1 -0
  131. package/dist/component/syncJobs.d.ts +142 -0
  132. package/dist/component/syncJobs.d.ts.map +1 -0
  133. package/dist/component/syncJobs.js +136 -0
  134. package/dist/component/syncJobs.js.map +1 -0
  135. package/dist/component/syncWorkflow.d.ts +99 -0
  136. package/dist/component/syncWorkflow.d.ts.map +1 -0
  137. package/dist/component/syncWorkflow.js +579 -0
  138. package/dist/component/syncWorkflow.js.map +1 -0
  139. package/dist/component/workflowManager.d.ts +3 -0
  140. package/dist/component/workflowManager.d.ts.map +1 -0
  141. package/dist/component/workflowManager.js +17 -0
  142. package/dist/component/workflowManager.js.map +1 -0
  143. package/package.json +84 -0
@@ -0,0 +1,208 @@
1
+ /**
2
+ * OAuth flow Convex actions.
3
+ *
4
+ * Actions (not mutations) because they make external HTTP calls to
5
+ * provider token endpoints and APIs.
6
+ */
7
+ import { v } from "convex/values";
8
+ import { api, internal } from "./_generated/api";
9
+ import { action, internalAction } from "./_generated/server";
10
+ import { buildAuthorizationUrl, exchangeCodeForTokens, generateCodeChallenge, generateRandomString, refreshAccessToken, } from "./providers/oauth";
11
+ import { getProvider } from "./providers/registry";
12
+ import { providerName } from "./schema";
13
+ // ---------------------------------------------------------------------------
14
+ // Generate authorization URL
15
+ // ---------------------------------------------------------------------------
16
+ /**
17
+ * Generate an OAuth authorization URL for a provider.
18
+ *
19
+ * Stores the state token in the oauthStates table and returns the URL
20
+ * the client should redirect the user to.
21
+ */
22
+ export const generateAuthUrl = action({
23
+ args: {
24
+ userId: v.string(),
25
+ provider: providerName,
26
+ clientId: v.string(),
27
+ clientSecret: v.string(),
28
+ subscriptionKey: v.optional(v.string()),
29
+ redirectUri: v.string(),
30
+ },
31
+ returns: v.string(),
32
+ handler: async (ctx, args) => {
33
+ await ctx.runMutation(internal.providerSettings.upsertCredentials, {
34
+ provider: args.provider,
35
+ clientId: args.clientId,
36
+ clientSecret: args.clientSecret,
37
+ subscriptionKey: args.subscriptionKey,
38
+ });
39
+ const providerDef = getProvider(args.provider);
40
+ if (!providerDef) {
41
+ throw new Error(`Provider "${args.provider}" is not implemented`);
42
+ }
43
+ const credentials = {
44
+ clientId: args.clientId,
45
+ clientSecret: args.clientSecret,
46
+ subscriptionKey: args.subscriptionKey,
47
+ };
48
+ const config = providerDef.oauthConfig(credentials);
49
+ // Generate state token
50
+ const state = generateRandomString(32);
51
+ // PKCE if the provider requires it
52
+ let codeVerifier;
53
+ let codeChallenge;
54
+ if (config.usePkce) {
55
+ codeVerifier = generateRandomString(64);
56
+ codeChallenge = await generateCodeChallenge(codeVerifier);
57
+ }
58
+ // Store state in the database for validation during callback
59
+ await ctx.runMutation(internal.oauthStates.store, {
60
+ state,
61
+ userId: args.userId,
62
+ provider: args.provider,
63
+ codeVerifier,
64
+ redirectUri: args.redirectUri,
65
+ });
66
+ // Build the authorization URL
67
+ const url = buildAuthorizationUrl({
68
+ config,
69
+ redirectUri: args.redirectUri,
70
+ state,
71
+ codeChallenge,
72
+ });
73
+ return url;
74
+ },
75
+ });
76
+ // ---------------------------------------------------------------------------
77
+ // Handle OAuth callback (exchange code for tokens)
78
+ // ---------------------------------------------------------------------------
79
+ /**
80
+ * Handle the OAuth callback. Consumes the state token, exchanges the
81
+ * authorization code for access/refresh tokens, fetches the user's
82
+ * provider profile, and creates/updates the connection.
83
+ */
84
+ export const handleCallback = action({
85
+ args: {
86
+ state: v.string(),
87
+ code: v.string(),
88
+ clientId: v.string(),
89
+ clientSecret: v.string(),
90
+ subscriptionKey: v.optional(v.string()),
91
+ },
92
+ returns: v.object({
93
+ provider: v.string(),
94
+ userId: v.string(),
95
+ connectionId: v.string(),
96
+ }),
97
+ handler: async (ctx, args) => {
98
+ // Consume the state token
99
+ const oauthState = await ctx.runMutation(internal.oauthStates.consume, {
100
+ state: args.state,
101
+ });
102
+ if (!oauthState) {
103
+ throw new Error("Invalid or expired OAuth state");
104
+ }
105
+ const providerDef = getProvider(oauthState.provider);
106
+ if (!providerDef) {
107
+ throw new Error(`Provider "${oauthState.provider}" is not implemented`);
108
+ }
109
+ await ctx.runMutation(internal.providerSettings.upsertCredentials, {
110
+ provider: oauthState.provider,
111
+ clientId: args.clientId,
112
+ clientSecret: args.clientSecret,
113
+ subscriptionKey: args.subscriptionKey,
114
+ });
115
+ const credentials = {
116
+ clientId: args.clientId,
117
+ clientSecret: args.clientSecret,
118
+ subscriptionKey: args.subscriptionKey,
119
+ };
120
+ const config = providerDef.oauthConfig(credentials);
121
+ // Exchange code for tokens
122
+ const tokenResponse = await exchangeCodeForTokens(config, args.code, oauthState.redirectUri ?? "", oauthState.codeVerifier);
123
+ // Fetch user info from the provider
124
+ if (providerDef.postConnect) {
125
+ await providerDef.postConnect(tokenResponse.access_token, tokenResponse, oauthState.userId, credentials);
126
+ }
127
+ const userInfo = await providerDef.getUserInfo(tokenResponse.access_token, tokenResponse, oauthState.userId, credentials);
128
+ // Calculate token expiry
129
+ const tokenExpiresAt = tokenResponse.expires_in
130
+ ? Date.now() + tokenResponse.expires_in * 1000
131
+ : undefined;
132
+ // Create or update the connection
133
+ const connectionId = await ctx.runMutation(internal.connections.createConnection, {
134
+ userId: oauthState.userId,
135
+ provider: oauthState.provider,
136
+ providerUserId: userInfo.providerUserId ?? undefined,
137
+ providerUsername: userInfo.username ?? undefined,
138
+ accessToken: tokenResponse.access_token,
139
+ refreshToken: tokenResponse.refresh_token,
140
+ tokenExpiresAt,
141
+ scope: tokenResponse.scope,
142
+ });
143
+ // Create a data source for this connection
144
+ await ctx.runMutation(api.dataSources.getOrCreate, {
145
+ userId: oauthState.userId,
146
+ provider: oauthState.provider,
147
+ connectionId,
148
+ source: oauthState.provider,
149
+ });
150
+ return {
151
+ provider: oauthState.provider,
152
+ userId: oauthState.userId,
153
+ connectionId: String(connectionId),
154
+ };
155
+ },
156
+ });
157
+ /**
158
+ * Ensure a connection has a valid (non-expired) access token.
159
+ * If expired, refreshes the token and updates the connection.
160
+ * Returns the valid access token.
161
+ */
162
+ export const ensureValidToken = internalAction({
163
+ args: {
164
+ connectionId: v.id("connections"),
165
+ provider: providerName,
166
+ accessToken: v.string(),
167
+ refreshToken: v.optional(v.string()),
168
+ tokenExpiresAt: v.optional(v.number()),
169
+ clientId: v.string(),
170
+ clientSecret: v.string(),
171
+ subscriptionKey: v.optional(v.string()),
172
+ },
173
+ returns: v.string(), // valid access token
174
+ handler: async (ctx, args) => {
175
+ // Check if token is still valid (with 5-minute buffer)
176
+ const bufferMs = 5 * 60 * 1000;
177
+ if (args.tokenExpiresAt && args.tokenExpiresAt - bufferMs > Date.now()) {
178
+ return args.accessToken;
179
+ }
180
+ // Token is expired or about to expire — refresh it
181
+ if (!args.refreshToken) {
182
+ throw new Error(`Token expired for ${args.provider} connection and no refresh token available`);
183
+ }
184
+ const providerDef = getProvider(args.provider);
185
+ if (!providerDef) {
186
+ throw new Error(`Provider "${args.provider}" is not implemented`);
187
+ }
188
+ const credentials = {
189
+ clientId: args.clientId,
190
+ clientSecret: args.clientSecret,
191
+ subscriptionKey: args.subscriptionKey,
192
+ };
193
+ const config = providerDef.oauthConfig(credentials);
194
+ const tokenResponse = await refreshAccessToken(config, args.refreshToken);
195
+ const newExpiresAt = tokenResponse.expires_in
196
+ ? Date.now() + tokenResponse.expires_in * 1000
197
+ : undefined;
198
+ // Update the connection with new tokens
199
+ await ctx.runMutation(internal.connections.updateTokens, {
200
+ connectionId: args.connectionId,
201
+ accessToken: tokenResponse.access_token,
202
+ refreshToken: tokenResponse.refresh_token ?? args.refreshToken,
203
+ tokenExpiresAt: newExpiresAt,
204
+ });
205
+ return tokenResponse.access_token;
206
+ },
207
+ });
208
+ //# sourceMappingURL=oauthActions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthActions.js","sourceRoot":"","sources":["../../src/component/oauthActions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Look up an OAuth state by its state token.
3
+ */
4
+ export declare const getByState: import("convex/server").RegisteredQuery<"internal", {
5
+ state: string;
6
+ }, Promise<{
7
+ _id: import("convex/values").GenericId<"oauthStates">;
8
+ _creationTime: number;
9
+ codeVerifier?: string | undefined;
10
+ redirectUri?: string | undefined;
11
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
12
+ userId: string;
13
+ state: string;
14
+ createdAt: number;
15
+ } | null>>;
16
+ /**
17
+ * Store a new OAuth state. Schedules automatic cleanup after 15 minutes.
18
+ */
19
+ export declare const store: import("convex/server").RegisteredMutation<"internal", {
20
+ codeVerifier?: string | undefined;
21
+ redirectUri?: string | undefined;
22
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
23
+ userId: string;
24
+ state: string;
25
+ }, Promise<import("convex/values").GenericId<"oauthStates">>>;
26
+ /**
27
+ * Consume an OAuth state (read and delete). Used during the callback.
28
+ */
29
+ export declare const consume: import("convex/server").RegisteredMutation<"internal", {
30
+ state: string;
31
+ }, Promise<{
32
+ _id: import("convex/values").GenericId<"oauthStates">;
33
+ _creationTime: number;
34
+ codeVerifier?: string | undefined;
35
+ redirectUri?: string | undefined;
36
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
37
+ userId: string;
38
+ state: string;
39
+ createdAt: number;
40
+ } | null>>;
41
+ /**
42
+ * Delete an OAuth state by ID (used by scheduled cleanup).
43
+ */
44
+ export declare const deleteById: import("convex/server").RegisteredMutation<"internal", {
45
+ id: import("convex/values").GenericId<"oauthStates">;
46
+ }, Promise<void>>;
47
+ //# sourceMappingURL=oauthStates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthStates.d.ts","sourceRoot":"","sources":["../../src/component/oauthStates.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;UASrB,CAAC;AAMH;;GAEG;AACH,eAAO,MAAM,KAAK;;;;;;6DAsBhB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;UAclB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,UAAU;;iBAQrB,CAAC"}
@@ -0,0 +1,77 @@
1
+ import { v } from "convex/values";
2
+ import { internal } from "./_generated/api";
3
+ import { internalMutation, internalQuery } from "./_generated/server";
4
+ import { providerName } from "./schema";
5
+ // ---------------------------------------------------------------------------
6
+ // Queries
7
+ // ---------------------------------------------------------------------------
8
+ /**
9
+ * Look up an OAuth state by its state token.
10
+ */
11
+ export const getByState = internalQuery({
12
+ args: { state: v.string() },
13
+ returns: v.any(),
14
+ handler: async (ctx, args) => {
15
+ return await ctx.db
16
+ .query("oauthStates")
17
+ .withIndex("by_state", (idx) => idx.eq("state", args.state))
18
+ .first();
19
+ },
20
+ });
21
+ // ---------------------------------------------------------------------------
22
+ // Mutations
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Store a new OAuth state. Schedules automatic cleanup after 15 minutes.
26
+ */
27
+ export const store = internalMutation({
28
+ args: {
29
+ state: v.string(),
30
+ userId: v.string(),
31
+ provider: providerName,
32
+ codeVerifier: v.optional(v.string()),
33
+ redirectUri: v.optional(v.string()),
34
+ },
35
+ returns: v.id("oauthStates"),
36
+ handler: async (ctx, args) => {
37
+ const id = await ctx.db.insert("oauthStates", {
38
+ ...args,
39
+ createdAt: Date.now(),
40
+ });
41
+ // Schedule cleanup in 15 minutes
42
+ await ctx.scheduler.runAfter(15 * 60 * 1000, internal.oauthStates.deleteById, {
43
+ id,
44
+ });
45
+ return id;
46
+ },
47
+ });
48
+ /**
49
+ * Consume an OAuth state (read and delete). Used during the callback.
50
+ */
51
+ export const consume = internalMutation({
52
+ args: { state: v.string() },
53
+ returns: v.any(),
54
+ handler: async (ctx, args) => {
55
+ const record = await ctx.db
56
+ .query("oauthStates")
57
+ .withIndex("by_state", (idx) => idx.eq("state", args.state))
58
+ .first();
59
+ if (!record)
60
+ return null;
61
+ await ctx.db.delete(record._id);
62
+ return record;
63
+ },
64
+ });
65
+ /**
66
+ * Delete an OAuth state by ID (used by scheduled cleanup).
67
+ */
68
+ export const deleteById = internalMutation({
69
+ args: { id: v.id("oauthStates") },
70
+ handler: async (ctx, args) => {
71
+ const record = await ctx.db.get(args.id);
72
+ if (record) {
73
+ await ctx.db.delete(args.id);
74
+ }
75
+ },
76
+ });
77
+ //# sourceMappingURL=oauthStates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthStates.js","sourceRoot":"","sources":["../../src/component/oauthStates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export declare const upsertCredentials: import("convex/server").RegisteredMutation<"internal", {
2
+ subscriptionKey?: string | undefined;
3
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
4
+ clientId: string;
5
+ clientSecret: string;
6
+ }, Promise<import("convex/values").GenericId<"providerSettings">>>;
7
+ export declare const getCredentials: import("convex/server").RegisteredQuery<"internal", {
8
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
9
+ }, Promise<{
10
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
11
+ clientId: string;
12
+ clientSecret: string;
13
+ subscriptionKey: string | undefined;
14
+ } | null>>;
15
+ //# sourceMappingURL=providerSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providerSettings.d.ts","sourceRoot":"","sources":["../../src/component/providerSettings.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB;;;;;kEA6B5B,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;UA8BzB,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { v } from "convex/values";
2
+ import { internalMutation, internalQuery } from "./_generated/server";
3
+ import { providerName } from "./schema";
4
+ export const upsertCredentials = internalMutation({
5
+ args: {
6
+ provider: providerName,
7
+ clientId: v.string(),
8
+ clientSecret: v.string(),
9
+ subscriptionKey: v.optional(v.string()),
10
+ },
11
+ handler: async (ctx, args) => {
12
+ const existing = await ctx.db
13
+ .query("providerSettings")
14
+ .withIndex("by_provider", (idx) => idx.eq("provider", args.provider))
15
+ .first();
16
+ const patch = {
17
+ provider: args.provider,
18
+ isEnabled: true,
19
+ clientId: args.clientId,
20
+ clientSecret: args.clientSecret,
21
+ subscriptionKey: args.subscriptionKey,
22
+ updatedAt: Date.now(),
23
+ };
24
+ if (existing) {
25
+ await ctx.db.patch(existing._id, patch);
26
+ return existing._id;
27
+ }
28
+ return await ctx.db.insert("providerSettings", patch);
29
+ },
30
+ });
31
+ export const getCredentials = internalQuery({
32
+ args: {
33
+ provider: providerName,
34
+ },
35
+ returns: v.union(v.object({
36
+ provider: providerName,
37
+ clientId: v.string(),
38
+ clientSecret: v.string(),
39
+ subscriptionKey: v.optional(v.string()),
40
+ }), v.null()),
41
+ handler: async (ctx, args) => {
42
+ const settings = await ctx.db
43
+ .query("providerSettings")
44
+ .withIndex("by_provider", (idx) => idx.eq("provider", args.provider))
45
+ .first();
46
+ if (!settings?.clientId || !settings.clientSecret) {
47
+ return null;
48
+ }
49
+ return {
50
+ provider: settings.provider,
51
+ clientId: settings.clientId,
52
+ clientSecret: settings.clientSecret,
53
+ subscriptionKey: settings.subscriptionKey,
54
+ };
55
+ },
56
+ });
57
+ //# sourceMappingURL=providerSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providerSettings.js","sourceRoot":"","sources":["../../src/component/providerSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,MAAM,CAAC,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;IAChD,IAAI,EAAE;QACJ,QAAQ,EAAE,YAAY;QACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,eAAe,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACxC;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE;aAC1B,KAAK,CAAC,kBAAkB,CAAC;aACzB,SAAS,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;aACpE,KAAK,EAAE,CAAC;QAEX,MAAM,KAAK,GAAG;YACZ,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACxC,OAAO,QAAQ,CAAC,GAAG,CAAC;QACtB,CAAC;QAED,OAAO,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAC;IAC1C,IAAI,EAAE;QACJ,QAAQ,EAAE,YAAY;KACvB;IACD,OAAO,EAAE,CAAC,CAAC,KAAK,CACd,CAAC,CAAC,MAAM,CAAC;QACP,QAAQ,EAAE,YAAY;QACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,eAAe,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACxC,CAAC,EACF,CAAC,CAAC,IAAI,EAAE,CACT;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE;aAC1B,KAAK,CAAC,kBAAkB,CAAC;aACzB,SAAS,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;aACpE,KAAK,EAAE,CAAC;QAEX,IAAI,CAAC,QAAQ,EAAE,QAAQ,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO;YACL,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,eAAe,EAAE,QAAQ,CAAC,eAAe;SAC1C,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}