@authrim/setup 0.1.140 → 0.1.142
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/__tests__/keys.test.js +73 -2
- package/dist/__tests__/keys.test.js.map +1 -1
- package/dist/__tests__/migrate.test.js +4 -4
- package/dist/__tests__/migrate.test.js.map +1 -1
- package/dist/__tests__/paths.test.js +163 -1
- package/dist/__tests__/paths.test.js.map +1 -1
- package/dist/__tests__/source-context.test.d.ts +2 -0
- package/dist/__tests__/source-context.test.d.ts.map +1 -0
- package/dist/__tests__/source-context.test.js +72 -0
- package/dist/__tests__/source-context.test.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/commands/deploy.js +65 -37
- package/dist/cli/commands/deploy.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +277 -198
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/core/admin.d.ts +6 -1
- package/dist/core/admin.d.ts.map +1 -1
- package/dist/core/admin.js +45 -20
- package/dist/core/admin.js.map +1 -1
- package/dist/core/cloudflare.d.ts +38 -1
- package/dist/core/cloudflare.d.ts.map +1 -1
- package/dist/core/cloudflare.js +729 -115
- package/dist/core/cloudflare.js.map +1 -1
- package/dist/core/config.d.ts +164 -34
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +72 -18
- package/dist/core/config.js.map +1 -1
- package/dist/core/deploy.d.ts +18 -0
- package/dist/core/deploy.d.ts.map +1 -1
- package/dist/core/deploy.js +126 -25
- package/dist/core/deploy.js.map +1 -1
- package/dist/core/keys.d.ts +20 -4
- package/dist/core/keys.d.ts.map +1 -1
- package/dist/core/keys.js +77 -17
- package/dist/core/keys.js.map +1 -1
- package/dist/core/login-ui-client.d.ts +42 -0
- package/dist/core/login-ui-client.d.ts.map +1 -0
- package/dist/core/login-ui-client.js +173 -0
- package/dist/core/login-ui-client.js.map +1 -0
- package/dist/core/migrate.d.ts +37 -0
- package/dist/core/migrate.d.ts.map +1 -1
- package/dist/core/migrate.js +92 -2
- package/dist/core/migrate.js.map +1 -1
- package/dist/core/paths.d.ts +78 -13
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +135 -17
- package/dist/core/paths.js.map +1 -1
- package/dist/core/source-context.d.ts +22 -0
- package/dist/core/source-context.d.ts.map +1 -0
- package/dist/core/source-context.js +46 -0
- package/dist/core/source-context.js.map +1 -0
- package/dist/core/tenant-mode.d.ts +4 -0
- package/dist/core/tenant-mode.d.ts.map +1 -0
- package/dist/core/tenant-mode.js +17 -0
- package/dist/core/tenant-mode.js.map +1 -0
- package/dist/core/ui-deployment.d.ts +21 -0
- package/dist/core/ui-deployment.d.ts.map +1 -0
- package/dist/core/ui-deployment.js +90 -0
- package/dist/core/ui-deployment.js.map +1 -0
- package/dist/core/ui-env.d.ts +28 -0
- package/dist/core/ui-env.d.ts.map +1 -1
- package/dist/core/ui-env.js +16 -0
- package/dist/core/ui-env.js.map +1 -1
- package/dist/core/url-config.d.ts +16 -0
- package/dist/core/url-config.d.ts.map +1 -0
- package/dist/core/url-config.js +46 -0
- package/dist/core/url-config.js.map +1 -0
- package/dist/core/wrangler.d.ts +50 -1
- package/dist/core/wrangler.d.ts.map +1 -1
- package/dist/core/wrangler.js +171 -57
- package/dist/core/wrangler.js.map +1 -1
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/de.js +38 -1
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +38 -1
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/es.js +38 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts.map +1 -1
- package/dist/i18n/locales/fr.js +38 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/id.d.ts.map +1 -1
- package/dist/i18n/locales/id.js +38 -1
- package/dist/i18n/locales/id.js.map +1 -1
- package/dist/i18n/locales/ja.d.ts.map +1 -1
- package/dist/i18n/locales/ja.js +38 -1
- package/dist/i18n/locales/ja.js.map +1 -1
- package/dist/i18n/locales/ko.d.ts.map +1 -1
- package/dist/i18n/locales/ko.js +38 -1
- package/dist/i18n/locales/ko.js.map +1 -1
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/pt.js +38 -1
- package/dist/i18n/locales/pt.js.map +1 -1
- package/dist/i18n/locales/ru.d.ts.map +1 -1
- package/dist/i18n/locales/ru.js +38 -1
- package/dist/i18n/locales/ru.js.map +1 -1
- package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
- package/dist/i18n/locales/zh-CN.js +38 -1
- package/dist/i18n/locales/zh-CN.js.map +1 -1
- package/dist/i18n/locales/zh-TW.d.ts.map +1 -1
- package/dist/i18n/locales/zh-TW.js +38 -1
- package/dist/i18n/locales/zh-TW.js.map +1 -1
- package/dist/i18n/types.d.ts +8 -0
- package/dist/i18n/types.d.ts.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -30
- package/dist/index.js.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +243 -116
- package/dist/web/api.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +513 -115
- package/dist/web/ui.js.map +1 -1
- package/migrations/000_fresh_schema.sql +229 -10
- package/migrations/admin/007_admin_role_inheritance.sql +32 -0
- package/migrations/admin/008_admin_rebac_definitions.sql +117 -0
- package/migrations/admin/009_optimize_admin_audit_indexes.sql +15 -0
- package/package.json +5 -5
package/dist/core/cloudflare.js
CHANGED
|
@@ -109,15 +109,11 @@ export async function getAccountId() {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
|
-
* Get
|
|
113
|
-
*
|
|
112
|
+
* Get Cloudflare API token from wrangler config or environment variable.
|
|
113
|
+
* Searches platform-specific paths for OAuth token, falls back to CLOUDFLARE_API_TOKEN env var.
|
|
114
114
|
*/
|
|
115
|
-
export async function
|
|
115
|
+
export async function getCloudflareApiToken() {
|
|
116
116
|
try {
|
|
117
|
-
const accountId = await getAccountId();
|
|
118
|
-
if (!accountId)
|
|
119
|
-
return null;
|
|
120
|
-
// Read OAuth token from wrangler config
|
|
121
117
|
const { readFile } = await import('node:fs/promises');
|
|
122
118
|
const { homedir, platform } = await import('node:os');
|
|
123
119
|
const { join } = await import('node:path');
|
|
@@ -126,23 +122,16 @@ export async function getWorkersSubdomain() {
|
|
|
126
122
|
const home = homedir();
|
|
127
123
|
const configPaths = [];
|
|
128
124
|
if (platform() === 'darwin') {
|
|
129
|
-
// macOS: ~/Library/Preferences/.wrangler/config/default.toml (XDG-compliant)
|
|
130
125
|
configPaths.push(join(home, 'Library/Preferences/.wrangler/config/default.toml'));
|
|
131
|
-
// macOS legacy fallback
|
|
132
126
|
configPaths.push(join(home, '.wrangler/config/default.toml'));
|
|
133
127
|
}
|
|
134
128
|
else if (platform() === 'win32') {
|
|
135
|
-
// Windows: Multiple possible locations
|
|
136
129
|
const appData = process.env.APPDATA;
|
|
137
130
|
if (appData) {
|
|
138
|
-
// 1. XDG-compliant: %APPDATA%\xdg.config\.wrangler\config\default.toml (CORRECT PATH)
|
|
139
131
|
configPaths.push(join(appData, 'xdg.config/.wrangler/config/default.toml'));
|
|
140
|
-
// Also try without xdg.config prefix
|
|
141
132
|
configPaths.push(join(appData, '.wrangler/config/default.toml'));
|
|
142
133
|
}
|
|
143
|
-
// 2. Legacy: %USERPROFILE%\.wrangler\config\default.toml
|
|
144
134
|
configPaths.push(join(home, '.wrangler/config/default.toml'));
|
|
145
|
-
// 3. %LOCALAPPDATA%
|
|
146
135
|
const localAppData = process.env.LOCALAPPDATA;
|
|
147
136
|
if (localAppData) {
|
|
148
137
|
configPaths.push(join(localAppData, 'xdg.config/.wrangler/config/default.toml'));
|
|
@@ -150,13 +139,10 @@ export async function getWorkersSubdomain() {
|
|
|
150
139
|
}
|
|
151
140
|
}
|
|
152
141
|
else {
|
|
153
|
-
// Linux: XDG-compliant path
|
|
154
142
|
const xdgConfigHome = process.env.XDG_CONFIG_HOME || join(home, '.config');
|
|
155
143
|
configPaths.push(join(xdgConfigHome, '.wrangler/config/default.toml'));
|
|
156
|
-
// Linux legacy fallback
|
|
157
144
|
configPaths.push(join(home, '.wrangler/config/default.toml'));
|
|
158
145
|
}
|
|
159
|
-
let oauthToken = null;
|
|
160
146
|
for (const configPath of configPaths) {
|
|
161
147
|
if (!existsSync(configPath))
|
|
162
148
|
continue;
|
|
@@ -164,28 +150,39 @@ export async function getWorkersSubdomain() {
|
|
|
164
150
|
const configContent = await readFile(configPath, 'utf-8');
|
|
165
151
|
const tokenMatch = configContent.match(/oauth_token\s*=\s*"([^"]+)"/);
|
|
166
152
|
if (tokenMatch?.[1]) {
|
|
167
|
-
|
|
168
|
-
break;
|
|
153
|
+
return { token: tokenMatch[1], source: 'oauth' };
|
|
169
154
|
}
|
|
170
155
|
}
|
|
171
156
|
catch {
|
|
172
157
|
// Continue to next path
|
|
173
158
|
}
|
|
174
159
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
oauthToken = apiToken;
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
160
|
+
// Fallback to CLOUDFLARE_API_TOKEN environment variable
|
|
161
|
+
const apiToken = process.env.CLOUDFLARE_API_TOKEN;
|
|
162
|
+
if (apiToken) {
|
|
163
|
+
return { token: apiToken, source: 'env' };
|
|
184
164
|
}
|
|
185
|
-
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get the workers.dev subdomain for the account
|
|
173
|
+
* This is needed because workers.dev URLs are: {worker}.{subdomain}.workers.dev
|
|
174
|
+
*/
|
|
175
|
+
export async function getWorkersSubdomain() {
|
|
176
|
+
try {
|
|
177
|
+
const accountId = await getAccountId();
|
|
178
|
+
if (!accountId)
|
|
179
|
+
return null;
|
|
180
|
+
const tokenInfo = await getCloudflareApiToken();
|
|
181
|
+
if (!tokenInfo)
|
|
182
|
+
return null;
|
|
186
183
|
const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/subdomain`, {
|
|
187
184
|
headers: {
|
|
188
|
-
Authorization: `Bearer ${
|
|
185
|
+
Authorization: `Bearer ${tokenInfo.token}`,
|
|
189
186
|
},
|
|
190
187
|
});
|
|
191
188
|
if (!response.ok)
|
|
@@ -197,6 +194,488 @@ export async function getWorkersSubdomain() {
|
|
|
197
194
|
return null;
|
|
198
195
|
}
|
|
199
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Extract zone name (registrable domain) from a hostname.
|
|
199
|
+
* e.g., "auth.example.com" → "example.com"
|
|
200
|
+
* "example.co.jp" → "example.co.jp"
|
|
201
|
+
*/
|
|
202
|
+
export function extractZoneName(hostname) {
|
|
203
|
+
const parts = hostname.split('.');
|
|
204
|
+
// Comprehensive two-part TLD list based on the Public Suffix List (PSL).
|
|
205
|
+
// Only includes patterns commonly used for web hosting on Cloudflare.
|
|
206
|
+
// Sorted alphabetically by country code.
|
|
207
|
+
const twoPartTlds = new Set([
|
|
208
|
+
// ae - United Arab Emirates
|
|
209
|
+
'ac.ae',
|
|
210
|
+
'co.ae',
|
|
211
|
+
'net.ae',
|
|
212
|
+
'org.ae',
|
|
213
|
+
// ar - Argentina
|
|
214
|
+
'com.ar',
|
|
215
|
+
'net.ar',
|
|
216
|
+
'org.ar',
|
|
217
|
+
// at - Austria
|
|
218
|
+
'co.at',
|
|
219
|
+
'or.at',
|
|
220
|
+
// au - Australia
|
|
221
|
+
'com.au',
|
|
222
|
+
'net.au',
|
|
223
|
+
'org.au',
|
|
224
|
+
// bd - Bangladesh
|
|
225
|
+
'com.bd',
|
|
226
|
+
'net.bd',
|
|
227
|
+
'org.bd',
|
|
228
|
+
// bh - Bahrain
|
|
229
|
+
'com.bh',
|
|
230
|
+
'net.bh',
|
|
231
|
+
'org.bh',
|
|
232
|
+
// bn - Brunei
|
|
233
|
+
'com.bn',
|
|
234
|
+
'net.bn',
|
|
235
|
+
'org.bn',
|
|
236
|
+
// bo - Bolivia
|
|
237
|
+
'com.bo',
|
|
238
|
+
'net.bo',
|
|
239
|
+
'org.bo',
|
|
240
|
+
// br - Brazil
|
|
241
|
+
'com.br',
|
|
242
|
+
'net.br',
|
|
243
|
+
'org.br',
|
|
244
|
+
// bw - Botswana
|
|
245
|
+
'co.bw',
|
|
246
|
+
'org.bw',
|
|
247
|
+
// bz - Belize
|
|
248
|
+
'co.bz',
|
|
249
|
+
'com.bz',
|
|
250
|
+
'net.bz',
|
|
251
|
+
'org.bz',
|
|
252
|
+
// cn - China
|
|
253
|
+
'com.cn',
|
|
254
|
+
'net.cn',
|
|
255
|
+
'org.cn',
|
|
256
|
+
// co - Colombia
|
|
257
|
+
'com.co',
|
|
258
|
+
'net.co',
|
|
259
|
+
'org.co',
|
|
260
|
+
// cr - Costa Rica
|
|
261
|
+
'co.cr',
|
|
262
|
+
'or.cr',
|
|
263
|
+
// cu - Cuba
|
|
264
|
+
'com.cu',
|
|
265
|
+
'net.cu',
|
|
266
|
+
'org.cu',
|
|
267
|
+
// cy - Cyprus
|
|
268
|
+
'com.cy',
|
|
269
|
+
'net.cy',
|
|
270
|
+
'org.cy',
|
|
271
|
+
// do - Dominican Republic
|
|
272
|
+
'com.do',
|
|
273
|
+
'net.do',
|
|
274
|
+
'org.do',
|
|
275
|
+
// dz - Algeria
|
|
276
|
+
'com.dz',
|
|
277
|
+
'net.dz',
|
|
278
|
+
'org.dz',
|
|
279
|
+
// ec - Ecuador
|
|
280
|
+
'com.ec',
|
|
281
|
+
'net.ec',
|
|
282
|
+
'org.ec',
|
|
283
|
+
// eg - Egypt
|
|
284
|
+
'com.eg',
|
|
285
|
+
'net.eg',
|
|
286
|
+
'org.eg',
|
|
287
|
+
// et - Ethiopia
|
|
288
|
+
'com.et',
|
|
289
|
+
'net.et',
|
|
290
|
+
'org.et',
|
|
291
|
+
// fj - Fiji
|
|
292
|
+
'com.fj',
|
|
293
|
+
'net.fj',
|
|
294
|
+
'org.fj',
|
|
295
|
+
// ge - Georgia
|
|
296
|
+
'com.ge',
|
|
297
|
+
'net.ge',
|
|
298
|
+
'org.ge',
|
|
299
|
+
// gh - Ghana
|
|
300
|
+
'com.gh',
|
|
301
|
+
'net.gh',
|
|
302
|
+
'org.gh',
|
|
303
|
+
// gr - Greece
|
|
304
|
+
'com.gr',
|
|
305
|
+
'net.gr',
|
|
306
|
+
'org.gr',
|
|
307
|
+
// gt - Guatemala
|
|
308
|
+
'com.gt',
|
|
309
|
+
'net.gt',
|
|
310
|
+
'org.gt',
|
|
311
|
+
// gy - Guyana
|
|
312
|
+
'co.gy',
|
|
313
|
+
'com.gy',
|
|
314
|
+
'net.gy',
|
|
315
|
+
'org.gy',
|
|
316
|
+
// hk - Hong Kong
|
|
317
|
+
'com.hk',
|
|
318
|
+
'net.hk',
|
|
319
|
+
'org.hk',
|
|
320
|
+
// hn - Honduras
|
|
321
|
+
'com.hn',
|
|
322
|
+
'net.hn',
|
|
323
|
+
'org.hn',
|
|
324
|
+
// hr - Croatia
|
|
325
|
+
'com.hr',
|
|
326
|
+
// id - Indonesia
|
|
327
|
+
'co.id',
|
|
328
|
+
'or.id',
|
|
329
|
+
'web.id',
|
|
330
|
+
'net.id',
|
|
331
|
+
// il - Israel
|
|
332
|
+
'co.il',
|
|
333
|
+
'net.il',
|
|
334
|
+
'org.il',
|
|
335
|
+
// im - Isle of Man
|
|
336
|
+
'co.im',
|
|
337
|
+
'com.im',
|
|
338
|
+
'net.im',
|
|
339
|
+
'org.im',
|
|
340
|
+
// in - India
|
|
341
|
+
'co.in',
|
|
342
|
+
'net.in',
|
|
343
|
+
'org.in',
|
|
344
|
+
// io - British Indian Ocean Territory
|
|
345
|
+
'com.io',
|
|
346
|
+
'net.io',
|
|
347
|
+
'org.io',
|
|
348
|
+
// iq - Iraq
|
|
349
|
+
'com.iq',
|
|
350
|
+
'net.iq',
|
|
351
|
+
'org.iq',
|
|
352
|
+
// ir - Iran
|
|
353
|
+
'co.ir',
|
|
354
|
+
'net.ir',
|
|
355
|
+
'org.ir',
|
|
356
|
+
// je - Jersey
|
|
357
|
+
'co.je',
|
|
358
|
+
'net.je',
|
|
359
|
+
'org.je',
|
|
360
|
+
// jo - Jordan
|
|
361
|
+
'com.jo',
|
|
362
|
+
'net.jo',
|
|
363
|
+
'org.jo',
|
|
364
|
+
// jp - Japan
|
|
365
|
+
'co.jp',
|
|
366
|
+
'ne.jp',
|
|
367
|
+
'or.jp',
|
|
368
|
+
'ac.jp',
|
|
369
|
+
'go.jp',
|
|
370
|
+
'gr.jp',
|
|
371
|
+
'ed.jp',
|
|
372
|
+
'ad.jp',
|
|
373
|
+
'lg.jp',
|
|
374
|
+
// ke - Kenya
|
|
375
|
+
'co.ke',
|
|
376
|
+
'or.ke',
|
|
377
|
+
'ne.ke',
|
|
378
|
+
// kh - Cambodia (uses .com.kh etc.)
|
|
379
|
+
'com.kh',
|
|
380
|
+
'net.kh',
|
|
381
|
+
'org.kh',
|
|
382
|
+
// kr - South Korea
|
|
383
|
+
'co.kr',
|
|
384
|
+
'or.kr',
|
|
385
|
+
'ne.kr',
|
|
386
|
+
// kw - Kuwait
|
|
387
|
+
'com.kw',
|
|
388
|
+
'net.kw',
|
|
389
|
+
'org.kw',
|
|
390
|
+
// kz - Kazakhstan
|
|
391
|
+
'com.kz',
|
|
392
|
+
'net.kz',
|
|
393
|
+
'org.kz',
|
|
394
|
+
// lb - Lebanon
|
|
395
|
+
'com.lb',
|
|
396
|
+
'net.lb',
|
|
397
|
+
'org.lb',
|
|
398
|
+
// lc - Saint Lucia
|
|
399
|
+
'co.lc',
|
|
400
|
+
'com.lc',
|
|
401
|
+
'net.lc',
|
|
402
|
+
'org.lc',
|
|
403
|
+
// lk - Sri Lanka
|
|
404
|
+
'com.lk',
|
|
405
|
+
'net.lk',
|
|
406
|
+
'org.lk',
|
|
407
|
+
// ly - Libya
|
|
408
|
+
'com.ly',
|
|
409
|
+
'net.ly',
|
|
410
|
+
'org.ly',
|
|
411
|
+
// ma - Morocco
|
|
412
|
+
'co.ma',
|
|
413
|
+
'net.ma',
|
|
414
|
+
'org.ma',
|
|
415
|
+
// mm - Myanmar
|
|
416
|
+
'com.mm',
|
|
417
|
+
'net.mm',
|
|
418
|
+
'org.mm',
|
|
419
|
+
// mo - Macau
|
|
420
|
+
'com.mo',
|
|
421
|
+
'net.mo',
|
|
422
|
+
'org.mo',
|
|
423
|
+
// mt - Malta
|
|
424
|
+
'com.mt',
|
|
425
|
+
'net.mt',
|
|
426
|
+
'org.mt',
|
|
427
|
+
// mu - Mauritius
|
|
428
|
+
'co.mu',
|
|
429
|
+
'com.mu',
|
|
430
|
+
'net.mu',
|
|
431
|
+
'org.mu',
|
|
432
|
+
// mv - Maldives
|
|
433
|
+
'com.mv',
|
|
434
|
+
'net.mv',
|
|
435
|
+
'org.mv',
|
|
436
|
+
// mx - Mexico
|
|
437
|
+
'com.mx',
|
|
438
|
+
'net.mx',
|
|
439
|
+
'org.mx',
|
|
440
|
+
// my - Malaysia
|
|
441
|
+
'com.my',
|
|
442
|
+
'net.my',
|
|
443
|
+
'org.my',
|
|
444
|
+
// mz - Mozambique
|
|
445
|
+
'co.mz',
|
|
446
|
+
'net.mz',
|
|
447
|
+
'org.mz',
|
|
448
|
+
// na - Namibia
|
|
449
|
+
'co.na',
|
|
450
|
+
'com.na',
|
|
451
|
+
'net.na',
|
|
452
|
+
'org.na',
|
|
453
|
+
// ng - Nigeria
|
|
454
|
+
'com.ng',
|
|
455
|
+
'net.ng',
|
|
456
|
+
'org.ng',
|
|
457
|
+
// ni - Nicaragua
|
|
458
|
+
'com.ni',
|
|
459
|
+
'net.ni',
|
|
460
|
+
'org.ni',
|
|
461
|
+
// np - Nepal
|
|
462
|
+
'com.np',
|
|
463
|
+
'net.np',
|
|
464
|
+
'org.np',
|
|
465
|
+
// nz - New Zealand
|
|
466
|
+
'co.nz',
|
|
467
|
+
'net.nz',
|
|
468
|
+
'org.nz',
|
|
469
|
+
// om - Oman
|
|
470
|
+
'com.om',
|
|
471
|
+
'net.om',
|
|
472
|
+
'org.om',
|
|
473
|
+
// pa - Panama
|
|
474
|
+
'com.pa',
|
|
475
|
+
'net.pa',
|
|
476
|
+
'org.pa',
|
|
477
|
+
// pe - Peru
|
|
478
|
+
'com.pe',
|
|
479
|
+
'net.pe',
|
|
480
|
+
'org.pe',
|
|
481
|
+
// pg - Papua New Guinea
|
|
482
|
+
'com.pg',
|
|
483
|
+
'net.pg',
|
|
484
|
+
'org.pg',
|
|
485
|
+
// ph - Philippines
|
|
486
|
+
'com.ph',
|
|
487
|
+
'net.ph',
|
|
488
|
+
'org.ph',
|
|
489
|
+
// pk - Pakistan
|
|
490
|
+
'com.pk',
|
|
491
|
+
'net.pk',
|
|
492
|
+
'org.pk',
|
|
493
|
+
// pr - Puerto Rico
|
|
494
|
+
'com.pr',
|
|
495
|
+
'net.pr',
|
|
496
|
+
'org.pr',
|
|
497
|
+
// ps - Palestine
|
|
498
|
+
'com.ps',
|
|
499
|
+
'net.ps',
|
|
500
|
+
'org.ps',
|
|
501
|
+
// pt - Portugal
|
|
502
|
+
'com.pt',
|
|
503
|
+
'net.pt',
|
|
504
|
+
'org.pt',
|
|
505
|
+
// py - Paraguay
|
|
506
|
+
'com.py',
|
|
507
|
+
'net.py',
|
|
508
|
+
'org.py',
|
|
509
|
+
// qa - Qatar
|
|
510
|
+
'com.qa',
|
|
511
|
+
'net.qa',
|
|
512
|
+
'org.qa',
|
|
513
|
+
// ro - Romania
|
|
514
|
+
'com.ro',
|
|
515
|
+
'net.ro',
|
|
516
|
+
'org.ro',
|
|
517
|
+
// rs - Serbia
|
|
518
|
+
'co.rs',
|
|
519
|
+
'org.rs',
|
|
520
|
+
// ru - Russia (ru uses direct TLD, but also has some patterns)
|
|
521
|
+
'com.ru',
|
|
522
|
+
'net.ru',
|
|
523
|
+
'org.ru',
|
|
524
|
+
// rw - Rwanda
|
|
525
|
+
'co.rw',
|
|
526
|
+
'net.rw',
|
|
527
|
+
'org.rw',
|
|
528
|
+
// sa - Saudi Arabia
|
|
529
|
+
'com.sa',
|
|
530
|
+
'net.sa',
|
|
531
|
+
'org.sa',
|
|
532
|
+
// sb - Solomon Islands
|
|
533
|
+
'com.sb',
|
|
534
|
+
'net.sb',
|
|
535
|
+
'org.sb',
|
|
536
|
+
// sc - Seychelles
|
|
537
|
+
'com.sc',
|
|
538
|
+
'net.sc',
|
|
539
|
+
'org.sc',
|
|
540
|
+
// sd - Sudan
|
|
541
|
+
'com.sd',
|
|
542
|
+
'net.sd',
|
|
543
|
+
'org.sd',
|
|
544
|
+
// sg - Singapore
|
|
545
|
+
'com.sg',
|
|
546
|
+
'net.sg',
|
|
547
|
+
'org.sg',
|
|
548
|
+
// sl - Sierra Leone
|
|
549
|
+
'com.sl',
|
|
550
|
+
'net.sl',
|
|
551
|
+
'org.sl',
|
|
552
|
+
// sv - El Salvador
|
|
553
|
+
'com.sv',
|
|
554
|
+
'org.sv',
|
|
555
|
+
// sy - Syria
|
|
556
|
+
'com.sy',
|
|
557
|
+
'net.sy',
|
|
558
|
+
'org.sy',
|
|
559
|
+
// th - Thailand
|
|
560
|
+
'co.th',
|
|
561
|
+
'in.th',
|
|
562
|
+
'ac.th',
|
|
563
|
+
'or.th',
|
|
564
|
+
'net.th',
|
|
565
|
+
// tn - Tunisia
|
|
566
|
+
'com.tn',
|
|
567
|
+
'net.tn',
|
|
568
|
+
'org.tn',
|
|
569
|
+
// tr - Turkey
|
|
570
|
+
'com.tr',
|
|
571
|
+
'net.tr',
|
|
572
|
+
'org.tr',
|
|
573
|
+
// tt - Trinidad and Tobago
|
|
574
|
+
'co.tt',
|
|
575
|
+
'com.tt',
|
|
576
|
+
'net.tt',
|
|
577
|
+
'org.tt',
|
|
578
|
+
// tw - Taiwan
|
|
579
|
+
'com.tw',
|
|
580
|
+
'net.tw',
|
|
581
|
+
'org.tw',
|
|
582
|
+
// tz - Tanzania
|
|
583
|
+
'co.tz',
|
|
584
|
+
'or.tz',
|
|
585
|
+
'ne.tz',
|
|
586
|
+
// ua - Ukraine
|
|
587
|
+
'com.ua',
|
|
588
|
+
'net.ua',
|
|
589
|
+
'org.ua',
|
|
590
|
+
// ug - Uganda
|
|
591
|
+
'co.ug',
|
|
592
|
+
'or.ug',
|
|
593
|
+
'ne.ug',
|
|
594
|
+
// uk - United Kingdom
|
|
595
|
+
'co.uk',
|
|
596
|
+
'org.uk',
|
|
597
|
+
'me.uk',
|
|
598
|
+
'net.uk',
|
|
599
|
+
// uy - Uruguay
|
|
600
|
+
'com.uy',
|
|
601
|
+
'net.uy',
|
|
602
|
+
'org.uy',
|
|
603
|
+
// uz - Uzbekistan
|
|
604
|
+
'co.uz',
|
|
605
|
+
'com.uz',
|
|
606
|
+
'net.uz',
|
|
607
|
+
'org.uz',
|
|
608
|
+
// vc - Saint Vincent and the Grenadines
|
|
609
|
+
'com.vc',
|
|
610
|
+
'net.vc',
|
|
611
|
+
'org.vc',
|
|
612
|
+
// ve - Venezuela
|
|
613
|
+
'co.ve',
|
|
614
|
+
'com.ve',
|
|
615
|
+
'net.ve',
|
|
616
|
+
'org.ve',
|
|
617
|
+
// vn - Vietnam
|
|
618
|
+
'com.vn',
|
|
619
|
+
'net.vn',
|
|
620
|
+
'org.vn',
|
|
621
|
+
// za - South Africa
|
|
622
|
+
'co.za',
|
|
623
|
+
'net.za',
|
|
624
|
+
'org.za',
|
|
625
|
+
// zm - Zambia
|
|
626
|
+
'co.zm',
|
|
627
|
+
'com.zm',
|
|
628
|
+
'net.zm',
|
|
629
|
+
'org.zm',
|
|
630
|
+
// zw - Zimbabwe
|
|
631
|
+
'co.zw',
|
|
632
|
+
'org.zw',
|
|
633
|
+
]);
|
|
634
|
+
const lastTwo = parts.slice(-2).join('.');
|
|
635
|
+
if (twoPartTlds.has(lastTwo) && parts.length >= 3) {
|
|
636
|
+
return parts.slice(-3).join('.');
|
|
637
|
+
}
|
|
638
|
+
return parts.length >= 2 ? parts.slice(-2).join('.') : hostname;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Check if a Cloudflare zone exists for the given domain.
|
|
642
|
+
* Gracefully handles authentication failures and network errors.
|
|
643
|
+
*/
|
|
644
|
+
export async function checkZoneExists(domain) {
|
|
645
|
+
try {
|
|
646
|
+
const tokenInfo = await getCloudflareApiToken();
|
|
647
|
+
if (!tokenInfo) {
|
|
648
|
+
return { found: false, error: 'Not logged in to Cloudflare (run: wrangler login)' };
|
|
649
|
+
}
|
|
650
|
+
const zoneName = extractZoneName(domain);
|
|
651
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4/zones?name=${encodeURIComponent(zoneName)}`, {
|
|
652
|
+
headers: {
|
|
653
|
+
Authorization: `Bearer ${tokenInfo.token}`,
|
|
654
|
+
},
|
|
655
|
+
});
|
|
656
|
+
if (!response.ok) {
|
|
657
|
+
if (response.status === 403) {
|
|
658
|
+
return { found: false, error: 'Token lacks zone:read permission' };
|
|
659
|
+
}
|
|
660
|
+
return { found: false, error: `Cloudflare API returned ${response.status}` };
|
|
661
|
+
}
|
|
662
|
+
const data = (await response.json());
|
|
663
|
+
if (!data.success || !data.result || data.result.length === 0) {
|
|
664
|
+
return { found: false };
|
|
665
|
+
}
|
|
666
|
+
const zone = data.result[0];
|
|
667
|
+
return {
|
|
668
|
+
found: true,
|
|
669
|
+
zone: { id: zone.id, name: zone.name, status: zone.status },
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
return {
|
|
674
|
+
found: false,
|
|
675
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
}
|
|
200
679
|
// =============================================================================
|
|
201
680
|
// D1 Database Operations
|
|
202
681
|
// =============================================================================
|
|
@@ -344,8 +823,76 @@ export async function executeD1Migration(dbName, sqlFilePath, onProgress) {
|
|
|
344
823
|
return { success: false, error: message };
|
|
345
824
|
}
|
|
346
825
|
}
|
|
826
|
+
/** SQL to create the migration tracking table (idempotent) */
|
|
827
|
+
const CREATE_MIGRATIONS_TABLE_SQL = `
|
|
828
|
+
CREATE TABLE IF NOT EXISTS authrim_migrations (
|
|
829
|
+
filename TEXT PRIMARY KEY,
|
|
830
|
+
applied_at INTEGER NOT NULL
|
|
831
|
+
);
|
|
832
|
+
`.trim();
|
|
347
833
|
/**
|
|
348
|
-
*
|
|
834
|
+
* Ensure the authrim_migrations tracking table exists in the target database.
|
|
835
|
+
* Returns true on success, false on failure.
|
|
836
|
+
*/
|
|
837
|
+
async function ensureMigrationsTable(dbName, onProgress) {
|
|
838
|
+
try {
|
|
839
|
+
await wrangler([
|
|
840
|
+
'd1',
|
|
841
|
+
'execute',
|
|
842
|
+
dbName,
|
|
843
|
+
'--remote',
|
|
844
|
+
'--yes',
|
|
845
|
+
'--command',
|
|
846
|
+
CREATE_MIGRATIONS_TABLE_SQL,
|
|
847
|
+
]);
|
|
848
|
+
return true;
|
|
849
|
+
}
|
|
850
|
+
catch (error) {
|
|
851
|
+
onProgress?.(` ⚠️ Could not create migrations table: ${error instanceof Error ? error.message : String(error)}`);
|
|
852
|
+
return false;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Return the set of migration filenames already recorded in authrim_migrations.
|
|
857
|
+
* Falls back to an empty set on error so we never skip migrations when unsure.
|
|
858
|
+
*/
|
|
859
|
+
async function getAppliedMigrations(dbName) {
|
|
860
|
+
try {
|
|
861
|
+
const { stdout } = await wrangler([
|
|
862
|
+
'd1',
|
|
863
|
+
'execute',
|
|
864
|
+
dbName,
|
|
865
|
+
'--remote',
|
|
866
|
+
'--yes',
|
|
867
|
+
'--command',
|
|
868
|
+
'SELECT filename FROM authrim_migrations;',
|
|
869
|
+
'--json',
|
|
870
|
+
]);
|
|
871
|
+
const rows = JSON.parse(stdout);
|
|
872
|
+
const results = rows?.[0]?.results ?? [];
|
|
873
|
+
return new Set(results.map((r) => r.filename));
|
|
874
|
+
}
|
|
875
|
+
catch {
|
|
876
|
+
return new Set();
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Record a migration filename as applied.
|
|
881
|
+
*/
|
|
882
|
+
async function recordMigration(dbName, filename) {
|
|
883
|
+
const sql = `INSERT OR IGNORE INTO authrim_migrations (filename, applied_at) VALUES ('${filename.replace(/'/g, "''")}', ${Date.now()});`;
|
|
884
|
+
try {
|
|
885
|
+
await wrangler(['d1', 'execute', dbName, '--remote', '--yes', '--command', sql]);
|
|
886
|
+
}
|
|
887
|
+
catch {
|
|
888
|
+
// Non-fatal: tracking failure should not abort the migration run
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Run all D1 migrations for a database.
|
|
893
|
+
*
|
|
894
|
+
* Uses an `authrim_migrations` tracking table inside the D1 database to skip
|
|
895
|
+
* files that have already been applied, making repeated runs idempotent.
|
|
349
896
|
*/
|
|
350
897
|
export async function runD1Migrations(dbName, migrationsDir, onProgress) {
|
|
351
898
|
const { existsSync, readdirSync } = await import('node:fs');
|
|
@@ -354,6 +901,7 @@ export async function runD1Migrations(dbName, migrationsDir, onProgress) {
|
|
|
354
901
|
return {
|
|
355
902
|
success: false,
|
|
356
903
|
appliedCount: 0,
|
|
904
|
+
skippedCount: 0,
|
|
357
905
|
error: `Migrations directory not found: ${migrationsDir}`,
|
|
358
906
|
};
|
|
359
907
|
}
|
|
@@ -363,18 +911,34 @@ export async function runD1Migrations(dbName, migrationsDir, onProgress) {
|
|
|
363
911
|
.sort();
|
|
364
912
|
if (sqlFiles.length === 0) {
|
|
365
913
|
onProgress?.(` No migration files found in ${migrationsDir}`);
|
|
366
|
-
return { success: true, appliedCount: 0 };
|
|
914
|
+
return { success: true, appliedCount: 0, skippedCount: 0 };
|
|
367
915
|
}
|
|
368
916
|
onProgress?.(` Found ${sqlFiles.length} migration files`);
|
|
917
|
+
// Ensure tracking table exists; if it fails we continue without tracking
|
|
918
|
+
await ensureMigrationsTable(dbName, onProgress);
|
|
919
|
+
const applied = await getAppliedMigrations(dbName);
|
|
920
|
+
onProgress?.(` ${applied.size} migration(s) already recorded as applied`);
|
|
369
921
|
let appliedCount = 0;
|
|
922
|
+
let skippedCount = 0;
|
|
370
923
|
for (const sqlFile of sqlFiles) {
|
|
924
|
+
if (applied.has(sqlFile)) {
|
|
925
|
+
onProgress?.(` ⏭ Skipping (already applied): ${sqlFile}`);
|
|
926
|
+
skippedCount++;
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
371
929
|
const result = await executeD1Migration(dbName, join(migrationsDir, sqlFile), onProgress);
|
|
372
930
|
if (!result.success) {
|
|
373
|
-
return {
|
|
931
|
+
return {
|
|
932
|
+
success: false,
|
|
933
|
+
appliedCount,
|
|
934
|
+
skippedCount,
|
|
935
|
+
error: `Failed on ${sqlFile}: ${result.error}`,
|
|
936
|
+
};
|
|
374
937
|
}
|
|
938
|
+
await recordMigration(dbName, sqlFile);
|
|
375
939
|
appliedCount++;
|
|
376
940
|
}
|
|
377
|
-
return { success: true, appliedCount };
|
|
941
|
+
return { success: true, appliedCount, skippedCount };
|
|
378
942
|
}
|
|
379
943
|
/**
|
|
380
944
|
* Run migrations for an Authrim environment
|
|
@@ -419,9 +983,9 @@ export async function runMigrationsForEnvironment(env, rootDir, onProgress) {
|
|
|
419
983
|
onProgress?.(` ❌ ${errorMsg}`);
|
|
420
984
|
return {
|
|
421
985
|
success: false,
|
|
422
|
-
core: { success: false, appliedCount: 0, error: errorMsg },
|
|
423
|
-
pii: { success: false, appliedCount: 0, error: errorMsg },
|
|
424
|
-
admin: { success: false, appliedCount: 0, error: errorMsg },
|
|
986
|
+
core: { success: false, appliedCount: 0, skippedCount: 0, error: errorMsg },
|
|
987
|
+
pii: { success: false, appliedCount: 0, skippedCount: 0, error: errorMsg },
|
|
988
|
+
admin: { success: false, appliedCount: 0, skippedCount: 0, error: errorMsg },
|
|
425
989
|
};
|
|
426
990
|
}
|
|
427
991
|
// Run core database migrations
|
|
@@ -431,7 +995,7 @@ export async function runMigrationsForEnvironment(env, rootDir, onProgress) {
|
|
|
431
995
|
onProgress?.(` ❌ Core migration failed: ${coreResult.error}`);
|
|
432
996
|
}
|
|
433
997
|
else {
|
|
434
|
-
onProgress?.(` ✅ Applied ${coreResult.appliedCount} core migrations`);
|
|
998
|
+
onProgress?.(` ✅ Applied ${coreResult.appliedCount} core migrations (${coreResult.skippedCount} skipped)`);
|
|
435
999
|
}
|
|
436
1000
|
// Run PII database migrations
|
|
437
1001
|
const piiMigrationsDir = join(migrationsRoot, 'pii');
|
|
@@ -439,7 +1003,7 @@ export async function runMigrationsForEnvironment(env, rootDir, onProgress) {
|
|
|
439
1003
|
let piiResult;
|
|
440
1004
|
if (!existsSync(piiMigrationsDir)) {
|
|
441
1005
|
onProgress?.(` ⚠️ PII migrations directory not found: ${piiMigrationsDir}`);
|
|
442
|
-
piiResult = { success: true, appliedCount: 0 };
|
|
1006
|
+
piiResult = { success: true, appliedCount: 0, skippedCount: 0 };
|
|
443
1007
|
}
|
|
444
1008
|
else {
|
|
445
1009
|
piiResult = await runD1Migrations(piiDbName, piiMigrationsDir, onProgress);
|
|
@@ -447,7 +1011,7 @@ export async function runMigrationsForEnvironment(env, rootDir, onProgress) {
|
|
|
447
1011
|
onProgress?.(` ❌ PII migration failed: ${piiResult.error}`);
|
|
448
1012
|
}
|
|
449
1013
|
else {
|
|
450
|
-
onProgress?.(` ✅ Applied ${piiResult.appliedCount} PII migrations`);
|
|
1014
|
+
onProgress?.(` ✅ Applied ${piiResult.appliedCount} PII migrations (${piiResult.skippedCount} skipped)`);
|
|
451
1015
|
}
|
|
452
1016
|
}
|
|
453
1017
|
// Run Admin database migrations
|
|
@@ -456,7 +1020,7 @@ export async function runMigrationsForEnvironment(env, rootDir, onProgress) {
|
|
|
456
1020
|
let adminResult;
|
|
457
1021
|
if (!existsSync(adminMigrationsDir)) {
|
|
458
1022
|
onProgress?.(` ⚠️ Admin migrations directory not found: ${adminMigrationsDir}`);
|
|
459
|
-
adminResult = { success: true, appliedCount: 0 };
|
|
1023
|
+
adminResult = { success: true, appliedCount: 0, skippedCount: 0 };
|
|
460
1024
|
}
|
|
461
1025
|
else {
|
|
462
1026
|
adminResult = await runD1Migrations(adminDbName, adminMigrationsDir, onProgress);
|
|
@@ -464,7 +1028,7 @@ export async function runMigrationsForEnvironment(env, rootDir, onProgress) {
|
|
|
464
1028
|
onProgress?.(` ❌ Admin migration failed: ${adminResult.error}`);
|
|
465
1029
|
}
|
|
466
1030
|
else {
|
|
467
|
-
onProgress?.(` ✅ Applied ${adminResult.appliedCount} admin migrations`);
|
|
1031
|
+
onProgress?.(` ✅ Applied ${adminResult.appliedCount} admin migrations (${adminResult.skippedCount} skipped)`);
|
|
468
1032
|
}
|
|
469
1033
|
}
|
|
470
1034
|
return {
|
|
@@ -604,7 +1168,14 @@ export async function createKVNamespace(name, preview = false) {
|
|
|
604
1168
|
*/
|
|
605
1169
|
export async function deleteKVNamespace(namespaceId) {
|
|
606
1170
|
try {
|
|
607
|
-
await wrangler([
|
|
1171
|
+
await wrangler([
|
|
1172
|
+
'kv',
|
|
1173
|
+
'namespace',
|
|
1174
|
+
'delete',
|
|
1175
|
+
'--namespace-id',
|
|
1176
|
+
namespaceId,
|
|
1177
|
+
'--skip-confirmation',
|
|
1178
|
+
]);
|
|
608
1179
|
return true;
|
|
609
1180
|
}
|
|
610
1181
|
catch {
|
|
@@ -837,18 +1408,31 @@ export async function provisionResources(options) {
|
|
|
837
1408
|
// Provision R2 buckets (optional)
|
|
838
1409
|
if (options.createR2) {
|
|
839
1410
|
onProgress('📁 R2 Buckets');
|
|
840
|
-
const
|
|
841
|
-
onProgress(` ⏳ Creating: ${
|
|
1411
|
+
const avatarBucketName = `${env}-authrim-avatars`;
|
|
1412
|
+
onProgress(` ⏳ Creating: ${avatarBucketName}...`);
|
|
842
1413
|
try {
|
|
843
|
-
const result = await createR2Bucket(
|
|
1414
|
+
const result = await createR2Bucket(avatarBucketName);
|
|
844
1415
|
resources.r2.push({
|
|
845
1416
|
binding: 'AVATARS',
|
|
846
1417
|
name: result.name,
|
|
847
1418
|
});
|
|
848
|
-
onProgress(` ✅ ${
|
|
1419
|
+
onProgress(` ✅ ${avatarBucketName} created`);
|
|
849
1420
|
}
|
|
850
1421
|
catch (error) {
|
|
851
|
-
onProgress(` ⚠️ Skipped: ${
|
|
1422
|
+
onProgress(` ⚠️ Skipped: ${avatarBucketName} - ${sanitizeError(error)}`);
|
|
1423
|
+
}
|
|
1424
|
+
const diagnosticBucketName = `${env}-diagnostic-logs`;
|
|
1425
|
+
onProgress(` ⏳ Creating: ${diagnosticBucketName}...`);
|
|
1426
|
+
try {
|
|
1427
|
+
const result = await createR2Bucket(diagnosticBucketName);
|
|
1428
|
+
resources.r2.push({
|
|
1429
|
+
binding: 'DIAGNOSTIC_LOGS',
|
|
1430
|
+
name: result.name,
|
|
1431
|
+
});
|
|
1432
|
+
onProgress(` ✅ ${diagnosticBucketName} created`);
|
|
1433
|
+
}
|
|
1434
|
+
catch (error) {
|
|
1435
|
+
onProgress(` ⚠️ Skipped: ${diagnosticBucketName} - ${sanitizeError(error)}`);
|
|
852
1436
|
}
|
|
853
1437
|
onProgress('');
|
|
854
1438
|
}
|
|
@@ -898,7 +1482,7 @@ const AUTHRIM_PATTERNS = {
|
|
|
898
1482
|
// KV can have either lowercase or uppercase env prefix (e.g., conformance-CLIENTS_CACHE or TESTENV-CLIENTS_CACHE)
|
|
899
1483
|
kv: /^([a-zA-Z][a-zA-Z0-9-]*)-(?:CLIENTS_CACHE|INITIAL_ACCESS_TOKENS|SETTINGS|REBAC_CACHE|USER_CACHE|AUTHRIM_CONFIG|STATE_STORE|CONSENT_CACHE)(?:_preview)?$/i,
|
|
900
1484
|
queue: /^([a-z][a-z0-9-]*)-audit-queue$/,
|
|
901
|
-
r2: /^([a-z][a-z0-9-]*)-authrim-avatars$/,
|
|
1485
|
+
r2: /^([a-z][a-z0-9-]*)-(authrim-avatars|diagnostic-logs)$/,
|
|
902
1486
|
// Pages projects: {env}-ar-admin-ui, {env}-ar-login-ui
|
|
903
1487
|
pages: /^([a-z][a-z0-9-]*)-(ar-admin-ui|ar-login-ui)$/,
|
|
904
1488
|
};
|
|
@@ -907,11 +1491,17 @@ const AUTHRIM_PATTERNS = {
|
|
|
907
1491
|
*/
|
|
908
1492
|
export async function listWorkers() {
|
|
909
1493
|
try {
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1494
|
+
const accountId = await getAccountId();
|
|
1495
|
+
if (!accountId)
|
|
1496
|
+
return [];
|
|
1497
|
+
const tokenInfo = await getCloudflareApiToken();
|
|
1498
|
+
if (!tokenInfo)
|
|
1499
|
+
return [];
|
|
1500
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/scripts`, { headers: { Authorization: `Bearer ${tokenInfo.token}` } });
|
|
1501
|
+
if (!response.ok)
|
|
1502
|
+
return [];
|
|
1503
|
+
const data = (await response.json());
|
|
1504
|
+
return (data.result || []).map((w) => ({ name: w.id }));
|
|
915
1505
|
}
|
|
916
1506
|
catch {
|
|
917
1507
|
return [];
|
|
@@ -996,6 +1586,46 @@ export async function listPagesProjects() {
|
|
|
996
1586
|
*/
|
|
997
1587
|
export async function deletePagesProject(name) {
|
|
998
1588
|
try {
|
|
1589
|
+
// First, remove all custom domains from the Pages project
|
|
1590
|
+
// This is required before the project can be deleted
|
|
1591
|
+
const token = await getCloudflareApiToken();
|
|
1592
|
+
if (token) {
|
|
1593
|
+
try {
|
|
1594
|
+
const accountId = await getAccountId();
|
|
1595
|
+
// Get project details to list custom domains
|
|
1596
|
+
const projectResponse = await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/pages/projects/${name}`, {
|
|
1597
|
+
headers: {
|
|
1598
|
+
Authorization: `Bearer ${token.token}`,
|
|
1599
|
+
},
|
|
1600
|
+
});
|
|
1601
|
+
if (projectResponse.ok) {
|
|
1602
|
+
const projectData = (await projectResponse.json());
|
|
1603
|
+
const domains = projectData.result?.domains || [];
|
|
1604
|
+
// Remove each custom domain (skip *.pages.dev domains)
|
|
1605
|
+
for (const domain of domains) {
|
|
1606
|
+
if (!domain.endsWith('.pages.dev')) {
|
|
1607
|
+
try {
|
|
1608
|
+
await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/pages/projects/${name}/domains/${domain}`, {
|
|
1609
|
+
method: 'DELETE',
|
|
1610
|
+
headers: {
|
|
1611
|
+
Authorization: `Bearer ${token.token}`,
|
|
1612
|
+
},
|
|
1613
|
+
});
|
|
1614
|
+
// Small delay to let Cloudflare process the deletion
|
|
1615
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1616
|
+
}
|
|
1617
|
+
catch {
|
|
1618
|
+
// Continue even if domain deletion fails
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
catch {
|
|
1625
|
+
// Continue even if custom domain cleanup fails
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
// Now delete the Pages project
|
|
999
1629
|
await wrangler(['pages', 'project', 'delete', name, '--yes']);
|
|
1000
1630
|
return true;
|
|
1001
1631
|
}
|
|
@@ -1009,13 +1639,16 @@ export async function deletePagesProject(name) {
|
|
|
1009
1639
|
export async function detectEnvironments(onProgress) {
|
|
1010
1640
|
const environments = new Map();
|
|
1011
1641
|
const progress = onProgress || (() => { });
|
|
1012
|
-
|
|
1642
|
+
// Scan Workers first — environments are only valid if Workers or D1 exist
|
|
1643
|
+
progress('Scanning Workers...');
|
|
1644
|
+
const workerEnvs = new Set();
|
|
1013
1645
|
try {
|
|
1014
|
-
const
|
|
1015
|
-
for (const
|
|
1016
|
-
const match =
|
|
1646
|
+
const workers = await listWorkers();
|
|
1647
|
+
for (const w of workers) {
|
|
1648
|
+
const match = w.name.match(AUTHRIM_PATTERNS.worker);
|
|
1017
1649
|
if (match) {
|
|
1018
1650
|
const env = match[1].toLowerCase();
|
|
1651
|
+
workerEnvs.add(env);
|
|
1019
1652
|
if (!environments.has(env)) {
|
|
1020
1653
|
environments.set(env, {
|
|
1021
1654
|
env,
|
|
@@ -1027,20 +1660,22 @@ export async function detectEnvironments(onProgress) {
|
|
|
1027
1660
|
pages: [],
|
|
1028
1661
|
});
|
|
1029
1662
|
}
|
|
1030
|
-
environments.get(env).
|
|
1663
|
+
environments.get(env).workers.push({ name: w.name });
|
|
1031
1664
|
}
|
|
1032
1665
|
}
|
|
1033
1666
|
}
|
|
1034
1667
|
catch (error) {
|
|
1035
|
-
progress(` ⚠️ Could not scan
|
|
1668
|
+
progress(` ⚠️ Could not scan Workers: ${error instanceof Error ? error.message : error}`);
|
|
1036
1669
|
}
|
|
1037
|
-
progress('Scanning
|
|
1670
|
+
progress('Scanning D1 databases...');
|
|
1671
|
+
const d1Envs = new Set();
|
|
1038
1672
|
try {
|
|
1039
|
-
const
|
|
1040
|
-
for (const
|
|
1041
|
-
const match =
|
|
1673
|
+
const databases = await listD1Databases();
|
|
1674
|
+
for (const db of databases) {
|
|
1675
|
+
const match = db.name.match(AUTHRIM_PATTERNS.d1);
|
|
1042
1676
|
if (match) {
|
|
1043
1677
|
const env = match[1].toLowerCase();
|
|
1678
|
+
d1Envs.add(env);
|
|
1044
1679
|
if (!environments.has(env)) {
|
|
1045
1680
|
environments.set(env, {
|
|
1046
1681
|
env,
|
|
@@ -1052,7 +1687,24 @@ export async function detectEnvironments(onProgress) {
|
|
|
1052
1687
|
pages: [],
|
|
1053
1688
|
});
|
|
1054
1689
|
}
|
|
1055
|
-
environments.get(env).
|
|
1690
|
+
environments.get(env).d1.push({ name: db.name, id: db.uuid });
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
catch (error) {
|
|
1695
|
+
progress(` ⚠️ Could not scan D1: ${error instanceof Error ? error.message : error}`);
|
|
1696
|
+
}
|
|
1697
|
+
progress('Scanning KV namespaces...');
|
|
1698
|
+
try {
|
|
1699
|
+
const namespaces = await listKVNamespaces();
|
|
1700
|
+
for (const ns of namespaces) {
|
|
1701
|
+
const match = ns.title.match(AUTHRIM_PATTERNS.kv);
|
|
1702
|
+
if (match) {
|
|
1703
|
+
const env = match[1].toLowerCase();
|
|
1704
|
+
// Only attach KV to environments that already have Workers or D1
|
|
1705
|
+
if (environments.has(env)) {
|
|
1706
|
+
environments.get(env).kv.push({ name: ns.title, id: ns.id });
|
|
1707
|
+
}
|
|
1056
1708
|
}
|
|
1057
1709
|
}
|
|
1058
1710
|
}
|
|
@@ -1066,18 +1718,10 @@ export async function detectEnvironments(onProgress) {
|
|
|
1066
1718
|
const match = q.name.match(AUTHRIM_PATTERNS.queue);
|
|
1067
1719
|
if (match) {
|
|
1068
1720
|
const env = match[1].toLowerCase();
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
workers: [],
|
|
1073
|
-
d1: [],
|
|
1074
|
-
kv: [],
|
|
1075
|
-
queues: [],
|
|
1076
|
-
r2: [],
|
|
1077
|
-
pages: [],
|
|
1078
|
-
});
|
|
1721
|
+
// Only attach Queues to environments that already have Workers or D1
|
|
1722
|
+
if (environments.has(env)) {
|
|
1723
|
+
environments.get(env).queues.push({ name: q.name, id: q.id });
|
|
1079
1724
|
}
|
|
1080
|
-
environments.get(env).queues.push({ name: q.name, id: q.id });
|
|
1081
1725
|
}
|
|
1082
1726
|
}
|
|
1083
1727
|
}
|
|
@@ -1091,18 +1735,10 @@ export async function detectEnvironments(onProgress) {
|
|
|
1091
1735
|
const match = bucket.name.match(AUTHRIM_PATTERNS.r2);
|
|
1092
1736
|
if (match) {
|
|
1093
1737
|
const env = match[1].toLowerCase();
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
workers: [],
|
|
1098
|
-
d1: [],
|
|
1099
|
-
kv: [],
|
|
1100
|
-
queues: [],
|
|
1101
|
-
r2: [],
|
|
1102
|
-
pages: [],
|
|
1103
|
-
});
|
|
1738
|
+
// Only attach R2 to environments that already have Workers or D1
|
|
1739
|
+
if (environments.has(env)) {
|
|
1740
|
+
environments.get(env).r2.push({ name: bucket.name });
|
|
1104
1741
|
}
|
|
1105
|
-
environments.get(env).r2.push({ name: bucket.name });
|
|
1106
1742
|
}
|
|
1107
1743
|
}
|
|
1108
1744
|
}
|
|
@@ -1116,42 +1752,20 @@ export async function detectEnvironments(onProgress) {
|
|
|
1116
1752
|
const match = project.name.match(AUTHRIM_PATTERNS.pages);
|
|
1117
1753
|
if (match) {
|
|
1118
1754
|
const env = match[1].toLowerCase();
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
workers: [],
|
|
1123
|
-
d1: [],
|
|
1124
|
-
kv: [],
|
|
1125
|
-
queues: [],
|
|
1126
|
-
r2: [],
|
|
1127
|
-
pages: [],
|
|
1128
|
-
});
|
|
1755
|
+
// Only attach Pages to environments that already have Workers or D1
|
|
1756
|
+
if (environments.has(env)) {
|
|
1757
|
+
environments.get(env).pages.push({ name: project.name });
|
|
1129
1758
|
}
|
|
1130
|
-
environments.get(env).pages.push({ name: project.name });
|
|
1131
1759
|
}
|
|
1132
1760
|
}
|
|
1133
1761
|
}
|
|
1134
1762
|
catch (error) {
|
|
1135
1763
|
progress(` ⚠️ Could not scan Pages: ${error instanceof Error ? error.message : error}`);
|
|
1136
1764
|
}
|
|
1137
|
-
//
|
|
1138
|
-
const workerComponents = [
|
|
1139
|
-
'ar-lib-core',
|
|
1140
|
-
'ar-auth',
|
|
1141
|
-
'ar-token',
|
|
1142
|
-
'ar-userinfo',
|
|
1143
|
-
'ar-discovery',
|
|
1144
|
-
'ar-management',
|
|
1145
|
-
'ar-router',
|
|
1146
|
-
'ar-async',
|
|
1147
|
-
'ar-saml',
|
|
1148
|
-
'ar-bridge',
|
|
1149
|
-
'ar-vc',
|
|
1150
|
-
'ar-policy',
|
|
1151
|
-
];
|
|
1765
|
+
// Filter: only keep environments that have actual Workers or D1 databases
|
|
1152
1766
|
for (const [env, info] of environments) {
|
|
1153
|
-
|
|
1154
|
-
|
|
1767
|
+
if (info.workers.length === 0 && info.d1.length === 0) {
|
|
1768
|
+
environments.delete(env);
|
|
1155
1769
|
}
|
|
1156
1770
|
}
|
|
1157
1771
|
progress(`Found ${environments.size} environment(s)`);
|