@netlify/spark-ui 1.31.0-alpha.2 → 1.31.0-alpha.3
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/components/preact/DropZone/DropZone.d.ts +8 -1
- package/dist/components/preact/DropZone/DropZone.js +42 -40
- package/dist/components/preact/DropZone/drop-core/authedDropClient.d.ts +57 -34
- package/dist/components/preact/DropZone/drop-core/authedDropClient.js +88 -62
- package/dist/components/preact/DropZone/drop-core/buildClient.d.ts +30 -16
- package/dist/components/preact/DropZone/drop-core/buildClient.js +60 -53
- package/dist/components/preact/DropZone/useDropDeploy.d.ts +2 -1
- package/dist/components/preact/DropZone/useDropDeploy.js +60 -59
- package/package.json +1 -1
- package/packages/components/preact/DropZone/DropZone.tsx +9 -0
- package/packages/components/preact/DropZone/drop-core/authedDropClient.ts +86 -40
- package/packages/components/preact/DropZone/drop-core/buildClient.ts +37 -23
- package/packages/components/preact/DropZone/useDropDeploy.ts +8 -2
|
@@ -33,20 +33,27 @@ export interface BuildClient {
|
|
|
33
33
|
|
|
34
34
|
export interface AuthenticatedBuildClientConfig {
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* access-control
|
|
39
|
-
*
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
* Base for JSON API calls, routed through the consumer's access-control
|
|
37
|
+
* rewrite so the session cookie authenticates them. On netlify.com that
|
|
38
|
+
* rewrite is `/access-control/*` → the app's access-control function, which
|
|
39
|
+
* makes `/access-control/bb-api/api/v1` the API root (the default here).
|
|
40
|
+
*/
|
|
41
|
+
proxyBase?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Direct API base for the zip upload. The proxy above runs in a Lambda with a
|
|
44
|
+
* ~6MB request body limit, so the multipart POST has to go straight to
|
|
45
|
+
* `api.netlify.com` (whose `/api/*` CORS policy allows any origin).
|
|
43
46
|
*/
|
|
44
47
|
apiBase?: string;
|
|
45
48
|
/**
|
|
46
|
-
* Mint a
|
|
47
|
-
*
|
|
49
|
+
* Mint a bearer token for that direct upload. On netlify.com this is
|
|
50
|
+
* `GET /access-control/generate-access-control-token`, whose
|
|
48
51
|
* `accessControlToken` the API decrypts back into the user's access token.
|
|
49
52
|
* Resolving `null` means "no session" and surfaces as `NotAuthenticatedError`.
|
|
53
|
+
*
|
|
54
|
+
* Note the token issued to a non-app origin lives only 30 seconds. It is
|
|
55
|
+
* minted immediately before the upload, but a zip that takes longer than that
|
|
56
|
+
* to transfer will still be rejected mid-flight — see `createBuildFromZip`.
|
|
50
57
|
*/
|
|
51
58
|
getUploadToken: () => Promise<string | null>;
|
|
52
59
|
/** Create the site in this account. Omit to use the user's default account. */
|
|
@@ -59,6 +66,7 @@ export interface AuthenticatedBuildClientConfig {
|
|
|
59
66
|
timeoutMs?: number;
|
|
60
67
|
}
|
|
61
68
|
|
|
69
|
+
const PROXY_BASE = '/access-control/bb-api/api/v1';
|
|
62
70
|
const API_BASE = 'https://api.netlify.com/api/v1';
|
|
63
71
|
const POLL_INTERVAL_MS = 1000;
|
|
64
72
|
const TIMEOUT_MS = 10 * 60 * 1000;
|
|
@@ -84,6 +92,7 @@ interface DeployState {
|
|
|
84
92
|
* short-lived bearer token because the proxy can't carry a body that large.
|
|
85
93
|
*/
|
|
86
94
|
export class AuthenticatedBuildClient implements BuildClient {
|
|
95
|
+
private proxyBase: string;
|
|
87
96
|
private apiBase: string;
|
|
88
97
|
private getUploadToken: () => Promise<string | null>;
|
|
89
98
|
private accountSlug?: string;
|
|
@@ -92,6 +101,7 @@ export class AuthenticatedBuildClient implements BuildClient {
|
|
|
92
101
|
private timeoutMs: number;
|
|
93
102
|
|
|
94
103
|
constructor({
|
|
104
|
+
proxyBase = PROXY_BASE,
|
|
95
105
|
apiBase = API_BASE,
|
|
96
106
|
getUploadToken,
|
|
97
107
|
accountSlug,
|
|
@@ -99,6 +109,7 @@ export class AuthenticatedBuildClient implements BuildClient {
|
|
|
99
109
|
pollIntervalMs = POLL_INTERVAL_MS,
|
|
100
110
|
timeoutMs = TIMEOUT_MS,
|
|
101
111
|
}: AuthenticatedBuildClientConfig) {
|
|
112
|
+
this.proxyBase = proxyBase;
|
|
102
113
|
this.apiBase = apiBase;
|
|
103
114
|
this.getUploadToken = getUploadToken;
|
|
104
115
|
this.accountSlug = accountSlug;
|
|
@@ -108,17 +119,16 @@ export class AuthenticatedBuildClient implements BuildClient {
|
|
|
108
119
|
}
|
|
109
120
|
|
|
110
121
|
/**
|
|
111
|
-
* Create the site the build deploys into.
|
|
112
|
-
*
|
|
122
|
+
* Create the site the build deploys into. Goes through the proxy with
|
|
123
|
+
* `credentials: 'include'` so the app session authorizes it; without an
|
|
124
|
+
* account slug the API picks the user's default account.
|
|
113
125
|
*/
|
|
114
126
|
async createSite(): Promise<BuildSite> {
|
|
115
|
-
const token = await this.getUploadToken();
|
|
116
|
-
if (!token) throw new NotAuthenticatedError();
|
|
117
|
-
|
|
118
127
|
const path = this.accountSlug ? `/${this.accountSlug}/sites` : '/sites';
|
|
119
|
-
const res = await fetch(`${this.
|
|
128
|
+
const res = await fetch(`${this.proxyBase}${path}`, {
|
|
120
129
|
method: 'POST',
|
|
121
|
-
|
|
130
|
+
credentials: 'include',
|
|
131
|
+
headers: { 'Content-Type': 'application/json' },
|
|
122
132
|
body: JSON.stringify({ created_via: 'drop' }),
|
|
123
133
|
});
|
|
124
134
|
// A logged-out visitor gets a 401 here rather than at the token step, since
|
|
@@ -133,6 +143,11 @@ export class AuthenticatedBuildClient implements BuildClient {
|
|
|
133
143
|
* (see the note on `apiBase`), authorized by a freshly minted bearer token.
|
|
134
144
|
* `Content-Type` is left unset on purpose so the browser adds the multipart
|
|
135
145
|
* boundary itself.
|
|
146
|
+
*
|
|
147
|
+
* Known ceiling: this is one request on a 30s token, and re-minting can't
|
|
148
|
+
* help once it is in flight. A zip that takes longer than that to upload will
|
|
149
|
+
* fail — the app origin gets 300s for exactly this reason, and lifting the
|
|
150
|
+
* limit for other origins is a platform-side change.
|
|
136
151
|
*/
|
|
137
152
|
async createBuildFromZip(siteId: string, zip: File): Promise<BuildResponse> {
|
|
138
153
|
const token = await this.getUploadToken();
|
|
@@ -159,19 +174,18 @@ export class AuthenticatedBuildClient implements BuildClient {
|
|
|
159
174
|
* deploy id yet has nothing pollable, so it resolves immediately and the
|
|
160
175
|
* caller falls back to the site page.
|
|
161
176
|
*
|
|
162
|
-
* Optional in the drop flow, and off by default: a build takes minutes,
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* a
|
|
177
|
+
* Optional in the drop flow, and off by default: a build takes minutes, so
|
|
178
|
+
* the useful hand-off is usually the project page rather than a spinner.
|
|
179
|
+
* Polling rides the session cookie, so it cannot outlive its credentials the
|
|
180
|
+
* way a 30s bearer would.
|
|
166
181
|
*/
|
|
167
182
|
async waitUntilBuildReady(build: BuildResponse): Promise<void> {
|
|
168
183
|
if (!build.deploy_id) return;
|
|
169
184
|
|
|
170
185
|
const deadline = this.timeoutMs / this.pollIntervalMs;
|
|
171
186
|
for (let i = 0; i < deadline; i++) {
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
187
|
+
const res = await fetch(`${this.proxyBase}/deploys/${build.deploy_id}`, {
|
|
188
|
+
credentials: 'include',
|
|
175
189
|
});
|
|
176
190
|
if (res.ok) {
|
|
177
191
|
const deploy: DeployState = await res.json();
|
|
@@ -89,6 +89,7 @@ interface UseDropDeployOptions {
|
|
|
89
89
|
buildClient?: BuildClient;
|
|
90
90
|
authenticatedDropClient?: DropClient;
|
|
91
91
|
getUploadToken?: () => Promise<string | null>;
|
|
92
|
+
proxyBase?: string;
|
|
92
93
|
accountSlug?: string;
|
|
93
94
|
buildDeployUrl: (build: DeployedBuild) => string;
|
|
94
95
|
waitForBuild?: boolean;
|
|
@@ -141,6 +142,7 @@ export function useDropDeploy({
|
|
|
141
142
|
buildClient,
|
|
142
143
|
authenticatedDropClient,
|
|
143
144
|
getUploadToken,
|
|
145
|
+
proxyBase,
|
|
144
146
|
accountSlug,
|
|
145
147
|
buildDeployUrl,
|
|
146
148
|
waitForBuild,
|
|
@@ -160,7 +162,9 @@ export function useDropDeploy({
|
|
|
160
162
|
// fresh client (and a fresh token cache) to an in-flight deploy.
|
|
161
163
|
const authedBuildClient = useRef(
|
|
162
164
|
buildClient ??
|
|
163
|
-
(getUploadToken
|
|
165
|
+
(getUploadToken
|
|
166
|
+
? new AuthenticatedBuildClient({ proxyBase, getUploadToken, accountSlug })
|
|
167
|
+
: null)
|
|
164
168
|
).current;
|
|
165
169
|
|
|
166
170
|
// Same opt-in for static drops: with authenticated wiring present, deploy
|
|
@@ -168,7 +172,9 @@ export function useDropDeploy({
|
|
|
168
172
|
// every static drop stays anonymous.
|
|
169
173
|
const authedDropClient = useRef(
|
|
170
174
|
authenticatedDropClient ??
|
|
171
|
-
(getUploadToken
|
|
175
|
+
(getUploadToken
|
|
176
|
+
? new AuthenticatedDropClient({ proxyBase, getUploadToken, accountSlug })
|
|
177
|
+
: null)
|
|
172
178
|
).current;
|
|
173
179
|
|
|
174
180
|
const busy = status.kind !== 'idle' && status.kind !== 'error';
|