@payloadcms/figma 0.0.1-alpha.61 → 0.0.1-alpha.63
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/dist/api/control-plane.d.ts +4 -0
- package/dist/api/control-plane.js +11 -4
- package/dist/auth/crypto-utils.d.ts +11 -0
- package/dist/auth/crypto-utils.js +51 -0
- package/dist/auth/oauth-flow.js +3 -3
- package/dist/auth/project-token.d.ts +17 -6
- package/dist/auth/project-token.js +37 -19
- package/dist/auth/token-store-migration.d.ts +58 -0
- package/dist/auth/token-store-migration.js +156 -0
- package/dist/auth/token-store.d.ts +51 -128
- package/dist/auth/token-store.js +326 -187
- package/dist/auth/types.d.ts +8 -1
- package/dist/cli.js +9 -1
- package/dist/commands/debug.js +16 -12
- package/dist/commands/deploy.js +2 -0
- package/dist/commands/dump-tokens.d.ts +12 -0
- package/dist/commands/dump-tokens.js +69 -0
- package/dist/commands/init.js +8 -4
- package/dist/commands/list-tokens.js +2 -2
- package/dist/commands/login.js +1 -1
- package/dist/commands/logout.js +23 -4
- package/dist/config/oauth.d.ts +10 -2
- package/dist/config/oauth.js +18 -4
- package/dist/constants.d.ts +4 -2
- package/dist/constants.js +19 -1
- package/dist/db-content-api/index.js +21 -0
- package/dist/db-content-api/utilities/auth.js +4 -1
- package/dist/db-content-api/utilities/data/validateRelationships.d.ts +7 -0
- package/dist/db-content-api/utilities/data/validateRelationships.js +102 -0
- package/dist/oauth/defaults.d.ts +3 -1
- package/dist/oauth/defaults.js +6 -5
- package/dist/oauth/endpoints/getLoginEndpoint.js +1 -1
- package/dist/oauth/index.js +1 -0
- package/dist/oauth/types.d.ts +10 -0
- package/dist/plugin/auth-preflight.d.ts +8 -0
- package/dist/plugin/auth-preflight.js +20 -0
- package/dist/plugin/bootstrap-preflight.d.ts +23 -0
- package/dist/plugin/bootstrap-preflight.js +45 -0
- package/dist/plugin/build-config.js +73 -12
- package/dist/plugin/dev-cookie-names.d.ts +14 -0
- package/dist/plugin/dev-cookie-names.js +19 -0
- package/dist/storage-content-api/client.js +4 -1
- package/dist/storage-content-api/staticHandler.js +1 -18
- package/dist/utils/build-lambda-zip.d.ts +4 -2
- package/dist/utils/build-lambda-zip.js +4 -7
- package/dist/utils/messages.js +1 -1
- package/dist/utils/token-display.d.ts +5 -1
- package/dist/utils/token-display.js +47 -26
- package/dist/utils/version-check.js +1 -1
- package/package.json +2 -1
package/dist/oauth/defaults.js
CHANGED
|
@@ -2,10 +2,11 @@ import { parseCookies } from 'payload';
|
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
3
|
import { hasUserTokenPropsChanged } from './utilities/hasUserTokenPropsChanged.js';
|
|
4
4
|
import { isDuplicateKeyError } from './utilities/isDuplicateKeyError.js';
|
|
5
|
-
|
|
5
|
+
export const DEFAULT_USER_INFO_COOKIE_NAME = 'figma-user-info';
|
|
6
|
+
function getFigmaUserInfo(headers, cookieName) {
|
|
6
7
|
try {
|
|
7
8
|
const cookies = parseCookies(headers);
|
|
8
|
-
const encoded = cookies.get(
|
|
9
|
+
const encoded = cookies.get(cookieName);
|
|
9
10
|
if (!encoded) {
|
|
10
11
|
return null;
|
|
11
12
|
}
|
|
@@ -28,7 +29,7 @@ export const defaultUsernameField = {
|
|
|
28
29
|
},
|
|
29
30
|
unique: true
|
|
30
31
|
};
|
|
31
|
-
export const defaultVerify = ({ collection, strategyName, usernameField })=>async ({ headers, payload, token })=>{
|
|
32
|
+
export const defaultVerify = ({ collection, strategyName, userInfoCookieName = DEFAULT_USER_INFO_COOKIE_NAME, usernameField })=>async ({ headers, payload, token })=>{
|
|
32
33
|
let tokenUsername = 'preferred_username';
|
|
33
34
|
if (usernameField.name !== 'preferredUsername') {
|
|
34
35
|
tokenUsername = usernameField.name;
|
|
@@ -38,12 +39,12 @@ export const defaultVerify = ({ collection, strategyName, usernameField })=>asyn
|
|
|
38
39
|
user: null
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
|
-
const figmaUserInfo = getFigmaUserInfo(headers);
|
|
42
|
+
const figmaUserInfo = getFigmaUserInfo(headers, userInfoCookieName);
|
|
42
43
|
let responseHeaders;
|
|
43
44
|
// Clear the cookie after reading
|
|
44
45
|
if (figmaUserInfo) {
|
|
45
46
|
responseHeaders = new Headers();
|
|
46
|
-
responseHeaders.append('Set-Cookie',
|
|
47
|
+
responseHeaders.append('Set-Cookie', `${userInfoCookieName}=; Max-Age=0; Path=/;`);
|
|
47
48
|
}
|
|
48
49
|
let user = null;
|
|
49
50
|
const depth = typeof collection.auth === 'object' ? collection.auth.depth : undefined;
|
|
@@ -262,7 +262,7 @@ export const getLoginEndpoint = ({ collection, collectionOptions, endpointSlug,
|
|
|
262
262
|
})).toString('base64');
|
|
263
263
|
const userInfoCookie = generateCookie({
|
|
264
264
|
...cookieOptions,
|
|
265
|
-
name: 'figma-user-info',
|
|
265
|
+
name: pluginOptions?.userInfoCookieName ?? 'figma-user-info',
|
|
266
266
|
expires: new Date(Date.now() + 60_000),
|
|
267
267
|
returnCookieAsObject: false,
|
|
268
268
|
value: userInfoValue
|
package/dist/oauth/index.js
CHANGED
|
@@ -144,6 +144,7 @@ export const oAuth2Plugin = (pluginOptions)=>(config)=>{
|
|
|
144
144
|
verify: collectionOptions.verify ?? defaultVerify({
|
|
145
145
|
collection: existingCollection,
|
|
146
146
|
strategyName,
|
|
147
|
+
userInfoCookieName: pluginOptions?.userInfoCookieName,
|
|
147
148
|
usernameField
|
|
148
149
|
})
|
|
149
150
|
});
|
package/dist/oauth/types.d.ts
CHANGED
|
@@ -148,6 +148,16 @@ export interface PluginOptions {
|
|
|
148
148
|
* @default 'oauth'
|
|
149
149
|
*/
|
|
150
150
|
strategyName?: string;
|
|
151
|
+
/**
|
|
152
|
+
* The name of the short-lived cookie used to carry Figma user profile
|
|
153
|
+
* info from the login endpoint to `defaultVerify`.
|
|
154
|
+
*
|
|
155
|
+
* Override in local dev when running multiple Payload apps on localhost
|
|
156
|
+
* to avoid cookie collisions (all localhost apps share a cookie jar).
|
|
157
|
+
*
|
|
158
|
+
* @default 'figma-user-info'
|
|
159
|
+
*/
|
|
160
|
+
userInfoCookieName?: string;
|
|
151
161
|
}
|
|
152
162
|
export type CookieOptions = {
|
|
153
163
|
domain?: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TokenStore } from '../auth/token-store.js';
|
|
2
|
+
/**
|
|
3
|
+
* Print a one-shot banner when the plugin is about to use `tokenStore`
|
|
4
|
+
* auth but no valid OAuth credential is present. Keeps the dev loop
|
|
5
|
+
* actionable instead of relying on per-request middleware errors.
|
|
6
|
+
*/
|
|
7
|
+
export declare function logMissingCliAuth(tokenStore: TokenStore): void;
|
|
8
|
+
//# sourceMappingURL=auth-preflight.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
/**
|
|
4
|
+
* Print a one-shot banner when the plugin is about to use `tokenStore`
|
|
5
|
+
* auth but no valid OAuth credential is present. Keeps the dev loop
|
|
6
|
+
* actionable instead of relying on per-request middleware errors.
|
|
7
|
+
*/ export function logMissingCliAuth(tokenStore) {
|
|
8
|
+
if (tokenStore.hasValidOauthToken()) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const lines = [
|
|
12
|
+
'Not logged in to the Figma CLI.',
|
|
13
|
+
'Content API requests will fail until you authenticate.',
|
|
14
|
+
'',
|
|
15
|
+
`Run ${pc.cyan('npx @payloadcms/figma login')} to log in, then restart the dev server.`
|
|
16
|
+
];
|
|
17
|
+
p.note(lines.join('\n'), pc.yellow('Figma authentication required'));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=auth-preflight.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reason the Content System ID could not be resolved. Drives the
|
|
3
|
+
* banner's copy so the dev sees the specific remediation step.
|
|
4
|
+
*/
|
|
5
|
+
export type MissingContentSystemIdReason = {
|
|
6
|
+
environmentName: string;
|
|
7
|
+
kind: 'environment-not-found';
|
|
8
|
+
projectId: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'network';
|
|
11
|
+
message: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'no-config';
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'unauthenticated';
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Print a banner explaining why Content System ID resolution failed and
|
|
19
|
+
* what to run next. Called just before `buildFigmaConfig` throws, so the
|
|
20
|
+
* actionable step is visible above the stack trace.
|
|
21
|
+
*/
|
|
22
|
+
export declare function logMissingContentSystemId(reason: MissingContentSystemIdReason): void;
|
|
23
|
+
//# sourceMappingURL=bootstrap-preflight.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
/**
|
|
4
|
+
* Print a banner explaining why Content System ID resolution failed and
|
|
5
|
+
* what to run next. Called just before `buildFigmaConfig` throws, so the
|
|
6
|
+
* actionable step is visible above the stack trace.
|
|
7
|
+
*/ export function logMissingContentSystemId(reason) {
|
|
8
|
+
const lines = buildBody(reason);
|
|
9
|
+
p.note(lines.join('\n'), pc.yellow('Figma project not configured'));
|
|
10
|
+
}
|
|
11
|
+
function buildBody(reason) {
|
|
12
|
+
const loginCmd = pc.cyan('npx @payloadcms/figma login');
|
|
13
|
+
const initCmd = pc.cyan('npx @payloadcms/figma init');
|
|
14
|
+
const initForceCmd = pc.cyan('npx @payloadcms/figma init --force');
|
|
15
|
+
if (reason.kind === 'no-config') {
|
|
16
|
+
return [
|
|
17
|
+
'No Figma project configuration found.',
|
|
18
|
+
'',
|
|
19
|
+
`Run ${initCmd} to set up this project.`
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
if (reason.kind === 'unauthenticated') {
|
|
23
|
+
return [
|
|
24
|
+
'Project config found, but you are not logged in.',
|
|
25
|
+
'Cannot fetch bootstrap data without a valid session.',
|
|
26
|
+
'',
|
|
27
|
+
`Run ${loginCmd}, then restart the dev server.`
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
if (reason.kind === 'network') {
|
|
31
|
+
return [
|
|
32
|
+
'Could not reach the Figma API to fetch bootstrap data.',
|
|
33
|
+
`Error: ${reason.message}`,
|
|
34
|
+
'',
|
|
35
|
+
'Check your network, then restart the dev server.'
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
return [
|
|
39
|
+
`Environment ${pc.bold(reason.environmentName)} not found for project ${pc.bold(reason.projectId)}.`,
|
|
40
|
+
'',
|
|
41
|
+
`Run ${initForceCmd} to reconfigure, or set FIGMA_ENVIRONMENT_NAME to a valid environment.`
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=bootstrap-preflight.js.map
|
|
@@ -15,7 +15,11 @@ import { createStorageClient } from '../storage-content-api/client.js';
|
|
|
15
15
|
import { getGenerateSignedURLHandler } from '../storage-content-api/client-uploads/generateSignedURL.js';
|
|
16
16
|
import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
17
17
|
import { cacheAllEnvironments } from '../utils/cache-bootstrap.js';
|
|
18
|
+
import { getEnvVarSync } from '../utils/env-management.js';
|
|
18
19
|
import * as log from '../utils/log.js';
|
|
20
|
+
import { logMissingCliAuth } from './auth-preflight.js';
|
|
21
|
+
import { logMissingContentSystemId } from './bootstrap-preflight.js';
|
|
22
|
+
import { getDevCookieNames } from './dev-cookie-names.js';
|
|
19
23
|
/**
|
|
20
24
|
* Fields added to users collection for Figma profile info
|
|
21
25
|
*/ const figmaUserFields = [
|
|
@@ -47,20 +51,58 @@ function missingOAuthCredential(name) {
|
|
|
47
51
|
}
|
|
48
52
|
/**
|
|
49
53
|
* Fetch bootstrap data from the API and cache all environments locally.
|
|
50
|
-
* Returns the requested environment's data, or
|
|
54
|
+
* Returns the requested environment's data, or a typed failure reason.
|
|
51
55
|
*/ async function resolveAndCacheBootstrap(store, projectId, environmentName) {
|
|
56
|
+
let credential;
|
|
57
|
+
try {
|
|
58
|
+
credential = await getValidCredential(store);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
reason: {
|
|
63
|
+
kind: 'network',
|
|
64
|
+
message: messageOf(error)
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (!credential) {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
reason: {
|
|
72
|
+
kind: 'unauthenticated'
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
52
76
|
try {
|
|
53
|
-
const credential = await getValidCredential(store);
|
|
54
|
-
if (!credential) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
77
|
const bootstrapInfo = await getBootstrapInfo(credential, projectId);
|
|
58
78
|
cacheAllEnvironments(store, projectId, bootstrapInfo);
|
|
59
|
-
return store.getBootstrapData(projectId, environmentName);
|
|
60
79
|
} catch (error) {
|
|
61
|
-
|
|
62
|
-
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
reason: {
|
|
83
|
+
kind: 'network',
|
|
84
|
+
message: messageOf(error)
|
|
85
|
+
}
|
|
86
|
+
};
|
|
63
87
|
}
|
|
88
|
+
const data = store.getBootstrapData(projectId, environmentName);
|
|
89
|
+
if (!data) {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
reason: {
|
|
93
|
+
environmentName,
|
|
94
|
+
kind: 'environment-not-found',
|
|
95
|
+
projectId
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
data,
|
|
101
|
+
ok: true
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function messageOf(error) {
|
|
105
|
+
return error instanceof Error ? error.message : 'Unknown error';
|
|
64
106
|
}
|
|
65
107
|
export async function buildFigmaConfig(config) {
|
|
66
108
|
const envConfig = getEnvConfig();
|
|
@@ -68,16 +110,27 @@ export async function buildFigmaConfig(config) {
|
|
|
68
110
|
let { contentSystemId } = config.figma;
|
|
69
111
|
contentSystemId ??= process.env.FIGMA_CONTENT_API_CONTENT_SYSTEM_ID;
|
|
70
112
|
let bootstrapData = null;
|
|
113
|
+
let bootstrapReason = null;
|
|
71
114
|
// Local dev: resolve from project ID + bootstrap cache
|
|
115
|
+
// Use getEnvVarSync fallback because Next.js evaluates config before dotenv populates process.env
|
|
72
116
|
if (!contentSystemId) {
|
|
73
|
-
const projectId = process.env.FIGMA_PROJECT_ID;
|
|
74
|
-
const environmentName = process.env.FIGMA_ENVIRONMENT_NAME;
|
|
75
|
-
if (projectId
|
|
117
|
+
const projectId = process.env.FIGMA_PROJECT_ID ?? getEnvVarSync(process.cwd(), 'FIGMA_PROJECT_ID');
|
|
118
|
+
const environmentName = process.env.FIGMA_ENVIRONMENT_NAME ?? getEnvVarSync(process.cwd(), 'FIGMA_ENVIRONMENT_NAME');
|
|
119
|
+
if (!projectId || !environmentName) {
|
|
120
|
+
bootstrapReason = {
|
|
121
|
+
kind: 'no-config'
|
|
122
|
+
};
|
|
123
|
+
} else {
|
|
76
124
|
const store = getTokenStore();
|
|
77
125
|
bootstrapData = store.getBootstrapData(projectId, environmentName);
|
|
78
126
|
// Runtime fallback: fetch from API if not cached (e.g. environment switch without re-init)
|
|
79
127
|
if (!bootstrapData) {
|
|
80
|
-
|
|
128
|
+
const resolution = await resolveAndCacheBootstrap(store, projectId, environmentName);
|
|
129
|
+
if (resolution.ok) {
|
|
130
|
+
bootstrapData = resolution.data;
|
|
131
|
+
} else {
|
|
132
|
+
bootstrapReason = resolution.reason;
|
|
133
|
+
}
|
|
81
134
|
}
|
|
82
135
|
if (bootstrapData) {
|
|
83
136
|
contentSystemId = bootstrapData.contentSystemId;
|
|
@@ -85,10 +138,17 @@ export async function buildFigmaConfig(config) {
|
|
|
85
138
|
}
|
|
86
139
|
}
|
|
87
140
|
if (!contentSystemId) {
|
|
141
|
+
logMissingContentSystemId(bootstrapReason ?? {
|
|
142
|
+
kind: 'no-config'
|
|
143
|
+
});
|
|
88
144
|
throw new Error('Content System ID could not be resolved. ' + 'Run `npx @payloadcms/figma init` to set up your project.');
|
|
89
145
|
}
|
|
90
146
|
const url = process.env.FIGMA_CONTENT_API_URL || envConfig.contentApiUrl;
|
|
91
147
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
148
|
+
const usesTokenStoreAuth = !process.env.FIGMA_CONTENT_API_ACCESS_KEY && process.env.FIGMA_DEV_JWT !== 'true';
|
|
149
|
+
if (usesTokenStoreAuth) {
|
|
150
|
+
logMissingCliAuth(getTokenStore());
|
|
151
|
+
}
|
|
92
152
|
// Determine database adapter based on environment
|
|
93
153
|
let db;
|
|
94
154
|
if (config.figma.useContentSystem === false) {
|
|
@@ -219,6 +279,7 @@ export async function buildFigmaConfig(config) {
|
|
|
219
279
|
usernameField: 'email'
|
|
220
280
|
}
|
|
221
281
|
],
|
|
282
|
+
...isProduction ? {} : getDevCookieNames(contentSystemId),
|
|
222
283
|
debug: !!process.env.DEBUG,
|
|
223
284
|
disabled: false
|
|
224
285
|
}),
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespace OAuth cookies per-app in local dev so multiple Payload apps
|
|
3
|
+
* running on localhost (shared cookie jar across ports) don't clobber each
|
|
4
|
+
* other's sessions on logout. Uses projectId so the cookie stays stable when
|
|
5
|
+
* switching environments in the same checkout; falls back to contentSystemId
|
|
6
|
+
* when projectId isn't in `.env`.
|
|
7
|
+
*
|
|
8
|
+
* In production, cookies are isolated by unique hostnames and this is unused.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getDevCookieNames(contentSystemId: string): {
|
|
11
|
+
cookieName: string;
|
|
12
|
+
userInfoCookieName: string;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=dev-cookie-names.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getEnvVarSync } from '../utils/env-management.js';
|
|
2
|
+
/**
|
|
3
|
+
* Namespace OAuth cookies per-app in local dev so multiple Payload apps
|
|
4
|
+
* running on localhost (shared cookie jar across ports) don't clobber each
|
|
5
|
+
* other's sessions on logout. Uses projectId so the cookie stays stable when
|
|
6
|
+
* switching environments in the same checkout; falls back to contentSystemId
|
|
7
|
+
* when projectId isn't in `.env`.
|
|
8
|
+
*
|
|
9
|
+
* In production, cookies are isolated by unique hostnames and this is unused.
|
|
10
|
+
*/ export function getDevCookieNames(contentSystemId) {
|
|
11
|
+
const projectId = process.env.FIGMA_PROJECT_ID ?? getEnvVarSync(process.cwd(), 'FIGMA_PROJECT_ID') ?? contentSystemId;
|
|
12
|
+
const suffix = projectId.slice(0, 8);
|
|
13
|
+
return {
|
|
14
|
+
cookieName: `figma-${suffix}-oauth-token`,
|
|
15
|
+
userInfoCookieName: `figma-${suffix}-user-info`
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=dev-cookie-names.js.map
|
|
@@ -18,7 +18,10 @@ function createStorageAuthMiddleware(config) {
|
|
|
18
18
|
if (auth.mode === 'apiKey') {
|
|
19
19
|
request.headers.set('X-Api-Key', auth.apiKey);
|
|
20
20
|
} else if (auth.mode === 'tokenStore') {
|
|
21
|
-
const token = await getValidProjectToken(
|
|
21
|
+
const token = await getValidProjectToken({
|
|
22
|
+
tenantId: contentSystemId,
|
|
23
|
+
tokenStore: auth.tokenStore
|
|
24
|
+
});
|
|
22
25
|
if (!token) {
|
|
23
26
|
throw new Error('Authentication required. Run `npx @payloadcms/figma login` to authenticate.');
|
|
24
27
|
}
|
|
@@ -23,24 +23,7 @@ export const getHandler = ({ client, collection })=>{
|
|
|
23
23
|
status: 500
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
const fileResponse = await fetch(data.url);
|
|
28
|
-
if (!fileResponse.ok) {
|
|
29
|
-
req.payload.logger.error(`S3 fetch failed: ${fileResponse.status} ${fileResponse.statusText}`);
|
|
30
|
-
return new Response('Failed to fetch file from storage', {
|
|
31
|
-
status: 502
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
const contentType = fileResponse.headers.get('content-type') || 'application/octet-stream';
|
|
35
|
-
const contentLength = fileResponse.headers.get('content-length');
|
|
36
|
-
return new Response(fileResponse.body, {
|
|
37
|
-
headers: {
|
|
38
|
-
'Content-Type': contentType,
|
|
39
|
-
...contentLength && {
|
|
40
|
-
'Content-Length': contentLength
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
});
|
|
26
|
+
return Response.redirect(data.url, 302);
|
|
44
27
|
} catch (err) {
|
|
45
28
|
req.payload.logger.error({
|
|
46
29
|
err,
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
* Cross-platform replacement for build_for_lambda.sh:
|
|
5
5
|
* 1. Copies .next/static to .next/standalone/.next/static
|
|
6
6
|
* 2. Copies run.sh to .next/standalone/run.sh
|
|
7
|
-
* 3.
|
|
8
|
-
*
|
|
7
|
+
* 3. Creates lambda.zip from .next/standalone/
|
|
8
|
+
*
|
|
9
|
+
* Note: public/ is NOT included in the zip — those files are uploaded
|
|
10
|
+
* as static assets and served via CDN, not from the Lambda function.
|
|
9
11
|
*
|
|
10
12
|
* @param projectPath - Path to project root
|
|
11
13
|
* @throws Error if standalone directory missing or zip creation fails
|
|
@@ -8,15 +8,16 @@ import path from 'path';
|
|
|
8
8
|
* Cross-platform replacement for build_for_lambda.sh:
|
|
9
9
|
* 1. Copies .next/static to .next/standalone/.next/static
|
|
10
10
|
* 2. Copies run.sh to .next/standalone/run.sh
|
|
11
|
-
* 3.
|
|
12
|
-
*
|
|
11
|
+
* 3. Creates lambda.zip from .next/standalone/
|
|
12
|
+
*
|
|
13
|
+
* Note: public/ is NOT included in the zip — those files are uploaded
|
|
14
|
+
* as static assets and served via CDN, not from the Lambda function.
|
|
13
15
|
*
|
|
14
16
|
* @param projectPath - Path to project root
|
|
15
17
|
* @throws Error if standalone directory missing or zip creation fails
|
|
16
18
|
*/ export async function buildLambdaZip(projectPath) {
|
|
17
19
|
const standalonePath = path.join(projectPath, '.next', 'standalone');
|
|
18
20
|
const staticPath = path.join(projectPath, '.next', 'static');
|
|
19
|
-
const publicPath = path.join(projectPath, 'public');
|
|
20
21
|
const runShPath = path.join(projectPath, 'run.sh');
|
|
21
22
|
const zipPath = path.join(projectPath, 'lambda.zip');
|
|
22
23
|
if (!await isDirectory(standalonePath)) {
|
|
@@ -31,10 +32,6 @@ import path from 'path';
|
|
|
31
32
|
} catch {
|
|
32
33
|
// run.sh may not exist in all setups
|
|
33
34
|
}
|
|
34
|
-
if (await isDirectory(publicPath)) {
|
|
35
|
-
const destPublic = path.join(standalonePath, 'public');
|
|
36
|
-
await copyDirectory(publicPath, destPublic);
|
|
37
|
-
}
|
|
38
35
|
await createZip(standalonePath, zipPath);
|
|
39
36
|
}
|
|
40
37
|
async function isDirectory(dirPath) {
|
package/dist/utils/messages.js
CHANGED
|
@@ -59,7 +59,7 @@ export function helpMessage() {
|
|
|
59
59
|
|
|
60
60
|
${pc.bold('GLOBAL OPTIONS')}
|
|
61
61
|
|
|
62
|
-
${pc.dim('--infra-env <env>')} Target infrastructure (production or
|
|
62
|
+
${pc.dim('--infra-env <env>')} Target infrastructure (production, staging, or devbox)
|
|
63
63
|
|
|
64
64
|
${pc.bold('DOCUMENTATION')}
|
|
65
65
|
|
|
@@ -8,7 +8,11 @@ export declare function maskToken(token: string): string;
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function formatTimestamp(timestamp: number): string;
|
|
10
10
|
/**
|
|
11
|
-
* Show
|
|
11
|
+
* Show the project token for the current project + environment scope.
|
|
12
|
+
*
|
|
13
|
+
* Resolves `projectId` and `environmentName` from env vars
|
|
14
|
+
* (`FIGMA_PROJECT_ID`, `FIGMA_ENVIRONMENT_NAME`) — the *user's* project
|
|
15
|
+
* environment, which is independent of the infra env (production/staging).
|
|
12
16
|
*/
|
|
13
17
|
export declare function showProjectTokens(tokenStore: TokenStore): void;
|
|
14
18
|
//# sourceMappingURL=token-display.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
|
+
import { getEnvVarSync } from './env-management.js';
|
|
3
4
|
import { isDebug } from './is-debug.js';
|
|
4
5
|
/**
|
|
5
6
|
* Mask token for display (show first 4 and last 4 characters)
|
|
@@ -32,38 +33,58 @@ import { isDebug } from './is-debug.js';
|
|
|
32
33
|
return pc.red(`${minutes}m`);
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
|
-
* Show
|
|
36
|
+
* Show the project token for the current project + environment scope.
|
|
37
|
+
*
|
|
38
|
+
* Resolves `projectId` and `environmentName` from env vars
|
|
39
|
+
* (`FIGMA_PROJECT_ID`, `FIGMA_ENVIRONMENT_NAME`) — the *user's* project
|
|
40
|
+
* environment, which is independent of the infra env (production/staging).
|
|
36
41
|
*/ export function showProjectTokens(tokenStore) {
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
const cwd = process.cwd();
|
|
43
|
+
const projectId = getEnvVarSync(cwd, 'FIGMA_PROJECT_ID');
|
|
44
|
+
const environmentName = getEnvVarSync(cwd, 'FIGMA_ENVIRONMENT_NAME');
|
|
45
|
+
if (!projectId || !environmentName) {
|
|
46
|
+
p.note(pc.dim('Set FIGMA_PROJECT_ID and FIGMA_ENVIRONMENT_NAME in your .env to view project tokens.'), 'Project Tokens');
|
|
41
47
|
return;
|
|
42
48
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
const header = [
|
|
50
|
+
`Project ID: ${projectId}`,
|
|
51
|
+
`Environment: ${environmentName}`
|
|
52
|
+
];
|
|
53
|
+
const bootstrapData = tokenStore.getBootstrapData(projectId, environmentName);
|
|
54
|
+
if (!bootstrapData) {
|
|
55
|
+
p.note([
|
|
56
|
+
...header,
|
|
57
|
+
pc.dim('No bootstrap data. Run `@payloadcms/figma init` to set up.')
|
|
58
|
+
].join('\n'), 'Project Token');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const projectInfo = {
|
|
62
|
+
environmentName,
|
|
63
|
+
projectId
|
|
64
|
+
};
|
|
65
|
+
const projectToken = tokenStore.getProjectToken({
|
|
66
|
+
projectInfo
|
|
60
67
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
if (!projectToken) {
|
|
69
|
+
p.note([
|
|
70
|
+
...header,
|
|
71
|
+
`Tenant ID: ${bootstrapData.contentSystemId}`,
|
|
72
|
+
pc.dim('No stored token.')
|
|
73
|
+
].join('\n'), 'Project Token');
|
|
64
74
|
return;
|
|
65
75
|
}
|
|
66
|
-
|
|
76
|
+
const isValid = tokenStore.hasValidProjectToken({
|
|
77
|
+
projectInfo
|
|
78
|
+
});
|
|
79
|
+
const status = isValid ? pc.green('✓ Valid') : pc.red('✗ Expired');
|
|
80
|
+
const projectTokenLines = [
|
|
81
|
+
...header,
|
|
82
|
+
`Tenant ID: ${bootstrapData.contentSystemId}`,
|
|
83
|
+
`Status: ${status}`,
|
|
84
|
+
`Token: ${isDebug() ? projectToken.token : maskToken(projectToken.token)}`,
|
|
85
|
+
`Expires: ${formatTimestamp(projectToken.expiresAt)}`
|
|
86
|
+
];
|
|
87
|
+
p.note(projectTokenLines.join('\n'), 'Project Token');
|
|
67
88
|
}
|
|
68
89
|
|
|
69
90
|
//# sourceMappingURL=token-display.js.map
|
|
@@ -21,7 +21,7 @@ export async function checkForUpdates(currentVersion) {
|
|
|
21
21
|
if (!latestVersion || latestVersion === currentVersion) {
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
p.log.warn(pc.yellow(`@payloadcms/figma ${currentVersion}
|
|
24
|
+
p.log.warn(pc.yellow(`@payloadcms/figma ${currentVersion} differs from latest. Latest: ${latestVersion}`));
|
|
25
25
|
p.log.message(pc.dim(` Run: npx @payloadcms/figma@${latestVersion} init --id <your-id>`));
|
|
26
26
|
} catch {
|
|
27
27
|
// Silent catch - network errors, timeouts, parse errors should not interrupt CLI
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/figma",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.63",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"arg": "^5.0.2",
|
|
35
35
|
"conf": "^13.1.0",
|
|
36
36
|
"cross-spawn": "7.0.6",
|
|
37
|
+
"env-paths": "^3.0.0",
|
|
37
38
|
"figures": "^6.1.0",
|
|
38
39
|
"jose": "6.0.12",
|
|
39
40
|
"jsonwebtoken": "9.0.3",
|