crewly 1.11.5 → 1.11.6

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 (75) hide show
  1. package/dist/backend/backend/src/constants.d.ts +22 -1
  2. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  3. package/dist/backend/backend/src/constants.js +22 -1
  4. package/dist/backend/backend/src/constants.js.map +1 -1
  5. package/dist/backend/backend/src/services/backup/backup-archive.service.d.ts +90 -0
  6. package/dist/backend/backend/src/services/backup/backup-archive.service.d.ts.map +1 -0
  7. package/dist/backend/backend/src/services/backup/backup-archive.service.js +309 -0
  8. package/dist/backend/backend/src/services/backup/backup-archive.service.js.map +1 -0
  9. package/dist/backend/backend/src/services/backup/backup-cloud.client.d.ts +75 -0
  10. package/dist/backend/backend/src/services/backup/backup-cloud.client.d.ts.map +1 -0
  11. package/dist/backend/backend/src/services/backup/backup-cloud.client.js +134 -0
  12. package/dist/backend/backend/src/services/backup/backup-cloud.client.js.map +1 -0
  13. package/dist/backend/backend/src/services/backup/backup-restore.service.d.ts +78 -0
  14. package/dist/backend/backend/src/services/backup/backup-restore.service.d.ts.map +1 -0
  15. package/dist/backend/backend/src/services/backup/backup-restore.service.js +358 -0
  16. package/dist/backend/backend/src/services/backup/backup-restore.service.js.map +1 -0
  17. package/dist/backend/backend/src/services/backup/backup.types.d.ts +163 -0
  18. package/dist/backend/backend/src/services/backup/backup.types.d.ts.map +1 -0
  19. package/dist/backend/backend/src/services/backup/backup.types.js +13 -0
  20. package/dist/backend/backend/src/services/backup/backup.types.js.map +1 -0
  21. package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts +29 -2
  22. package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts.map +1 -1
  23. package/dist/backend/backend/src/services/cloud/cloud-sync.service.js +97 -13
  24. package/dist/backend/backend/src/services/cloud/cloud-sync.service.js.map +1 -1
  25. package/dist/cli/backend/src/constants.d.ts +22 -1
  26. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  27. package/dist/cli/backend/src/constants.js +22 -1
  28. package/dist/cli/backend/src/constants.js.map +1 -1
  29. package/dist/cli/backend/src/controllers/cloud/cloud-google-auth.controller.d.ts +70 -0
  30. package/dist/cli/backend/src/controllers/cloud/cloud-google-auth.controller.d.ts.map +1 -0
  31. package/dist/cli/backend/src/controllers/cloud/cloud-google-auth.controller.js +427 -0
  32. package/dist/cli/backend/src/controllers/cloud/cloud-google-auth.controller.js.map +1 -0
  33. package/dist/cli/backend/src/services/backup/backup-archive.service.d.ts +90 -0
  34. package/dist/cli/backend/src/services/backup/backup-archive.service.d.ts.map +1 -0
  35. package/dist/cli/backend/src/services/backup/backup-archive.service.js +309 -0
  36. package/dist/cli/backend/src/services/backup/backup-archive.service.js.map +1 -0
  37. package/dist/cli/backend/src/services/backup/backup-cloud.client.d.ts +75 -0
  38. package/dist/cli/backend/src/services/backup/backup-cloud.client.d.ts.map +1 -0
  39. package/dist/cli/backend/src/services/backup/backup-cloud.client.js +134 -0
  40. package/dist/cli/backend/src/services/backup/backup-cloud.client.js.map +1 -0
  41. package/dist/cli/backend/src/services/backup/backup-restore.service.d.ts +78 -0
  42. package/dist/cli/backend/src/services/backup/backup-restore.service.d.ts.map +1 -0
  43. package/dist/cli/backend/src/services/backup/backup-restore.service.js +358 -0
  44. package/dist/cli/backend/src/services/backup/backup-restore.service.js.map +1 -0
  45. package/dist/cli/backend/src/services/backup/backup.types.d.ts +163 -0
  46. package/dist/cli/backend/src/services/backup/backup.types.d.ts.map +1 -0
  47. package/dist/cli/backend/src/services/backup/backup.types.js +13 -0
  48. package/dist/cli/backend/src/services/backup/backup.types.js.map +1 -0
  49. package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts +410 -0
  50. package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -0
  51. package/dist/cli/backend/src/services/cloud/cloud-client.service.js +863 -0
  52. package/dist/cli/backend/src/services/cloud/cloud-client.service.js.map +1 -0
  53. package/dist/cli/backend/src/services/cloud/cloud-sync.service.d.ts +292 -0
  54. package/dist/cli/backend/src/services/cloud/cloud-sync.service.d.ts.map +1 -0
  55. package/dist/cli/backend/src/services/cloud/cloud-sync.service.js +1093 -0
  56. package/dist/cli/backend/src/services/cloud/cloud-sync.service.js.map +1 -0
  57. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +328 -0
  58. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -0
  59. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +171 -0
  60. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -0
  61. package/dist/cli/backend/src/services/cloud/device-identity.service.d.ts +89 -0
  62. package/dist/cli/backend/src/services/cloud/device-identity.service.d.ts.map +1 -0
  63. package/dist/cli/backend/src/services/cloud/device-identity.service.js +148 -0
  64. package/dist/cli/backend/src/services/cloud/device-identity.service.js.map +1 -0
  65. package/dist/cli/backend/src/services/user/user-identity.service.d.ts +86 -0
  66. package/dist/cli/backend/src/services/user/user-identity.service.d.ts.map +1 -0
  67. package/dist/cli/backend/src/services/user/user-identity.service.js +190 -0
  68. package/dist/cli/backend/src/services/user/user-identity.service.js.map +1 -0
  69. package/dist/cli/cli/src/commands/backup.d.ts +31 -0
  70. package/dist/cli/cli/src/commands/backup.d.ts.map +1 -0
  71. package/dist/cli/cli/src/commands/backup.js +280 -0
  72. package/dist/cli/cli/src/commands/backup.js.map +1 -0
  73. package/dist/cli/cli/src/index.js +10 -0
  74. package/dist/cli/cli/src/index.js.map +1 -1
  75. package/package.json +1 -1
@@ -0,0 +1,427 @@
1
+ /**
2
+ * Cloud Google OAuth Controller
3
+ *
4
+ * Handles Google OAuth login flow for the CrewlyAI Cloud Console.
5
+ * Provides both browser-redirect (GET) and API (POST) flows.
6
+ *
7
+ * Routes:
8
+ * - GET /api/cloud/google/start -> Redirects to Google consent screen
9
+ * - GET /api/cloud/google/callback -> Handles Google redirect, issues JWT, redirects to frontend
10
+ * - POST /api/cloud/google/url -> Returns Google OAuth URL as JSON (for SPA clients)
11
+ * - POST /api/cloud/google/callback -> Exchanges code for JWT, returns JSON (for SPA clients)
12
+ *
13
+ * @module controllers/cloud/cloud-google-auth.controller
14
+ */
15
+ import crypto from 'crypto';
16
+ import { GOOGLE_OAUTH_CONSTANTS, AUTH_CONSTANTS, CLOUD_AUTH_CONSTANTS } from '../../constants.js';
17
+ import { UserIdentityService } from '../../services/user/user-identity.service.js';
18
+ import { DeviceIdentityService } from '../../services/cloud/device-identity.service.js';
19
+ import { CloudClientService } from '../../services/cloud/cloud-client.service.js';
20
+ import { LoggerService } from '../../services/core/logger.service.js';
21
+ const logger = LoggerService.getInstance().createComponentLogger('CloudGoogleAuth');
22
+ /** Env var for the Cloud Console frontend URL (where to redirect after login). */
23
+ const CLOUD_CONSOLE_FRONTEND_URL = () => process.env['CLOUD_CONSOLE_URL'] || process.env['CLOUD_PORTAL_URL'] || 'https://crewlyai.com';
24
+ /** Env var for the Google OAuth redirect URI (must match GCP console). */
25
+ const CLOUD_GOOGLE_REDIRECT_URI = (req) => process.env['CLOUD_GOOGLE_REDIRECT_URI'] ||
26
+ `${req.protocol}://${req.get('host')}/api/cloud/google/callback`;
27
+ /** Scopes for Cloud Console login -- only need email and profile. */
28
+ const LOGIN_SCOPES = ['openid', 'email', 'profile'];
29
+ /** Default user plan assigned to new users. */
30
+ const DEFAULT_USER_PLAN = AUTH_CONSTANTS.PLANS.FREE;
31
+ /**
32
+ * Build a Google OAuth consent URL with the given post-login redirect.
33
+ *
34
+ * Consolidates URL construction used by both cloudGoogleStart and cloudGoogleUrl.
35
+ *
36
+ * @param req - Express request (used to derive redirect_uri)
37
+ * @param postLoginRedirect - Where to send user after login completes
38
+ * @returns Full Google OAuth consent URL string
39
+ * @throws Error if GOOGLE_CLIENT_ID is not configured
40
+ */
41
+ function buildGoogleOAuthUrl(req, postLoginRedirect) {
42
+ const clientId = CLOUD_AUTH_CONSTANTS.GOOGLE.CLIENT_ID;
43
+ if (!clientId)
44
+ throw new Error('GOOGLE_CLIENT_ID is not configured');
45
+ const redirectUri = CLOUD_GOOGLE_REDIRECT_URI(req);
46
+ const statePayload = {
47
+ redirectTo: postLoginRedirect,
48
+ t: Date.now(),
49
+ nonce: crypto.randomUUID(),
50
+ };
51
+ const state = Buffer.from(JSON.stringify(statePayload)).toString('base64url');
52
+ const url = new URL(GOOGLE_OAUTH_CONSTANTS.AUTH_BASE_URL);
53
+ url.searchParams.set('client_id', clientId);
54
+ url.searchParams.set('redirect_uri', redirectUri);
55
+ url.searchParams.set('response_type', 'code');
56
+ url.searchParams.set('access_type', 'offline');
57
+ url.searchParams.set('prompt', 'consent');
58
+ url.searchParams.set('scope', LOGIN_SCOPES.join(' '));
59
+ url.searchParams.set('state', state);
60
+ return url.toString();
61
+ }
62
+ /**
63
+ * Exchange a Google authorization code for tokens, fetch user profile,
64
+ * and upsert the user in the local identity store.
65
+ *
66
+ * Shared by both GET and POST callback handlers.
67
+ *
68
+ * @param code - Google authorization code
69
+ * @param req - Express request (used to derive redirect_uri)
70
+ * @returns GoogleLoginResult with user, profile, and token data
71
+ * @throws Error on credential misconfiguration, token exchange failure, or missing profile data
72
+ */
73
+ async function exchangeCodeAndCreateUser(code, req) {
74
+ const clientId = CLOUD_AUTH_CONSTANTS.GOOGLE.CLIENT_ID;
75
+ const clientSecret = process.env['GOOGLE_CLIENT_SECRET'] || '';
76
+ if (!clientId || !clientSecret) {
77
+ throw new Error('Google OAuth credentials not configured');
78
+ }
79
+ const redirectUri = CLOUD_GOOGLE_REDIRECT_URI(req);
80
+ // Exchange code for tokens
81
+ const tokenResp = await fetch(GOOGLE_OAUTH_CONSTANTS.TOKEN_ENDPOINT, {
82
+ method: 'POST',
83
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
84
+ body: new URLSearchParams({
85
+ code,
86
+ client_id: clientId,
87
+ client_secret: clientSecret,
88
+ redirect_uri: redirectUri,
89
+ grant_type: 'authorization_code',
90
+ }),
91
+ });
92
+ if (!tokenResp.ok) {
93
+ const details = await tokenResp.text();
94
+ logger.error('Failed to exchange Google OAuth code', { status: tokenResp.status, details });
95
+ throw new Error(`token_exchange_failed: ${tokenResp.status}`);
96
+ }
97
+ const tokenData = (await tokenResp.json());
98
+ // Fetch Google profile
99
+ const profileResp = await fetch(GOOGLE_OAUTH_CONSTANTS.USERINFO_ENDPOINT, {
100
+ headers: { Authorization: `Bearer ${tokenData.access_token}` },
101
+ });
102
+ if (!profileResp.ok) {
103
+ logger.error('Failed to fetch Google profile', { status: profileResp.status });
104
+ throw new Error(`profile_fetch_failed: ${profileResp.status}`);
105
+ }
106
+ const profile = (await profileResp.json());
107
+ if (!profile.email) {
108
+ throw new Error('no_email');
109
+ }
110
+ // Create or find user and store tokens
111
+ const users = UserIdentityService.getInstance();
112
+ const user = await users.createOrUpdateUser({ email: profile.email });
113
+ if (tokenData.refresh_token || tokenData.access_token) {
114
+ await users.connectService(user.id, 'google', {
115
+ refreshToken: tokenData.refresh_token || tokenData.access_token,
116
+ accessToken: tokenData.access_token,
117
+ scopes: LOGIN_SCOPES,
118
+ });
119
+ }
120
+ return {
121
+ user,
122
+ profile: { email: profile.email, name: profile.name, picture: profile.picture },
123
+ tokenData: { access_token: tokenData.access_token, refresh_token: tokenData.refresh_token },
124
+ };
125
+ }
126
+ /**
127
+ * Sign a JWT using HMAC-SHA256.
128
+ *
129
+ * @param payload - JWT payload object
130
+ * @returns Signed JWT string (header.payload.signature)
131
+ */
132
+ export function signJwt(payload) {
133
+ const header = { alg: 'HS256', typ: 'JWT' };
134
+ const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');
135
+ const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
136
+ const signature = crypto
137
+ .createHmac('sha256', AUTH_CONSTANTS.JWT.DEFAULT_SECRET)
138
+ .update(`${headerB64}.${payloadB64}`)
139
+ .digest('base64url');
140
+ return `${headerB64}.${payloadB64}.${signature}`;
141
+ }
142
+ /**
143
+ * Verify a JWT signed with HMAC-SHA256.
144
+ *
145
+ * @param token - JWT string (header.payload.signature)
146
+ * @returns Decoded payload if valid, null if invalid or expired
147
+ */
148
+ export function verifyJwt(token) {
149
+ try {
150
+ const parts = token.split('.');
151
+ if (parts.length !== 3)
152
+ return null;
153
+ const [headerB64, payloadB64, signature] = parts;
154
+ const expectedSig = crypto
155
+ .createHmac('sha256', AUTH_CONSTANTS.JWT.DEFAULT_SECRET)
156
+ .update(`${headerB64}.${payloadB64}`)
157
+ .digest('base64url');
158
+ if (signature !== expectedSig)
159
+ return null;
160
+ const payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString('utf8'));
161
+ // Check expiry
162
+ if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
163
+ return null;
164
+ }
165
+ return payload;
166
+ }
167
+ catch {
168
+ return null;
169
+ }
170
+ }
171
+ // ---------------------------------------------------------------------------
172
+ // Route handlers
173
+ // ---------------------------------------------------------------------------
174
+ /**
175
+ * GET /api/cloud/google/start
176
+ *
177
+ * Redirects the browser to the Google OAuth consent screen.
178
+ *
179
+ * @param req - Express request with optional query: { redirect }
180
+ * @param res - Express response (302 redirect)
181
+ * @param next - Next function for error propagation
182
+ */
183
+ export async function cloudGoogleStart(req, res, next) {
184
+ try {
185
+ const postLoginRedirect = req.query['redirect'] ? String(req.query['redirect']) : '';
186
+ const url = buildGoogleOAuthUrl(req, postLoginRedirect);
187
+ logger.info('Redirecting to Google OAuth consent screen');
188
+ res.redirect(url);
189
+ }
190
+ catch (error) {
191
+ if (error instanceof Error && error.message.includes('GOOGLE_CLIENT_ID')) {
192
+ res.status(500).json({ success: false, error: error.message });
193
+ return;
194
+ }
195
+ logger.error('Failed to initiate Google OAuth', {
196
+ error: error instanceof Error ? error.message : String(error),
197
+ });
198
+ next(error);
199
+ }
200
+ }
201
+ /**
202
+ * GET /api/cloud/google/callback
203
+ *
204
+ * Handles the Google OAuth redirect, exchanges code, issues JWT, and redirects.
205
+ *
206
+ * @param req - Express request with query: { code, state? }
207
+ * @param res - Express response (302 redirect to frontend)
208
+ * @param next - Next function for error propagation
209
+ */
210
+ export async function cloudGoogleCallback(req, res, next) {
211
+ try {
212
+ const code = req.query['code'] ? String(req.query['code']) : '';
213
+ const state = req.query['state'] ? String(req.query['state']) : '';
214
+ const errorParam = req.query['error'] ? String(req.query['error']) : '';
215
+ const consoleUrl = CLOUD_CONSOLE_FRONTEND_URL();
216
+ if (errorParam) {
217
+ logger.warn('Google OAuth returned error', { error: errorParam });
218
+ res.redirect(`${consoleUrl}/login?error=${encodeURIComponent(errorParam)}`);
219
+ return;
220
+ }
221
+ if (!code) {
222
+ logger.warn('Google OAuth callback missing code');
223
+ res.redirect(`${consoleUrl}/login?error=missing_code`);
224
+ return;
225
+ }
226
+ let result;
227
+ try {
228
+ result = await exchangeCodeAndCreateUser(code, req);
229
+ }
230
+ catch (err) {
231
+ const errMsg = err instanceof Error ? err.message : String(err);
232
+ logger.error('Google OAuth exchange failed', { error: errMsg });
233
+ const errorCode = errMsg.split(':')[0] || 'exchange_failed';
234
+ res.redirect(`${consoleUrl}/login?error=${encodeURIComponent(errorCode)}`);
235
+ return;
236
+ }
237
+ // Resolve device ID for JWT (enables Cloud-side auto-registration on poll/send)
238
+ let deviceId;
239
+ try {
240
+ const identity = await DeviceIdentityService.getInstance().getOrCreateIdentity();
241
+ deviceId = identity.deviceId;
242
+ }
243
+ catch {
244
+ logger.debug('Could not resolve deviceId for JWT (non-fatal)');
245
+ }
246
+ // Issue JWT access token
247
+ const now = Math.floor(Date.now() / 1000);
248
+ const accessToken = signJwt({
249
+ sub: result.user.id,
250
+ email: result.profile.email,
251
+ name: result.profile.name || '',
252
+ plan: DEFAULT_USER_PLAN,
253
+ ...(deviceId && { deviceId }),
254
+ iat: now,
255
+ exp: now + AUTH_CONSTANTS.JWT.ACCESS_TOKEN_EXPIRY_S,
256
+ iss: AUTH_CONSTANTS.JWT.ISSUER,
257
+ type: 'access',
258
+ });
259
+ // Issue refresh token (long-lived, enables auto-renewal after access token expires)
260
+ const refreshToken = signJwt({
261
+ sub: result.user.id,
262
+ email: result.profile.email,
263
+ name: result.profile.name || '',
264
+ plan: DEFAULT_USER_PLAN,
265
+ ...(deviceId && { deviceId }),
266
+ iat: now,
267
+ exp: now + AUTH_CONSTANTS.JWT.REFRESH_TOKEN_EXPIRY_S,
268
+ iss: AUTH_CONSTANTS.JWT.ISSUER,
269
+ type: 'refresh',
270
+ });
271
+ // Parse state for post-login redirect
272
+ let postLoginRedirect = '';
273
+ if (state) {
274
+ try {
275
+ const parsed = JSON.parse(Buffer.from(state, 'base64url').toString('utf8'));
276
+ postLoginRedirect = parsed.redirectTo || parsed.redirect || '';
277
+ }
278
+ catch {
279
+ logger.warn('Failed to parse OAuth state parameter');
280
+ }
281
+ }
282
+ const finalRedirect = postLoginRedirect || consoleUrl;
283
+ const separator = finalRedirect.includes('?') ? '&' : '?';
284
+ // Persist tokens to CloudClientService immediately so the refresh token
285
+ // is written to ~/.crewly/cloud/config.json before the frontend calls /connect.
286
+ try {
287
+ const cloudUrl = `${req.protocol}://${req.get('host')}`;
288
+ const client = CloudClientService.getInstance();
289
+ client.connectLocal(cloudUrl, accessToken, DEFAULT_USER_PLAN, refreshToken);
290
+ }
291
+ catch (connectErr) {
292
+ logger.warn('Failed to connect CloudClientService during OAuth callback (non-fatal)', {
293
+ error: connectErr instanceof Error ? connectErr.message : String(connectErr),
294
+ });
295
+ }
296
+ logger.info('Cloud Google OAuth login successful', { email: result.profile.email, userId: result.user.id });
297
+ res.redirect(`${finalRedirect}${separator}token=${accessToken}&refreshToken=${refreshToken}`);
298
+ }
299
+ catch (error) {
300
+ logger.error('Cloud Google OAuth callback error', {
301
+ error: error instanceof Error ? error.message : String(error),
302
+ });
303
+ next(error);
304
+ }
305
+ }
306
+ /**
307
+ * POST /api/cloud/google/url
308
+ *
309
+ * Returns the Google OAuth consent URL as JSON (for SPA clients).
310
+ *
311
+ * @param req - Request with optional body: { redirectTo, redirectUrl }
312
+ * @param res - Response returning { success, data: { url } }
313
+ * @param next - Next function for error propagation
314
+ */
315
+ export async function cloudGoogleUrl(req, res, next) {
316
+ try {
317
+ const postLoginRedirect = req.body?.redirectTo || req.body?.redirectUrl || '';
318
+ const url = buildGoogleOAuthUrl(req, postLoginRedirect);
319
+ logger.info('Returning Google OAuth URL for SPA client');
320
+ res.json({ success: true, data: { url } });
321
+ }
322
+ catch (error) {
323
+ if (error instanceof Error && error.message.includes('GOOGLE_CLIENT_ID')) {
324
+ res.status(500).json({ success: false, error: error.message });
325
+ return;
326
+ }
327
+ logger.error('Failed to generate Google OAuth URL', {
328
+ error: error instanceof Error ? error.message : String(error),
329
+ });
330
+ next(error);
331
+ }
332
+ }
333
+ /**
334
+ * POST /api/cloud/google/callback
335
+ *
336
+ * SPA-friendly code exchange: accepts { code } in body, returns JWT + user as JSON.
337
+ *
338
+ * @param req - Request with body: { code }
339
+ * @param res - Response returning { success, data: { accessToken, refreshToken, expiresIn, user } }
340
+ * @param next - Next function for error propagation
341
+ */
342
+ export async function cloudGoogleCallbackPost(req, res, next) {
343
+ try {
344
+ const { code } = req.body;
345
+ if (!code) {
346
+ res.status(400).json({ success: false, error: 'Missing authorization code' });
347
+ return;
348
+ }
349
+ const result = await exchangeCodeAndCreateUser(code, req);
350
+ // Resolve device ID for JWT (enables Cloud-side auto-registration on poll/send)
351
+ let deviceIdForJwt;
352
+ try {
353
+ const identity = await DeviceIdentityService.getInstance().getOrCreateIdentity();
354
+ deviceIdForJwt = identity.deviceId;
355
+ }
356
+ catch {
357
+ logger.debug('Could not resolve deviceId for JWT (non-fatal)');
358
+ }
359
+ // Issue access token
360
+ const now = Math.floor(Date.now() / 1000);
361
+ const accessToken = signJwt({
362
+ sub: result.user.id,
363
+ email: result.profile.email,
364
+ name: result.profile.name || '',
365
+ plan: DEFAULT_USER_PLAN,
366
+ ...(deviceIdForJwt && { deviceId: deviceIdForJwt }),
367
+ iat: now,
368
+ exp: now + AUTH_CONSTANTS.JWT.ACCESS_TOKEN_EXPIRY_S,
369
+ iss: AUTH_CONSTANTS.JWT.ISSUER,
370
+ type: 'access',
371
+ });
372
+ // Issue refresh token (longer lived, carries user claims for token refresh)
373
+ const refreshToken = signJwt({
374
+ sub: result.user.id,
375
+ email: result.profile.email,
376
+ name: result.profile.name || '',
377
+ plan: DEFAULT_USER_PLAN,
378
+ ...(deviceIdForJwt && { deviceId: deviceIdForJwt }),
379
+ iat: now,
380
+ exp: now + AUTH_CONSTANTS.JWT.REFRESH_TOKEN_EXPIRY_S,
381
+ iss: AUTH_CONSTANTS.JWT.ISSUER,
382
+ type: 'refresh',
383
+ });
384
+ // Persist tokens to CloudClientService immediately so the refresh token
385
+ // is written to ~/.crewly/cloud/config.json before the frontend calls /connect.
386
+ try {
387
+ const cloudUrl = `${req.protocol}://${req.get('host')}`;
388
+ const client = CloudClientService.getInstance();
389
+ client.connectLocal(cloudUrl, accessToken, DEFAULT_USER_PLAN, refreshToken);
390
+ }
391
+ catch (connectErr) {
392
+ logger.warn('Failed to connect CloudClientService during OAuth POST callback (non-fatal)', {
393
+ error: connectErr instanceof Error ? connectErr.message : String(connectErr),
394
+ });
395
+ }
396
+ logger.info('Cloud Google OAuth login successful (POST)', { email: result.profile.email, userId: result.user.id });
397
+ res.json({
398
+ success: true,
399
+ data: {
400
+ accessToken,
401
+ refreshToken,
402
+ expiresIn: AUTH_CONSTANTS.JWT.ACCESS_TOKEN_EXPIRY_S,
403
+ user: {
404
+ id: result.user.id,
405
+ email: result.profile.email,
406
+ displayName: result.profile.name || '',
407
+ plan: DEFAULT_USER_PLAN,
408
+ createdAt: result.user.createdAt || '',
409
+ },
410
+ },
411
+ });
412
+ }
413
+ catch (error) {
414
+ const errMsg = error instanceof Error ? error.message : String(error);
415
+ if (errMsg.includes('credentials not configured')) {
416
+ res.status(500).json({ success: false, error: 'Google OAuth credentials not configured' });
417
+ return;
418
+ }
419
+ if (errMsg.startsWith('token_exchange_failed') || errMsg.startsWith('profile_fetch_failed') || errMsg === 'no_email') {
420
+ res.status(400).json({ success: false, error: errMsg.includes(':') ? errMsg.split(':')[0] : errMsg });
421
+ return;
422
+ }
423
+ logger.error('Cloud Google OAuth callback (POST) error', { error: errMsg });
424
+ next(error);
425
+ }
426
+ }
427
+ //# sourceMappingURL=cloud-google-auth.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-google-auth.controller.js","sourceRoot":"","sources":["../../../../../../backend/src/controllers/cloud/cloud-google-auth.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Backup Archive Service (P0)
3
+ *
4
+ * Builds a portable workspace backup: a single `.tar.gz` containing a
5
+ * top-level `manifest.json`, the captured `CREWLY_HOME` globals (under
6
+ * `home/`), each project's `.crewly/` tree (under `projects/<id>/`), and
7
+ * optionally `chat.db`.
8
+ *
9
+ * Runs locally with no Cloud dependency — `crewly backup create`. The Cloud
10
+ * upload/park step (Pro-gated) is a later phase and consumes the archive this
11
+ * service produces. See specs/2026-06-07-workspace-backup.md.
12
+ *
13
+ * @module services/backup/backup-archive.service
14
+ */
15
+ import { type ComponentLogger } from '../core/logger.service.js';
16
+ import { type CreateBackupOptions, type CreateBackupResult } from './backup.types.js';
17
+ /**
18
+ * Service that builds workspace backup archives.
19
+ */
20
+ export declare class BackupArchiveService {
21
+ private readonly logger;
22
+ constructor(logger?: ComponentLogger);
23
+ /**
24
+ * Build a workspace backup archive.
25
+ *
26
+ * Captures CREWLY_HOME globals (exclude-based, so new data domains are
27
+ * picked up automatically), each project's `.crewly/` tree (walked from
28
+ * projects.json) with git provenance, and chat.db (unless excluded). Stages
29
+ * everything into a temp dir, writes the manifest, and tars it to `outPath`.
30
+ *
31
+ * @param options - Build options (createdAt is required — no clock in lib code)
32
+ * @returns The archive path, manifest, and total captured bytes
33
+ * @throws If CREWLY_HOME does not exist or the tar write fails
34
+ */
35
+ createArchive(options: CreateBackupOptions): Promise<CreateBackupResult>;
36
+ /**
37
+ * Recursively collect capturable global files under CREWLY_HOME (relative
38
+ * paths). Exclude-based so new data domains are captured automatically.
39
+ *
40
+ * @param home - CREWLY_HOME absolute path
41
+ * @returns Relative file paths (POSIX-style) to capture
42
+ */
43
+ private collectGlobalFiles;
44
+ /**
45
+ * Walk projects.json and capture each project's `.crewly/` tree + git
46
+ * provenance into staging/projects/<id>/.
47
+ *
48
+ * @param home - CREWLY_HOME absolute path
49
+ * @param staging - staging dir root
50
+ * @returns Project entries for the manifest
51
+ */
52
+ private collectProjects;
53
+ /**
54
+ * Read `origin` remote + HEAD commit for a project dir (best-effort).
55
+ *
56
+ * @param projectPath - Absolute project path
57
+ * @returns Git provenance (nulls when not a repo / unavailable)
58
+ */
59
+ private readGitProvenance;
60
+ /**
61
+ * Capture chat.db via the SQLite online backup API (consistent snapshot of a
62
+ * possibly-live DB). Degrades gracefully when chat.db is absent or the native
63
+ * module can't load — the backup just omits it with a recorded reason.
64
+ *
65
+ * @param home - CREWLY_HOME absolute path
66
+ * @param staging - staging dir root
67
+ * @returns chat.db manifest record
68
+ */
69
+ private captureChatDb;
70
+ /**
71
+ * Copy a source file into the staging archive layout, returning its manifest
72
+ * entry (archive-relative path + sha256 + size).
73
+ *
74
+ * @param srcRoot - Root the relative path is resolved against
75
+ * @param rel - Source path relative to srcRoot (POSIX-style)
76
+ * @param staging - staging dir root
77
+ * @param archivePath - Destination path inside the archive
78
+ * @returns Manifest file entry
79
+ */
80
+ private stageFile;
81
+ /**
82
+ * Read the running Crewly version (best-effort). Uses npm_package_version —
83
+ * the same source as tracing.service.ts — which works under both the ESM
84
+ * runtime and the CJS test runner (no import.meta / __dirname).
85
+ *
86
+ * @returns Version string or 'unknown'
87
+ */
88
+ private readCrewlyVersion;
89
+ }
90
+ //# sourceMappingURL=backup-archive.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-archive.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/backup/backup-archive.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAYH,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EAKL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAkC3B;;GAEG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAE7B,MAAM,CAAC,EAAE,eAAe;IAIpC;;;;;;;;;;;OAWG;IACG,aAAa,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAmE9E;;;;;;OAMG;YACW,kBAAkB;IAmBhC;;;;;;;OAOG;YACW,eAAe;IAqC7B;;;;;OAKG;YACW,iBAAiB;IAgB/B;;;;;;;;OAQG;YACW,aAAa;IA6B3B;;;;;;;;;OASG;YACW,SAAS;IASvB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;CAG1B"}