@notionx/core 0.1.0

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 (208) hide show
  1. package/dist/admin/index.d.ts +137 -0
  2. package/dist/admin/index.js +206 -0
  3. package/dist/admin/index.js.map +1 -0
  4. package/dist/admin/pages/index.d.ts +324 -0
  5. package/dist/admin/pages/index.js +827 -0
  6. package/dist/admin/pages/index.js.map +1 -0
  7. package/dist/auth/auth-pages/forgot-password.d.ts +20 -0
  8. package/dist/auth/auth-pages/forgot-password.js +70 -0
  9. package/dist/auth/auth-pages/forgot-password.js.map +1 -0
  10. package/dist/auth/auth-pages/index.d.ts +6 -0
  11. package/dist/auth/auth-pages/index.js +342 -0
  12. package/dist/auth/auth-pages/index.js.map +1 -0
  13. package/dist/auth/auth-pages/login.d.ts +30 -0
  14. package/dist/auth/auth-pages/login.js +125 -0
  15. package/dist/auth/auth-pages/login.js.map +1 -0
  16. package/dist/auth/auth-pages/register.d.ts +17 -0
  17. package/dist/auth/auth-pages/register.js +81 -0
  18. package/dist/auth/auth-pages/register.js.map +1 -0
  19. package/dist/auth/auth-pages/reset-password.d.ts +18 -0
  20. package/dist/auth/auth-pages/reset-password.js +72 -0
  21. package/dist/auth/auth-pages/reset-password.js.map +1 -0
  22. package/dist/auth/index.d.ts +72 -0
  23. package/dist/auth/index.js +1011 -0
  24. package/dist/auth/index.js.map +1 -0
  25. package/dist/auth/passwords.d.ts +6 -0
  26. package/dist/auth/passwords.js +79 -0
  27. package/dist/auth/passwords.js.map +1 -0
  28. package/dist/auth/rate-limit.d.ts +28 -0
  29. package/dist/auth/rate-limit.js +245 -0
  30. package/dist/auth/rate-limit.js.map +1 -0
  31. package/dist/auth/routes/google-callback.d.ts +6 -0
  32. package/dist/auth/routes/google-callback.js +404 -0
  33. package/dist/auth/routes/google-callback.js.map +1 -0
  34. package/dist/auth/routes/google.d.ts +6 -0
  35. package/dist/auth/routes/google.js +250 -0
  36. package/dist/auth/routes/google.js.map +1 -0
  37. package/dist/auth/routes/index.d.ts +22 -0
  38. package/dist/auth/routes/index.js +619 -0
  39. package/dist/auth/routes/index.js.map +1 -0
  40. package/dist/auth/routes/verify-email.d.ts +6 -0
  41. package/dist/auth/routes/verify-email.js +317 -0
  42. package/dist/auth/routes/verify-email.js.map +1 -0
  43. package/dist/auth/routes/viewer.d.ts +6 -0
  44. package/dist/auth/routes/viewer.js +372 -0
  45. package/dist/auth/routes/viewer.js.map +1 -0
  46. package/dist/auth/session.d.ts +9 -0
  47. package/dist/auth/session.js +1 -0
  48. package/dist/auth/session.js.map +1 -0
  49. package/dist/auth/turnstile.d.ts +20 -0
  50. package/dist/auth/turnstile.js +301 -0
  51. package/dist/auth/turnstile.js.map +1 -0
  52. package/dist/auth/user-session.d.ts +42 -0
  53. package/dist/auth/user-session.js +419 -0
  54. package/dist/auth/user-session.js.map +1 -0
  55. package/dist/auth/users.d.ts +112 -0
  56. package/dist/auth/users.js +558 -0
  57. package/dist/auth/users.js.map +1 -0
  58. package/dist/bootstrap-CN2g76M6.d.ts +67 -0
  59. package/dist/cache/index.d.ts +6 -0
  60. package/dist/cache/index.js +47 -0
  61. package/dist/cache/index.js.map +1 -0
  62. package/dist/content/admin-summary.d.ts +24 -0
  63. package/dist/content/admin-summary.js +36 -0
  64. package/dist/content/admin-summary.js.map +1 -0
  65. package/dist/content/index.d.ts +9 -0
  66. package/dist/content/index.js +473 -0
  67. package/dist/content/index.js.map +1 -0
  68. package/dist/content/models.d.ts +69 -0
  69. package/dist/content/models.js +24 -0
  70. package/dist/content/models.js.map +1 -0
  71. package/dist/content/prewarm.d.ts +28 -0
  72. package/dist/content/prewarm.js +56 -0
  73. package/dist/content/prewarm.js.map +1 -0
  74. package/dist/content/revalidate.d.ts +37 -0
  75. package/dist/content/revalidate.js +170 -0
  76. package/dist/content/revalidate.js.map +1 -0
  77. package/dist/content/search-index.d.ts +54 -0
  78. package/dist/content/search-index.js +172 -0
  79. package/dist/content/search-index.js.map +1 -0
  80. package/dist/content/search.d.ts +8 -0
  81. package/dist/content/search.js +57 -0
  82. package/dist/content/search.js.map +1 -0
  83. package/dist/doctor/cli.d.ts +1 -0
  84. package/dist/doctor/cli.js +360 -0
  85. package/dist/doctor/cli.js.map +1 -0
  86. package/dist/doctor/index.d.ts +139 -0
  87. package/dist/doctor/index.js +289 -0
  88. package/dist/doctor/index.js.map +1 -0
  89. package/dist/email/index.d.ts +38 -0
  90. package/dist/email/index.js +126 -0
  91. package/dist/email/index.js.map +1 -0
  92. package/dist/env-C5qu-0R-.d.ts +35 -0
  93. package/dist/hooks/index.d.ts +2 -0
  94. package/dist/hooks/index.js +1 -0
  95. package/dist/hooks/index.js.map +1 -0
  96. package/dist/i18n/index.d.ts +26 -0
  97. package/dist/i18n/index.js +73 -0
  98. package/dist/i18n/index.js.map +1 -0
  99. package/dist/index.d.ts +8 -0
  100. package/dist/index.js +1281 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/internal/admin/index.d.ts +75 -0
  103. package/dist/internal/admin/index.js +365 -0
  104. package/dist/internal/admin/index.js.map +1 -0
  105. package/dist/media/index.d.ts +24 -0
  106. package/dist/media/index.js +86 -0
  107. package/dist/media/index.js.map +1 -0
  108. package/dist/media/routes/index.d.ts +1 -0
  109. package/dist/media/routes/index.js +585 -0
  110. package/dist/media/routes/index.js.map +1 -0
  111. package/dist/media/routes/notion-media.d.ts +19 -0
  112. package/dist/media/routes/notion-media.js +588 -0
  113. package/dist/media/routes/notion-media.js.map +1 -0
  114. package/dist/middleware.d.ts +95 -0
  115. package/dist/middleware.js +79 -0
  116. package/dist/middleware.js.map +1 -0
  117. package/dist/notion/block-text.d.ts +5 -0
  118. package/dist/notion/block-text.js +37 -0
  119. package/dist/notion/block-text.js.map +1 -0
  120. package/dist/notion/blocks.d.ts +24 -0
  121. package/dist/notion/blocks.js +46 -0
  122. package/dist/notion/blocks.js.map +1 -0
  123. package/dist/notion/client.d.ts +7 -0
  124. package/dist/notion/client.js +13 -0
  125. package/dist/notion/client.js.map +1 -0
  126. package/dist/notion/config.d.ts +25 -0
  127. package/dist/notion/config.js +147 -0
  128. package/dist/notion/config.js.map +1 -0
  129. package/dist/notion/content-cache.d.ts +45 -0
  130. package/dist/notion/content-cache.js +166 -0
  131. package/dist/notion/content-cache.js.map +1 -0
  132. package/dist/notion/generic-source.d.ts +61 -0
  133. package/dist/notion/generic-source.js +408 -0
  134. package/dist/notion/generic-source.js.map +1 -0
  135. package/dist/notion/index.d.ts +13 -0
  136. package/dist/notion/index.js +1278 -0
  137. package/dist/notion/index.js.map +1 -0
  138. package/dist/notion/mappers.d.ts +1 -0
  139. package/dist/notion/mappers.js +152 -0
  140. package/dist/notion/mappers.js.map +1 -0
  141. package/dist/notion/media.d.ts +22 -0
  142. package/dist/notion/media.js +209 -0
  143. package/dist/notion/media.js.map +1 -0
  144. package/dist/notion/property-mappers.d.ts +24 -0
  145. package/dist/notion/property-mappers.js +152 -0
  146. package/dist/notion/property-mappers.js.map +1 -0
  147. package/dist/notion/routes/index.d.ts +8 -0
  148. package/dist/notion/routes/index.js +428 -0
  149. package/dist/notion/routes/index.js.map +1 -0
  150. package/dist/notion/routes/webhook.d.ts +98 -0
  151. package/dist/notion/routes/webhook.js +428 -0
  152. package/dist/notion/routes/webhook.js.map +1 -0
  153. package/dist/notion/types.d.ts +152 -0
  154. package/dist/notion/types.js +1 -0
  155. package/dist/notion/types.js.map +1 -0
  156. package/dist/notion/webhook.d.ts +83 -0
  157. package/dist/notion/webhook.js +490 -0
  158. package/dist/notion/webhook.js.map +1 -0
  159. package/dist/platform/capabilities.d.ts +34 -0
  160. package/dist/platform/capabilities.js +42 -0
  161. package/dist/platform/capabilities.js.map +1 -0
  162. package/dist/platform/current.d.ts +13 -0
  163. package/dist/platform/current.js +181 -0
  164. package/dist/platform/current.js.map +1 -0
  165. package/dist/platform/index.d.ts +5 -0
  166. package/dist/platform/index.js +269 -0
  167. package/dist/platform/index.js.map +1 -0
  168. package/dist/platform/runtime.d.ts +118 -0
  169. package/dist/platform/runtime.js +160 -0
  170. package/dist/platform/runtime.js.map +1 -0
  171. package/dist/platform/selection.d.ts +10 -0
  172. package/dist/platform/selection.js +22 -0
  173. package/dist/platform/selection.js.map +1 -0
  174. package/dist/storage/index.d.ts +17 -0
  175. package/dist/storage/index.js +218 -0
  176. package/dist/storage/index.js.map +1 -0
  177. package/dist/storage/routes/cdn.d.ts +19 -0
  178. package/dist/storage/routes/cdn.js +289 -0
  179. package/dist/storage/routes/cdn.js.map +1 -0
  180. package/dist/storage/routes/files.d.ts +27 -0
  181. package/dist/storage/routes/files.js +216 -0
  182. package/dist/storage/routes/files.js.map +1 -0
  183. package/dist/storage/routes/index.d.ts +2 -0
  184. package/dist/storage/routes/index.js +352 -0
  185. package/dist/storage/routes/index.js.map +1 -0
  186. package/dist/types-BsAcZSNX.d.ts +94 -0
  187. package/dist/types.d.ts +78 -0
  188. package/dist/types.js +1 -0
  189. package/dist/types.js.map +1 -0
  190. package/dist/util/index.d.ts +18 -0
  191. package/dist/util/index.js +48 -0
  192. package/dist/util/index.js.map +1 -0
  193. package/dist/worker/index.d.ts +6 -0
  194. package/dist/worker/index.js +1026 -0
  195. package/dist/worker/index.js.map +1 -0
  196. package/dist/worker/routes/content-prewarm.d.ts +34 -0
  197. package/dist/worker/routes/content-prewarm.js +38 -0
  198. package/dist/worker/routes/content-prewarm.js.map +1 -0
  199. package/dist/worker/routes/content-revalidate.d.ts +81 -0
  200. package/dist/worker/routes/content-revalidate.js +64 -0
  201. package/dist/worker/routes/content-revalidate.js.map +1 -0
  202. package/dist/worker/routes/health.d.ts +14 -0
  203. package/dist/worker/routes/health.js +278 -0
  204. package/dist/worker/routes/health.js.map +1 -0
  205. package/dist/worker/routes/index.d.ts +6 -0
  206. package/dist/worker/routes/index.js +373 -0
  207. package/dist/worker/routes/index.js.map +1 -0
  208. package/package.json +124 -0
@@ -0,0 +1,619 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/auth/routes/google.ts
9
+ import { NextResponse } from "next/server";
10
+
11
+ // src/internal/admin/settings.ts
12
+ import { cache } from "react";
13
+
14
+ // src/util/env.ts
15
+ import { env } from "cloudflare:workers";
16
+ var workerEnv = env;
17
+
18
+ // src/platform/runtime.ts
19
+ function cacheRequestForKey(key) {
20
+ return new Request(key, { method: "GET" });
21
+ }
22
+ function createCloudflarePublicCacheAdapter(cache2) {
23
+ return {
24
+ kind: "cloudflare-cache",
25
+ async match(key) {
26
+ return await cache2.match(cacheRequestForKey(key)) ?? null;
27
+ },
28
+ put(key, response) {
29
+ return cache2.put(cacheRequestForKey(key), response);
30
+ },
31
+ delete(key) {
32
+ return cache2.delete(cacheRequestForKey(key));
33
+ }
34
+ };
35
+ }
36
+ function createCloudflareKeyValueCacheAdapter(namespace) {
37
+ return {
38
+ kind: "workers-kv",
39
+ async get(key, options) {
40
+ return await namespace.get(key, {
41
+ type: "json",
42
+ cacheTtl: options?.cacheTtl
43
+ });
44
+ },
45
+ async put(key, value, options) {
46
+ await namespace.put(key, JSON.stringify(value), {
47
+ expirationTtl: options?.expirationTtl,
48
+ metadata: options?.metadata
49
+ });
50
+ },
51
+ delete(key) {
52
+ return namespace.delete(key);
53
+ },
54
+ async list(options) {
55
+ const result = await namespace.list({
56
+ prefix: options?.prefix,
57
+ limit: options?.limit,
58
+ cursor: options?.cursor
59
+ });
60
+ return {
61
+ keys: result.keys.map((key) => ({ name: key.name })),
62
+ cursor: result.list_complete ? void 0 : result.cursor,
63
+ listComplete: result.list_complete
64
+ };
65
+ }
66
+ };
67
+ }
68
+ function r2ObjectToStoredObject(object) {
69
+ return {
70
+ body: object.body,
71
+ size: object.size,
72
+ etag: object.etag,
73
+ contentType: object.httpMetadata?.contentType
74
+ };
75
+ }
76
+ function createCloudflareRuntimePlatform(env2, options) {
77
+ const database = env2.DB ? {
78
+ kind: "d1",
79
+ prepare(query) {
80
+ return env2.DB.prepare(query);
81
+ },
82
+ async batch(statements) {
83
+ return await env2.DB.batch(
84
+ statements
85
+ );
86
+ }
87
+ } : null;
88
+ const objectStorage = env2.ASSETS_BUCKET ? {
89
+ kind: "r2",
90
+ async get(key) {
91
+ const object = await env2.ASSETS_BUCKET?.get(key);
92
+ return object ? r2ObjectToStoredObject(object) : null;
93
+ },
94
+ async put(key, value, options2) {
95
+ await env2.ASSETS_BUCKET?.put(key, value, {
96
+ httpMetadata: {
97
+ contentType: options2?.contentType,
98
+ cacheControl: options2?.cacheControl
99
+ },
100
+ customMetadata: options2?.metadata
101
+ });
102
+ },
103
+ async delete(key) {
104
+ await env2.ASSETS_BUCKET?.delete(key);
105
+ },
106
+ async list(options2) {
107
+ const listed = await env2.ASSETS_BUCKET?.list({
108
+ prefix: options2?.prefix,
109
+ limit: options2?.limit
110
+ });
111
+ return listed?.objects.map((object) => ({
112
+ key: object.key,
113
+ size: object.size,
114
+ uploaded: object.uploaded
115
+ })) ?? [];
116
+ }
117
+ } : null;
118
+ const imageTransformer = env2.IMAGES ? {
119
+ kind: "cloudflare-images",
120
+ async transform(body, options2) {
121
+ const result = await env2.IMAGES.input(body).transform(options2.width ? { width: options2.width } : {}).output({
122
+ format: options2.format,
123
+ quality: options2.quality
124
+ });
125
+ return {
126
+ body: result.image(),
127
+ contentType: result.contentType(),
128
+ response: () => result.response()
129
+ };
130
+ }
131
+ } : null;
132
+ const keyValueCache = env2.CONTENT_CACHE ? createCloudflareKeyValueCacheAdapter(env2.CONTENT_CACHE) : null;
133
+ return {
134
+ id: "cloudflare-workers",
135
+ database,
136
+ objectStorage,
137
+ imageTransformer,
138
+ keyValueCache,
139
+ publicCache: options?.publicCache ? createCloudflarePublicCacheAdapter(options.publicCache) : null
140
+ };
141
+ }
142
+
143
+ // src/platform/cloudflare-runtime.ts
144
+ function getDefaultCloudflareCache() {
145
+ const globalWithCaches = globalThis;
146
+ return globalWithCaches.caches?.default ?? null;
147
+ }
148
+ function getRuntimePlatform() {
149
+ return createCloudflareRuntimePlatform(workerEnv, {
150
+ publicCache: getDefaultCloudflareCache()
151
+ });
152
+ }
153
+
154
+ // src/platform/current.ts
155
+ function getRuntimePlatform2() {
156
+ return getRuntimePlatform();
157
+ }
158
+ function getDatabase() {
159
+ const platform = getRuntimePlatform2();
160
+ const database = platform.database;
161
+ if (!database) {
162
+ throw new Error(`SQL database adapter not configured for ${platform.id}`);
163
+ }
164
+ return database;
165
+ }
166
+
167
+ // src/internal/admin/settings.ts
168
+ var DEFAULT_ADMIN_EMAIL = "zhaofilms@gmail.com";
169
+ function rowToSettings(r) {
170
+ return {
171
+ site_title: r.site_title,
172
+ google_enabled: r.google_enabled === 1 ? 1 : 0,
173
+ google_client_id: r.google_client_id,
174
+ google_client_secret: r.google_client_secret,
175
+ google_updated_at: r.google_updated_at,
176
+ turnstile_enabled: r.turnstile_enabled === 1 ? 1 : 0,
177
+ turnstile_site_key: r.turnstile_site_key,
178
+ turnstile_updated_at: r.turnstile_updated_at,
179
+ admin_email: r.admin_email,
180
+ updated_at: r.updated_at
181
+ };
182
+ }
183
+ var getAppSettingsCached = cache(async () => {
184
+ const row = await getDatabase().prepare(
185
+ `SELECT site_title, google_enabled, google_client_id, google_client_secret,
186
+ google_updated_at, turnstile_enabled, turnstile_site_key,
187
+ turnstile_updated_at, admin_email, updated_at
188
+ FROM app_settings WHERE id = 1`
189
+ ).first();
190
+ if (!row) {
191
+ return {
192
+ site_title: "vinext Blog",
193
+ google_enabled: 0,
194
+ google_client_id: null,
195
+ google_client_secret: null,
196
+ google_updated_at: null,
197
+ turnstile_enabled: 0,
198
+ turnstile_site_key: null,
199
+ turnstile_updated_at: null,
200
+ admin_email: DEFAULT_ADMIN_EMAIL,
201
+ updated_at: ""
202
+ };
203
+ }
204
+ return rowToSettings(row);
205
+ });
206
+ async function getAppSettings() {
207
+ return getAppSettingsCached();
208
+ }
209
+ async function getGoogleOAuthConfig() {
210
+ const s = await getAppSettings();
211
+ if (!s.google_enabled) return null;
212
+ if (!s.google_client_id || !s.google_client_secret) return null;
213
+ return {
214
+ enabled: true,
215
+ clientId: s.google_client_id,
216
+ clientSecret: s.google_client_secret
217
+ };
218
+ }
219
+
220
+ // src/auth/routes/google.ts
221
+ var STATE_COOKIE = "vinext_oauth_state";
222
+ async function GET(request) {
223
+ const config = await getGoogleOAuthConfig();
224
+ if (!config) {
225
+ return new NextResponse(
226
+ "Google OAuth not configured. Enable it in admin /admin/settings.",
227
+ { status: 503 }
228
+ );
229
+ }
230
+ const state = [...crypto.getRandomValues(new Uint8Array(24))].map((b) => b.toString(16).padStart(2, "0")).join("");
231
+ const url = new URL(request.url);
232
+ const origin = `${url.protocol}//${url.host}`;
233
+ const redirectUri = `${origin}/api/auth/google/callback`;
234
+ const googleAuthUrl = new URL("https://accounts.google.com/o/oauth2/v2/auth");
235
+ googleAuthUrl.searchParams.set("client_id", config.clientId);
236
+ googleAuthUrl.searchParams.set("redirect_uri", redirectUri);
237
+ googleAuthUrl.searchParams.set("response_type", "code");
238
+ googleAuthUrl.searchParams.set("scope", "openid email profile");
239
+ googleAuthUrl.searchParams.set("state", state);
240
+ googleAuthUrl.searchParams.set("access_type", "offline");
241
+ googleAuthUrl.searchParams.set("prompt", "consent");
242
+ const res = NextResponse.redirect(googleAuthUrl.toString());
243
+ res.cookies.set(STATE_COOKIE, state, {
244
+ httpOnly: true,
245
+ secure: url.protocol === "https:",
246
+ sameSite: "lax",
247
+ path: "/",
248
+ maxAge: 600
249
+ });
250
+ return res;
251
+ }
252
+
253
+ // src/auth/routes/google-callback.ts
254
+ import { NextResponse as NextResponse2 } from "next/server";
255
+ import { cookies as cookies2 } from "next/headers";
256
+
257
+ // src/internal/admin/admin.ts
258
+ function normalizeEmail(email) {
259
+ return email.trim().toLowerCase();
260
+ }
261
+ async function isAdminEmail(email) {
262
+ if (!email) return false;
263
+ const settings = await getAppSettings();
264
+ return normalizeEmail(email) === normalizeEmail(settings.admin_email);
265
+ }
266
+
267
+ // src/auth/users.ts
268
+ function normalizeEmail2(email) {
269
+ return email.trim().toLowerCase();
270
+ }
271
+ async function defaultRoleFor(email) {
272
+ return await isAdminEmail(email) ? "admin" : "user";
273
+ }
274
+ function userToSession(user) {
275
+ return {
276
+ uid: user.id,
277
+ email: user.email,
278
+ name: user.name,
279
+ picture: user.picture,
280
+ rev: user.session_rev ?? 0
281
+ };
282
+ }
283
+ async function upsertGoogleUser(input) {
284
+ const db = getDatabase();
285
+ const email = normalizeEmail2(input.email);
286
+ const existing = await db.prepare(
287
+ `SELECT * FROM users WHERE google_sub = ? OR email = ? LIMIT 1`
288
+ ).bind(input.googleSub, email).first();
289
+ if (existing) {
290
+ await db.prepare(
291
+ `UPDATE users
292
+ SET email = ?, name = ?, picture = ?, google_sub = ?, email_verified = 1,
293
+ email_verify_token = NULL, email_verify_expires_at = NULL,
294
+ last_seen_at = datetime('now')
295
+ WHERE id = ?`
296
+ ).bind(email, input.name, input.picture, input.googleSub, existing.id).run();
297
+ } else {
298
+ const role = await defaultRoleFor(email);
299
+ await db.prepare(
300
+ `INSERT INTO users (
301
+ email, name, picture, google_sub, email_verified, role, last_seen_at
302
+ ) VALUES (?, ?, ?, ?, 1, ?, datetime('now'))`
303
+ ).bind(email, input.name, input.picture, input.googleSub, role).run();
304
+ }
305
+ if (await isAdminEmail(email)) {
306
+ await db.prepare(
307
+ `UPDATE users SET role = 'admin' WHERE email = ?`
308
+ ).bind(email).run();
309
+ }
310
+ const user = await getUserByEmail(email);
311
+ if (!user) throw new Error("User upsert failed");
312
+ return user;
313
+ }
314
+ async function verifyEmailUser(token) {
315
+ const user = await getDatabase().prepare(
316
+ `SELECT * FROM users WHERE email_verify_token = ?`
317
+ ).bind(token).first();
318
+ if (!user || !user.email_verify_expires_at) return null;
319
+ if (new Date(user.email_verify_expires_at).getTime() < Date.now()) {
320
+ return null;
321
+ }
322
+ await getDatabase().prepare(
323
+ `UPDATE users
324
+ SET email_verified = 1,
325
+ email_verify_token = NULL,
326
+ email_verify_expires_at = NULL,
327
+ last_seen_at = datetime('now')
328
+ WHERE id = ?`
329
+ ).bind(user.id).run();
330
+ return getUserByEmail(user.email);
331
+ }
332
+ async function getUserByEmail(email) {
333
+ return await getDatabase().prepare(`SELECT * FROM users WHERE email = ?`).bind(normalizeEmail2(email)).first();
334
+ }
335
+ async function getUserById(id) {
336
+ return await getDatabase().prepare(`SELECT * FROM users WHERE id = ?`).bind(id).first();
337
+ }
338
+
339
+ // src/auth/user-session.ts
340
+ import { cookies } from "next/headers";
341
+ var SESSION_TTL_SECONDS = 60 * 60 * 24 * 7;
342
+ var USER_COOKIE_NAME = "vinext_user_session";
343
+ var ADMIN_COOKIE_NAME = "vinext_admin_session";
344
+ var USER_COOKIE = USER_COOKIE_NAME;
345
+ function getAdminPassword() {
346
+ let fromWorker;
347
+ try {
348
+ const mod = __require("cloudflare:workers");
349
+ fromWorker = mod.env?.ADMIN_PASSWORD;
350
+ } catch {
351
+ fromWorker = void 0;
352
+ }
353
+ if (fromWorker) return fromWorker;
354
+ return process.env.ADMIN_PASSWORD ?? "vinext-admin-2026";
355
+ }
356
+ async function hmac(secret, message) {
357
+ const enc = new TextEncoder();
358
+ const key = await crypto.subtle.importKey(
359
+ "raw",
360
+ enc.encode(secret),
361
+ { name: "HMAC", hash: "SHA-256" },
362
+ false,
363
+ ["sign"]
364
+ );
365
+ const sig = await crypto.subtle.sign("HMAC", key, enc.encode(message));
366
+ return [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join("");
367
+ }
368
+ async function constantTimeEqual(a, b) {
369
+ const aBytes = new TextEncoder().encode(a);
370
+ const bBytes = new TextEncoder().encode(b);
371
+ if (aBytes.length !== bBytes.length) return false;
372
+ let diff = 0;
373
+ for (let i = 0; i < aBytes.length; i++) {
374
+ diff |= aBytes[i] ^ bBytes[i];
375
+ }
376
+ return diff === 0;
377
+ }
378
+ async function signPayload(payload) {
379
+ return hmac(getAdminPassword(), payload);
380
+ }
381
+ function utf8ToBase64(s) {
382
+ const bytes = new TextEncoder().encode(s);
383
+ let bin = "";
384
+ for (const b of bytes) bin += String.fromCharCode(b);
385
+ return btoa(bin);
386
+ }
387
+ function base64ToUtf8(b64) {
388
+ const bin = atob(b64);
389
+ const bytes = new Uint8Array(bin.length);
390
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
391
+ return new TextDecoder().decode(bytes);
392
+ }
393
+ async function signUserToken(user) {
394
+ const exp = Math.floor(Date.now() / 1e3) + SESSION_TTL_SECONDS;
395
+ const payload = { ...user, exp };
396
+ const json = JSON.stringify(payload);
397
+ const b64 = utf8ToBase64(json);
398
+ const sig = await signPayload(b64);
399
+ return `${b64}.${sig}`;
400
+ }
401
+ async function verifyUserToken(token) {
402
+ if (!token) return null;
403
+ const parts = token.split(".");
404
+ if (parts.length !== 2) return null;
405
+ const [b64, sig] = parts;
406
+ const expected = await signPayload(b64);
407
+ if (!await constantTimeEqual(sig, expected)) return null;
408
+ try {
409
+ const json = base64ToUtf8(b64);
410
+ const payload = JSON.parse(json);
411
+ if (payload.exp < Math.floor(Date.now() / 1e3)) return null;
412
+ const dbUser = await getUserById(payload.uid);
413
+ if (!dbUser) return null;
414
+ if (dbUser.email !== payload.email) return null;
415
+ const tokenRev = payload.rev ?? 0;
416
+ const dbRev = dbUser.session_rev ?? 0;
417
+ if (tokenRev !== dbRev) return null;
418
+ return {
419
+ uid: payload.uid,
420
+ email: payload.email,
421
+ name: payload.name,
422
+ picture: payload.picture,
423
+ rev: dbRev
424
+ };
425
+ } catch {
426
+ return null;
427
+ }
428
+ }
429
+ async function verifyAdminToken(token) {
430
+ if (!token) return false;
431
+ const parts = token.split(".");
432
+ if (parts.length !== 3) return false;
433
+ const [flag, expStr, sig] = parts;
434
+ if (flag !== "ok") return false;
435
+ const exp = Number(expStr);
436
+ if (!Number.isFinite(exp) || exp < Math.floor(Date.now() / 1e3)) return false;
437
+ const password = getAdminPassword();
438
+ const expected = await hmac(password, `${flag}.${exp}`);
439
+ return constantTimeEqual(sig, expected);
440
+ }
441
+ function getAdminEmailFromEnv() {
442
+ let fromWorker;
443
+ try {
444
+ const mod = __require("cloudflare:workers");
445
+ fromWorker = mod.env?.ADMIN_EMAIL;
446
+ } catch {
447
+ fromWorker = void 0;
448
+ }
449
+ return (fromWorker || "zhaofilms@gmail.com").toLowerCase();
450
+ }
451
+ async function getAuthViewer() {
452
+ const jar = await cookies();
453
+ if (await verifyAdminToken(jar.get(ADMIN_COOKIE_NAME)?.value)) {
454
+ return {
455
+ email: getAdminEmailFromEnv(),
456
+ user: null,
457
+ role: "admin",
458
+ isAdmin: true,
459
+ isVip: true,
460
+ canViewVipContent: true
461
+ };
462
+ }
463
+ const user = await verifyUserToken(jar.get(USER_COOKIE_NAME)?.value);
464
+ if (!user) return null;
465
+ const dbUser = await getUserById(user.uid);
466
+ if (!dbUser) return null;
467
+ const role = dbUser.role === "admin" || dbUser.role === "vip" ? dbUser.role : "user";
468
+ const isAdmin = role === "admin";
469
+ const isVip = role === "vip" || isAdmin;
470
+ return {
471
+ email: user.email,
472
+ user,
473
+ role,
474
+ isAdmin,
475
+ isVip,
476
+ canViewVipContent: isVip
477
+ };
478
+ }
479
+
480
+ // src/auth/routes/google-callback.ts
481
+ var STATE_COOKIE2 = "vinext_oauth_state";
482
+ async function GET2(request) {
483
+ const config = await getGoogleOAuthConfig();
484
+ if (!config) {
485
+ return new NextResponse2("Google OAuth not configured", { status: 503 });
486
+ }
487
+ const url = new URL(request.url);
488
+ const code = url.searchParams.get("code");
489
+ const state = url.searchParams.get("state");
490
+ const error = url.searchParams.get("error");
491
+ if (error) {
492
+ return new NextResponse2(`Google OAuth error: ${error}`, { status: 400 });
493
+ }
494
+ if (!code || !state) {
495
+ return new NextResponse2("Missing code or state", { status: 400 });
496
+ }
497
+ const jar = await cookies2();
498
+ const savedState = jar.get(STATE_COOKIE2)?.value;
499
+ if (!savedState || savedState !== state) {
500
+ return new NextResponse2("Invalid state (CSRF protection)", { status: 400 });
501
+ }
502
+ const origin = `${url.protocol}//${url.host}`;
503
+ const redirectUri = `${origin}/api/auth/google/callback`;
504
+ try {
505
+ const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
506
+ method: "POST",
507
+ headers: { "content-type": "application/x-www-form-urlencoded" },
508
+ body: new URLSearchParams({
509
+ code,
510
+ client_id: config.clientId,
511
+ client_secret: config.clientSecret,
512
+ redirect_uri: redirectUri,
513
+ grant_type: "authorization_code"
514
+ })
515
+ });
516
+ if (!tokenRes.ok) {
517
+ const t = await tokenRes.text();
518
+ throw new Error(`Token exchange failed: ${tokenRes.status} ${t}`);
519
+ }
520
+ const tokens = await tokenRes.json();
521
+ const userRes = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
522
+ headers: { Authorization: `Bearer ${tokens.access_token}` }
523
+ });
524
+ if (!userRes.ok) throw new Error("Failed to fetch user info");
525
+ const profile = await userRes.json();
526
+ if (!profile.verified_email) {
527
+ return new NextResponse2("Google email not verified", { status: 403 });
528
+ }
529
+ const user = await upsertGoogleUser({
530
+ email: profile.email,
531
+ name: profile.name,
532
+ picture: profile.picture,
533
+ googleSub: profile.id
534
+ });
535
+ const token = await signUserToken(userToSession(user));
536
+ const res = NextResponse2.redirect(`${origin}/admin`);
537
+ res.cookies.set(USER_COOKIE, token, {
538
+ httpOnly: true,
539
+ secure: url.protocol === "https:",
540
+ sameSite: "lax",
541
+ path: "/",
542
+ maxAge: 60 * 60 * 24 * 7
543
+ });
544
+ res.cookies.set(STATE_COOKIE2, "", { path: "/", maxAge: 0 });
545
+ return res;
546
+ } catch (e) {
547
+ const msg = e instanceof Error ? e.message : String(e);
548
+ return new NextResponse2(`OAuth callback error: ${msg}`, { status: 500 });
549
+ }
550
+ }
551
+
552
+ // src/auth/routes/verify-email.ts
553
+ import { NextResponse as NextResponse3 } from "next/server";
554
+ async function GET3(request) {
555
+ const url = new URL(request.url);
556
+ const token = url.searchParams.get("token");
557
+ if (!token) {
558
+ return NextResponse3.redirect(new URL("/login?verifyError=invalid", url));
559
+ }
560
+ const user = await verifyEmailUser(token);
561
+ if (!user) {
562
+ return NextResponse3.redirect(new URL("/login?verifyError=invalid", url));
563
+ }
564
+ const sessionToken = await signUserToken(userToSession(user));
565
+ const res = NextResponse3.redirect(new URL("/admin?verified=1", url));
566
+ res.cookies.set(USER_COOKIE, sessionToken, {
567
+ httpOnly: true,
568
+ secure: url.protocol === "https:",
569
+ sameSite: "lax",
570
+ path: "/",
571
+ maxAge: 60 * 60 * 24 * 7
572
+ });
573
+ return res;
574
+ }
575
+
576
+ // src/auth/routes/viewer.ts
577
+ import { NextResponse as NextResponse4 } from "next/server";
578
+ function jsonResponse(body, init) {
579
+ const headers = new Headers(init?.headers);
580
+ headers.set("Cache-Control", "no-store");
581
+ return NextResponse4.json(body, { ...init, headers });
582
+ }
583
+ async function GET4() {
584
+ const viewer = await getAuthViewer();
585
+ if (!viewer) {
586
+ return jsonResponse(
587
+ { ok: false, reason: "unauthenticated" },
588
+ { status: 401 }
589
+ );
590
+ }
591
+ return jsonResponse({
592
+ ok: true,
593
+ email: viewer.email,
594
+ role: viewer.role,
595
+ isAdmin: viewer.isAdmin,
596
+ isVip: viewer.isVip,
597
+ canViewVipContent: viewer.canViewVipContent,
598
+ user: viewer.user ? {
599
+ name: viewer.user.name,
600
+ picture: viewer.user.picture
601
+ } : null
602
+ });
603
+ }
604
+
605
+ // src/auth/routes/index.ts
606
+ var authRoutes = {
607
+ "/api/auth/google": { GET: "./google" },
608
+ "/api/auth/google/callback": { GET: "./google-callback" },
609
+ "/api/auth/verify-email": { GET: "./verify-email" },
610
+ "/api/auth/viewer": { GET: "./viewer" }
611
+ };
612
+ export {
613
+ authRoutes,
614
+ GET2 as googleCallbackGET,
615
+ GET as googleGET,
616
+ GET3 as verifyEmailGET,
617
+ GET4 as viewerGET
618
+ };
619
+ //# sourceMappingURL=index.js.map