@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 @@
1
+ {"version":3,"sources":["../../../src/auth/auth-pages/login.tsx","../../../src/auth/auth-pages/register.tsx","../../../src/auth/auth-pages/forgot-password.tsx","../../../src/auth/auth-pages/reset-password.tsx"],"sourcesContent":["// auth/auth-pages/login.tsx\n//\n// Login page. Renders the \"Sign in with Google\" button when OAuth is\n// configured plus the email/password form, with optional Turnstile\n// captcha. All UI primitives are passed in via the `ui` prop so the\n// package stays unopinionated about the design system.\n\nimport { redirect } from \"next/navigation\";\nimport type { AuthPageContext } from \"./types\";\n\nexport interface LoginPageProps {\n ui: AuthPageContext[\"ui\"];\n searchParams: {\n error?: string;\n loginError?: string;\n registered?: string;\n email?: string;\n verifyError?: string;\n resendSent?: string;\n resendError?: string;\n retry?: string;\n accountDeleted?: string;\n };\n emailLoginAction: AuthPageContext[\"actions\"] extends infer A\n ? A extends { emailLogin: infer F }\n ? F\n : never\n : never;\n resendVerificationAction?: AuthPageContext[\"actions\"] extends infer A\n ? A extends { resendVerification: infer F }\n ? F\n : never\n : never;\n isAuthenticated?: AuthPageContext[\"isAuthenticated\"];\n getCurrentUser?: AuthPageContext[\"getCurrentUser\"];\n getGoogleOAuthConfig?: AuthPageContext[\"getGoogleOAuthConfig\"];\n redirectWhenAuthenticated?: string;\n}\n\nexport default async function LoginPage({\n ui,\n searchParams,\n emailLoginAction,\n resendVerificationAction,\n isAuthenticated,\n getCurrentUser,\n getGoogleOAuthConfig,\n redirectWhenAuthenticated = \"/admin\",\n}: LoginPageProps) {\n if (isAuthenticated && (await isAuthenticated())) {\n redirect(redirectWhenAuthenticated);\n }\n const {\n loginError,\n registered,\n email,\n verifyError,\n resendSent,\n resendError,\n retry,\n accountDeleted,\n } = searchParams;\n const user = getCurrentUser ? await getCurrentUser() : null;\n const googleCfg = getGoogleOAuthConfig ? await getGoogleOAuthConfig() : null;\n const googleEnabled = Boolean(googleCfg);\n const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent, Separator, Turnstile } = ui;\n\n return (\n <main\n style={{\n minHeight: \"100vh\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n background: \"linear-gradient(to bottom right, var(--background), var(--muted))\",\n }}\n >\n <Card className=\"w-full max-w-sm\">\n <CardHeader>\n <CardTitle>后台登录</CardTitle>\n <CardDescription>多种方式登录管理面板</CardDescription>\n </CardHeader>\n <CardContent>\n {accountDeleted && (\n <div className=\"rounded-md bg-muted px-3 py-2 text-sm text-muted-foreground\">\n 账户已注销。如需再次使用,请重新注册。\n </div>\n )}\n {registered && (\n <div className=\"rounded-md bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-400\">\n 验证邮件已发送到 {email ?? \"你的邮箱\"},请先验证后再登录。\n </div>\n )}\n {verifyError && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 验证链接无效或已过期,请重新发送验证邮件。\n </div>\n )}\n {resendSent && (\n <div className=\"rounded-md bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-400\">\n 如果 {email ?? \"该邮箱\"} 有待验证账户,验证邮件已重新发送。\n </div>\n )}\n {resendError === \"verified\" && (\n <div className=\"rounded-md bg-amber-500/10 px-3 py-2 text-sm text-amber-700 dark:text-amber-400\">\n 该邮箱已验证,请直接登录。\n </div>\n )}\n {resendError === \"captcha\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 请完成人机验证后再重发邮件\n </div>\n )}\n {resendError === \"rate\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 重发过于频繁,请 {retry ?? \"900\"} 秒后再试\n </div>\n )}\n\n {googleEnabled ? (\n <Button asChild variant=\"outline\" className=\"w-full\" size=\"lg\">\n <a href=\"/api/auth/google\">使用 Google 登录</a>\n </Button>\n ) : (\n <div className=\"rounded-md border border-dashed p-3 text-xs text-muted-foreground\">\n Google 登录未启用(管理员可在 <a href=\"/admin/settings\" className=\"underline\">/admin/settings</a> 配置)\n </div>\n )}\n\n <Separator />\n\n <form action={emailLoginAction as unknown as (formData: FormData) => void}>\n <Label htmlFor=\"email\">邮箱</Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n required\n />\n <Label htmlFor=\"email-password\">密码</Label>\n <Input\n id=\"email-password\"\n name=\"password\"\n type=\"password\"\n placeholder=\"输入密码\"\n required\n />\n {loginError === \"rate\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 登录尝试过多,请 {retry ?? \"900\"} 秒后再试\n </div>\n )}\n {loginError === \"captcha\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 请完成人机验证后再试\n </div>\n )}\n {loginError === \"invalid\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 邮箱或密码错误\n </div>\n )}\n {loginError === \"unverified\" && (\n <div className=\"rounded-md bg-amber-500/10 px-3 py-2 text-sm text-amber-700 dark:text-amber-400\">\n 邮箱还没有验证,请先打开验证邮件中的链接。\n </div>\n )}\n {Turnstile && <Turnstile action=\"login\" />}\n <Button type=\"submit\" className=\"w-full\">使用邮箱登录</Button>\n </form>\n\n {loginError === \"unverified\" && email && resendVerificationAction && (\n <form action={resendVerificationAction as unknown as (formData: FormData) => void}>\n <input type=\"hidden\" name=\"email\" value={email} />\n {Turnstile && <Turnstile action=\"resend_verify\" />}\n <Button type=\"submit\" variant=\"outline\" size=\"sm\" className=\"w-full\">\n 重新发送验证邮件\n </Button>\n </form>\n )}\n\n <Button asChild variant=\"secondary\" className=\"w-full\">\n <a href=\"/register\">新用户注册</a>\n </Button>\n\n {user && (\n <p className=\"text-center text-xs text-muted-foreground\">\n 当前已登录:{user.email}\n </p>\n )}\n </CardContent>\n </Card>\n </main>\n );\n}\n","// auth/auth-pages/register.tsx\n\nimport { redirect } from \"next/navigation\";\nimport type { AuthPageContext } from \"./types\";\n\nexport interface RegisterPageProps {\n ui: AuthPageContext[\"ui\"];\n searchParams: { error?: string };\n registerAction: AuthPageContext[\"actions\"] extends infer A\n ? A extends { register: infer F }\n ? F\n : never\n : never;\n isAuthenticated?: AuthPageContext[\"isAuthenticated\"];\n redirectWhenAuthenticated?: string;\n}\n\nexport default async function RegisterPage({\n ui,\n searchParams,\n registerAction,\n isAuthenticated,\n redirectWhenAuthenticated = \"/admin\",\n}: RegisterPageProps) {\n if (isAuthenticated && (await isAuthenticated())) {\n redirect(redirectWhenAuthenticated);\n }\n const { error } = searchParams;\n const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent, Turnstile } = ui;\n\n return (\n <main\n style={{\n minHeight: \"100vh\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n background: \"linear-gradient(to bottom right, var(--background), var(--muted))\",\n }}\n >\n <Card className=\"w-full max-w-sm\">\n <CardHeader>\n <CardTitle>邮箱注册</CardTitle>\n <CardDescription>\n 注册后会发送验证邮件,验证完成即可进入后台。\n </CardDescription>\n </CardHeader>\n <CardContent>\n <form action={registerAction as unknown as (formData: FormData) => void}>\n <Label htmlFor=\"email\">邮箱</Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n autoFocus\n required\n />\n <Label htmlFor=\"password\">密码</Label>\n <Input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n placeholder=\"至少 8 位,包含字母和数字\"\n required\n />\n <Label htmlFor=\"confirmPassword\">确认密码</Label>\n <Input\n id=\"confirmPassword\"\n name=\"confirmPassword\"\n type=\"password\"\n placeholder=\"再次输入密码\"\n required\n />\n {error && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n {error}\n </div>\n )}\n {Turnstile && <Turnstile action=\"register\" />}\n <Button type=\"submit\" className=\"w-full\">注册并发送验证邮件</Button>\n </form>\n\n <Button asChild variant=\"ghost\" className=\"w-full\">\n <a href=\"/login\">返回登录</a>\n </Button>\n </CardContent>\n </Card>\n </main>\n );\n}\n","// auth/auth-pages/forgot-password.tsx\n\nimport { redirect } from \"next/navigation\";\nimport type { AuthPageContext } from \"./types\";\n\nexport interface ForgotPasswordPageProps {\n ui: AuthPageContext[\"ui\"];\n searchParams: {\n sent?: string;\n email?: string;\n error?: string;\n retry?: string;\n };\n forgotPasswordAction: AuthPageContext[\"actions\"] extends infer A\n ? A extends { forgotPassword: infer F }\n ? F\n : never\n : never;\n isAuthenticated?: AuthPageContext[\"isAuthenticated\"];\n redirectWhenAuthenticated?: string;\n}\n\nexport default async function ForgotPasswordPage({\n ui,\n searchParams,\n forgotPasswordAction,\n isAuthenticated,\n redirectWhenAuthenticated = \"/admin\",\n}: ForgotPasswordPageProps) {\n if (isAuthenticated && (await isAuthenticated())) {\n redirect(redirectWhenAuthenticated);\n }\n const { sent, email, error, retry } = searchParams;\n const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent, Turnstile } = ui;\n\n return (\n <main\n style={{\n minHeight: \"100vh\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n background: \"linear-gradient(to bottom right, var(--background), var(--muted))\",\n }}\n >\n <Card className=\"w-full max-w-sm\">\n <CardHeader>\n <CardTitle>忘记密码</CardTitle>\n <CardDescription>\n 输入注册邮箱,我们会发送重置链接(1 小时内有效)。\n </CardDescription>\n </CardHeader>\n <CardContent>\n {sent && (\n <div className=\"rounded-md bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-400\">\n 如果 {email ?? \"该邮箱\"} 已注册且已验证,你会收到重置密码邮件。\n </div>\n )}\n {error === \"invalid\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 邮箱格式不正确\n </div>\n )}\n {error === \"captcha\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 请完成人机验证后再试\n </div>\n )}\n {error === \"rate\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 请求过于频繁,请 {retry ?? \"900\"} 秒后再试\n </div>\n )}\n\n <form action={forgotPasswordAction as unknown as (formData: FormData) => void}>\n <Label htmlFor=\"email\">邮箱</Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n defaultValue={email ?? \"\"}\n required\n />\n {Turnstile && <Turnstile action=\"forgot_password\" />}\n <Button type=\"submit\" className=\"w-full\">发送重置链接</Button>\n </form>\n\n <Button asChild variant=\"ghost\" className=\"w-full\">\n <a href=\"/login\">返回登录</a>\n </Button>\n </CardContent>\n </Card>\n </main>\n );\n}\n","// auth/auth-pages/reset-password.tsx\n\nimport { redirect } from \"next/navigation\";\nimport type { AuthPageContext } from \"./types\";\n\nexport interface ResetPasswordPageProps {\n ui: AuthPageContext[\"ui\"];\n searchParams: {\n token?: string;\n error?: string;\n };\n resetPasswordAction: AuthPageContext[\"actions\"] extends infer A\n ? A extends { resetPassword: infer F }\n ? F\n : never\n : never;\n isAuthenticated?: AuthPageContext[\"isAuthenticated\"];\n redirectWhenAuthenticated?: string;\n}\n\nexport default async function ResetPasswordPage({\n ui,\n searchParams,\n resetPasswordAction,\n isAuthenticated,\n redirectWhenAuthenticated = \"/admin\",\n}: ResetPasswordPageProps) {\n if (isAuthenticated && (await isAuthenticated())) {\n redirect(redirectWhenAuthenticated);\n }\n const { token, error } = searchParams;\n if (!token) {\n redirect(\"/forgot-password?error=missing\");\n }\n const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent } = ui;\n\n return (\n <main\n style={{\n minHeight: \"100vh\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n background: \"linear-gradient(to bottom right, var(--background), var(--muted))\",\n }}\n >\n <Card className=\"w-full max-w-sm\">\n <CardHeader>\n <CardTitle>设置新密码</CardTitle>\n <CardDescription>请输入新密码,提交后将自动登录。</CardDescription>\n </CardHeader>\n <CardContent>\n {error && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n {error}\n </div>\n )}\n\n <form action={resetPasswordAction as unknown as (formData: FormData) => void}>\n <input type=\"hidden\" name=\"token\" value={token} />\n <Label htmlFor=\"password\">新密码</Label>\n <Input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n placeholder=\"至少 8 位,包含字母和数字\"\n required\n />\n <Label htmlFor=\"confirmPassword\">确认新密码</Label>\n <Input\n id=\"confirmPassword\"\n name=\"confirmPassword\"\n type=\"password\"\n placeholder=\"再次输入新密码\"\n required\n />\n <Button type=\"submit\" className=\"w-full\">更新密码并登录</Button>\n </form>\n\n <Button asChild variant=\"ghost\" className=\"w-full\">\n <a href=\"/login\">返回登录</a>\n </Button>\n </CardContent>\n </Card>\n </main>\n );\n}\n"],"mappings":"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","names":["redirect","jsx","jsxs","redirect","jsx","jsxs","redirect","jsx","jsxs"]}
@@ -0,0 +1,30 @@
1
+ import * as react from 'react';
2
+ import { A as AuthPageContext } from '../../types-BsAcZSNX.js';
3
+
4
+ interface LoginPageProps {
5
+ ui: AuthPageContext["ui"];
6
+ searchParams: {
7
+ error?: string;
8
+ loginError?: string;
9
+ registered?: string;
10
+ email?: string;
11
+ verifyError?: string;
12
+ resendSent?: string;
13
+ resendError?: string;
14
+ retry?: string;
15
+ accountDeleted?: string;
16
+ };
17
+ emailLoginAction: AuthPageContext["actions"] extends infer A ? A extends {
18
+ emailLogin: infer F;
19
+ } ? F : never : never;
20
+ resendVerificationAction?: AuthPageContext["actions"] extends infer A ? A extends {
21
+ resendVerification: infer F;
22
+ } ? F : never : never;
23
+ isAuthenticated?: AuthPageContext["isAuthenticated"];
24
+ getCurrentUser?: AuthPageContext["getCurrentUser"];
25
+ getGoogleOAuthConfig?: AuthPageContext["getGoogleOAuthConfig"];
26
+ redirectWhenAuthenticated?: string;
27
+ }
28
+ declare function LoginPage({ ui, searchParams, emailLoginAction, resendVerificationAction, isAuthenticated, getCurrentUser, getGoogleOAuthConfig, redirectWhenAuthenticated, }: LoginPageProps): Promise<react.JSX.Element>;
29
+
30
+ export { type LoginPageProps, LoginPage as default };
@@ -0,0 +1,125 @@
1
+ // src/auth/auth-pages/login.tsx
2
+ import { redirect } from "next/navigation";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ async function LoginPage({
5
+ ui,
6
+ searchParams,
7
+ emailLoginAction,
8
+ resendVerificationAction,
9
+ isAuthenticated,
10
+ getCurrentUser,
11
+ getGoogleOAuthConfig,
12
+ redirectWhenAuthenticated = "/admin"
13
+ }) {
14
+ if (isAuthenticated && await isAuthenticated()) {
15
+ redirect(redirectWhenAuthenticated);
16
+ }
17
+ const {
18
+ loginError,
19
+ registered,
20
+ email,
21
+ verifyError,
22
+ resendSent,
23
+ resendError,
24
+ retry,
25
+ accountDeleted
26
+ } = searchParams;
27
+ const user = getCurrentUser ? await getCurrentUser() : null;
28
+ const googleCfg = getGoogleOAuthConfig ? await getGoogleOAuthConfig() : null;
29
+ const googleEnabled = Boolean(googleCfg);
30
+ const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent, Separator, Turnstile } = ui;
31
+ return /* @__PURE__ */ jsx(
32
+ "main",
33
+ {
34
+ style: {
35
+ minHeight: "100vh",
36
+ display: "flex",
37
+ alignItems: "center",
38
+ justifyContent: "center",
39
+ padding: "1rem",
40
+ background: "linear-gradient(to bottom right, var(--background), var(--muted))"
41
+ },
42
+ children: /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-sm", children: [
43
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
44
+ /* @__PURE__ */ jsx(CardTitle, { children: "\u540E\u53F0\u767B\u5F55" }),
45
+ /* @__PURE__ */ jsx(CardDescription, { children: "\u591A\u79CD\u65B9\u5F0F\u767B\u5F55\u7BA1\u7406\u9762\u677F" })
46
+ ] }),
47
+ /* @__PURE__ */ jsxs(CardContent, { children: [
48
+ accountDeleted && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-muted px-3 py-2 text-sm text-muted-foreground", children: "\u8D26\u6237\u5DF2\u6CE8\u9500\u3002\u5982\u9700\u518D\u6B21\u4F7F\u7528\uFF0C\u8BF7\u91CD\u65B0\u6CE8\u518C\u3002" }),
49
+ registered && /* @__PURE__ */ jsxs("div", { className: "rounded-md bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-400", children: [
50
+ "\u9A8C\u8BC1\u90AE\u4EF6\u5DF2\u53D1\u9001\u5230 ",
51
+ email ?? "\u4F60\u7684\u90AE\u7BB1",
52
+ "\uFF0C\u8BF7\u5148\u9A8C\u8BC1\u540E\u518D\u767B\u5F55\u3002"
53
+ ] }),
54
+ verifyError && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: "\u9A8C\u8BC1\u94FE\u63A5\u65E0\u6548\u6216\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u9A8C\u8BC1\u90AE\u4EF6\u3002" }),
55
+ resendSent && /* @__PURE__ */ jsxs("div", { className: "rounded-md bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-400", children: [
56
+ "\u5982\u679C ",
57
+ email ?? "\u8BE5\u90AE\u7BB1",
58
+ " \u6709\u5F85\u9A8C\u8BC1\u8D26\u6237\uFF0C\u9A8C\u8BC1\u90AE\u4EF6\u5DF2\u91CD\u65B0\u53D1\u9001\u3002"
59
+ ] }),
60
+ resendError === "verified" && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-amber-500/10 px-3 py-2 text-sm text-amber-700 dark:text-amber-400", children: "\u8BE5\u90AE\u7BB1\u5DF2\u9A8C\u8BC1\uFF0C\u8BF7\u76F4\u63A5\u767B\u5F55\u3002" }),
61
+ resendError === "captcha" && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: "\u8BF7\u5B8C\u6210\u4EBA\u673A\u9A8C\u8BC1\u540E\u518D\u91CD\u53D1\u90AE\u4EF6" }),
62
+ resendError === "rate" && /* @__PURE__ */ jsxs("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
63
+ "\u91CD\u53D1\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7 ",
64
+ retry ?? "900",
65
+ " \u79D2\u540E\u518D\u8BD5"
66
+ ] }),
67
+ googleEnabled ? /* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", className: "w-full", size: "lg", children: /* @__PURE__ */ jsx("a", { href: "/api/auth/google", children: "\u4F7F\u7528 Google \u767B\u5F55" }) }) : /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-dashed p-3 text-xs text-muted-foreground", children: [
68
+ "Google \u767B\u5F55\u672A\u542F\u7528\uFF08\u7BA1\u7406\u5458\u53EF\u5728 ",
69
+ /* @__PURE__ */ jsx("a", { href: "/admin/settings", className: "underline", children: "/admin/settings" }),
70
+ " \u914D\u7F6E\uFF09"
71
+ ] }),
72
+ /* @__PURE__ */ jsx(Separator, {}),
73
+ /* @__PURE__ */ jsxs("form", { action: emailLoginAction, children: [
74
+ /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: "\u90AE\u7BB1" }),
75
+ /* @__PURE__ */ jsx(
76
+ Input,
77
+ {
78
+ id: "email",
79
+ name: "email",
80
+ type: "email",
81
+ placeholder: "you@example.com",
82
+ required: true
83
+ }
84
+ ),
85
+ /* @__PURE__ */ jsx(Label, { htmlFor: "email-password", children: "\u5BC6\u7801" }),
86
+ /* @__PURE__ */ jsx(
87
+ Input,
88
+ {
89
+ id: "email-password",
90
+ name: "password",
91
+ type: "password",
92
+ placeholder: "\u8F93\u5165\u5BC6\u7801",
93
+ required: true
94
+ }
95
+ ),
96
+ loginError === "rate" && /* @__PURE__ */ jsxs("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
97
+ "\u767B\u5F55\u5C1D\u8BD5\u8FC7\u591A\uFF0C\u8BF7 ",
98
+ retry ?? "900",
99
+ " \u79D2\u540E\u518D\u8BD5"
100
+ ] }),
101
+ loginError === "captcha" && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: "\u8BF7\u5B8C\u6210\u4EBA\u673A\u9A8C\u8BC1\u540E\u518D\u8BD5" }),
102
+ loginError === "invalid" && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: "\u90AE\u7BB1\u6216\u5BC6\u7801\u9519\u8BEF" }),
103
+ loginError === "unverified" && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-amber-500/10 px-3 py-2 text-sm text-amber-700 dark:text-amber-400", children: "\u90AE\u7BB1\u8FD8\u6CA1\u6709\u9A8C\u8BC1\uFF0C\u8BF7\u5148\u6253\u5F00\u9A8C\u8BC1\u90AE\u4EF6\u4E2D\u7684\u94FE\u63A5\u3002" }),
104
+ Turnstile && /* @__PURE__ */ jsx(Turnstile, { action: "login" }),
105
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "w-full", children: "\u4F7F\u7528\u90AE\u7BB1\u767B\u5F55" })
106
+ ] }),
107
+ loginError === "unverified" && email && resendVerificationAction && /* @__PURE__ */ jsxs("form", { action: resendVerificationAction, children: [
108
+ /* @__PURE__ */ jsx("input", { type: "hidden", name: "email", value: email }),
109
+ Turnstile && /* @__PURE__ */ jsx(Turnstile, { action: "resend_verify" }),
110
+ /* @__PURE__ */ jsx(Button, { type: "submit", variant: "outline", size: "sm", className: "w-full", children: "\u91CD\u65B0\u53D1\u9001\u9A8C\u8BC1\u90AE\u4EF6" })
111
+ ] }),
112
+ /* @__PURE__ */ jsx(Button, { asChild: true, variant: "secondary", className: "w-full", children: /* @__PURE__ */ jsx("a", { href: "/register", children: "\u65B0\u7528\u6237\u6CE8\u518C" }) }),
113
+ user && /* @__PURE__ */ jsxs("p", { className: "text-center text-xs text-muted-foreground", children: [
114
+ "\u5F53\u524D\u5DF2\u767B\u5F55\uFF1A",
115
+ user.email
116
+ ] })
117
+ ] })
118
+ ] })
119
+ }
120
+ );
121
+ }
122
+ export {
123
+ LoginPage as default
124
+ };
125
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/auth/auth-pages/login.tsx"],"sourcesContent":["// auth/auth-pages/login.tsx\n//\n// Login page. Renders the \"Sign in with Google\" button when OAuth is\n// configured plus the email/password form, with optional Turnstile\n// captcha. All UI primitives are passed in via the `ui` prop so the\n// package stays unopinionated about the design system.\n\nimport { redirect } from \"next/navigation\";\nimport type { AuthPageContext } from \"./types\";\n\nexport interface LoginPageProps {\n ui: AuthPageContext[\"ui\"];\n searchParams: {\n error?: string;\n loginError?: string;\n registered?: string;\n email?: string;\n verifyError?: string;\n resendSent?: string;\n resendError?: string;\n retry?: string;\n accountDeleted?: string;\n };\n emailLoginAction: AuthPageContext[\"actions\"] extends infer A\n ? A extends { emailLogin: infer F }\n ? F\n : never\n : never;\n resendVerificationAction?: AuthPageContext[\"actions\"] extends infer A\n ? A extends { resendVerification: infer F }\n ? F\n : never\n : never;\n isAuthenticated?: AuthPageContext[\"isAuthenticated\"];\n getCurrentUser?: AuthPageContext[\"getCurrentUser\"];\n getGoogleOAuthConfig?: AuthPageContext[\"getGoogleOAuthConfig\"];\n redirectWhenAuthenticated?: string;\n}\n\nexport default async function LoginPage({\n ui,\n searchParams,\n emailLoginAction,\n resendVerificationAction,\n isAuthenticated,\n getCurrentUser,\n getGoogleOAuthConfig,\n redirectWhenAuthenticated = \"/admin\",\n}: LoginPageProps) {\n if (isAuthenticated && (await isAuthenticated())) {\n redirect(redirectWhenAuthenticated);\n }\n const {\n loginError,\n registered,\n email,\n verifyError,\n resendSent,\n resendError,\n retry,\n accountDeleted,\n } = searchParams;\n const user = getCurrentUser ? await getCurrentUser() : null;\n const googleCfg = getGoogleOAuthConfig ? await getGoogleOAuthConfig() : null;\n const googleEnabled = Boolean(googleCfg);\n const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent, Separator, Turnstile } = ui;\n\n return (\n <main\n style={{\n minHeight: \"100vh\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n background: \"linear-gradient(to bottom right, var(--background), var(--muted))\",\n }}\n >\n <Card className=\"w-full max-w-sm\">\n <CardHeader>\n <CardTitle>后台登录</CardTitle>\n <CardDescription>多种方式登录管理面板</CardDescription>\n </CardHeader>\n <CardContent>\n {accountDeleted && (\n <div className=\"rounded-md bg-muted px-3 py-2 text-sm text-muted-foreground\">\n 账户已注销。如需再次使用,请重新注册。\n </div>\n )}\n {registered && (\n <div className=\"rounded-md bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-400\">\n 验证邮件已发送到 {email ?? \"你的邮箱\"},请先验证后再登录。\n </div>\n )}\n {verifyError && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 验证链接无效或已过期,请重新发送验证邮件。\n </div>\n )}\n {resendSent && (\n <div className=\"rounded-md bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-400\">\n 如果 {email ?? \"该邮箱\"} 有待验证账户,验证邮件已重新发送。\n </div>\n )}\n {resendError === \"verified\" && (\n <div className=\"rounded-md bg-amber-500/10 px-3 py-2 text-sm text-amber-700 dark:text-amber-400\">\n 该邮箱已验证,请直接登录。\n </div>\n )}\n {resendError === \"captcha\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 请完成人机验证后再重发邮件\n </div>\n )}\n {resendError === \"rate\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 重发过于频繁,请 {retry ?? \"900\"} 秒后再试\n </div>\n )}\n\n {googleEnabled ? (\n <Button asChild variant=\"outline\" className=\"w-full\" size=\"lg\">\n <a href=\"/api/auth/google\">使用 Google 登录</a>\n </Button>\n ) : (\n <div className=\"rounded-md border border-dashed p-3 text-xs text-muted-foreground\">\n Google 登录未启用(管理员可在 <a href=\"/admin/settings\" className=\"underline\">/admin/settings</a> 配置)\n </div>\n )}\n\n <Separator />\n\n <form action={emailLoginAction as unknown as (formData: FormData) => void}>\n <Label htmlFor=\"email\">邮箱</Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n required\n />\n <Label htmlFor=\"email-password\">密码</Label>\n <Input\n id=\"email-password\"\n name=\"password\"\n type=\"password\"\n placeholder=\"输入密码\"\n required\n />\n {loginError === \"rate\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 登录尝试过多,请 {retry ?? \"900\"} 秒后再试\n </div>\n )}\n {loginError === \"captcha\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 请完成人机验证后再试\n </div>\n )}\n {loginError === \"invalid\" && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n 邮箱或密码错误\n </div>\n )}\n {loginError === \"unverified\" && (\n <div className=\"rounded-md bg-amber-500/10 px-3 py-2 text-sm text-amber-700 dark:text-amber-400\">\n 邮箱还没有验证,请先打开验证邮件中的链接。\n </div>\n )}\n {Turnstile && <Turnstile action=\"login\" />}\n <Button type=\"submit\" className=\"w-full\">使用邮箱登录</Button>\n </form>\n\n {loginError === \"unverified\" && email && resendVerificationAction && (\n <form action={resendVerificationAction as unknown as (formData: FormData) => void}>\n <input type=\"hidden\" name=\"email\" value={email} />\n {Turnstile && <Turnstile action=\"resend_verify\" />}\n <Button type=\"submit\" variant=\"outline\" size=\"sm\" className=\"w-full\">\n 重新发送验证邮件\n </Button>\n </form>\n )}\n\n <Button asChild variant=\"secondary\" className=\"w-full\">\n <a href=\"/register\">新用户注册</a>\n </Button>\n\n {user && (\n <p className=\"text-center text-xs text-muted-foreground\">\n 当前已登录:{user.email}\n </p>\n )}\n </CardContent>\n </Card>\n </main>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,17 @@
1
+ import * as react from 'react';
2
+ import { A as AuthPageContext } from '../../types-BsAcZSNX.js';
3
+
4
+ interface RegisterPageProps {
5
+ ui: AuthPageContext["ui"];
6
+ searchParams: {
7
+ error?: string;
8
+ };
9
+ registerAction: AuthPageContext["actions"] extends infer A ? A extends {
10
+ register: infer F;
11
+ } ? F : never : never;
12
+ isAuthenticated?: AuthPageContext["isAuthenticated"];
13
+ redirectWhenAuthenticated?: string;
14
+ }
15
+ declare function RegisterPage({ ui, searchParams, registerAction, isAuthenticated, redirectWhenAuthenticated, }: RegisterPageProps): Promise<react.JSX.Element>;
16
+
17
+ export { type RegisterPageProps, RegisterPage as default };
@@ -0,0 +1,81 @@
1
+ // src/auth/auth-pages/register.tsx
2
+ import { redirect } from "next/navigation";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ async function RegisterPage({
5
+ ui,
6
+ searchParams,
7
+ registerAction,
8
+ isAuthenticated,
9
+ redirectWhenAuthenticated = "/admin"
10
+ }) {
11
+ if (isAuthenticated && await isAuthenticated()) {
12
+ redirect(redirectWhenAuthenticated);
13
+ }
14
+ const { error } = searchParams;
15
+ const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent, Turnstile } = ui;
16
+ return /* @__PURE__ */ jsx(
17
+ "main",
18
+ {
19
+ style: {
20
+ minHeight: "100vh",
21
+ display: "flex",
22
+ alignItems: "center",
23
+ justifyContent: "center",
24
+ padding: "1rem",
25
+ background: "linear-gradient(to bottom right, var(--background), var(--muted))"
26
+ },
27
+ children: /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-sm", children: [
28
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
29
+ /* @__PURE__ */ jsx(CardTitle, { children: "\u90AE\u7BB1\u6CE8\u518C" }),
30
+ /* @__PURE__ */ jsx(CardDescription, { children: "\u6CE8\u518C\u540E\u4F1A\u53D1\u9001\u9A8C\u8BC1\u90AE\u4EF6\uFF0C\u9A8C\u8BC1\u5B8C\u6210\u5373\u53EF\u8FDB\u5165\u540E\u53F0\u3002" })
31
+ ] }),
32
+ /* @__PURE__ */ jsxs(CardContent, { children: [
33
+ /* @__PURE__ */ jsxs("form", { action: registerAction, children: [
34
+ /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: "\u90AE\u7BB1" }),
35
+ /* @__PURE__ */ jsx(
36
+ Input,
37
+ {
38
+ id: "email",
39
+ name: "email",
40
+ type: "email",
41
+ placeholder: "you@example.com",
42
+ autoFocus: true,
43
+ required: true
44
+ }
45
+ ),
46
+ /* @__PURE__ */ jsx(Label, { htmlFor: "password", children: "\u5BC6\u7801" }),
47
+ /* @__PURE__ */ jsx(
48
+ Input,
49
+ {
50
+ id: "password",
51
+ name: "password",
52
+ type: "password",
53
+ placeholder: "\u81F3\u5C11 8 \u4F4D\uFF0C\u5305\u542B\u5B57\u6BCD\u548C\u6570\u5B57",
54
+ required: true
55
+ }
56
+ ),
57
+ /* @__PURE__ */ jsx(Label, { htmlFor: "confirmPassword", children: "\u786E\u8BA4\u5BC6\u7801" }),
58
+ /* @__PURE__ */ jsx(
59
+ Input,
60
+ {
61
+ id: "confirmPassword",
62
+ name: "confirmPassword",
63
+ type: "password",
64
+ placeholder: "\u518D\u6B21\u8F93\u5165\u5BC6\u7801",
65
+ required: true
66
+ }
67
+ ),
68
+ error && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error }),
69
+ Turnstile && /* @__PURE__ */ jsx(Turnstile, { action: "register" }),
70
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "w-full", children: "\u6CE8\u518C\u5E76\u53D1\u9001\u9A8C\u8BC1\u90AE\u4EF6" })
71
+ ] }),
72
+ /* @__PURE__ */ jsx(Button, { asChild: true, variant: "ghost", className: "w-full", children: /* @__PURE__ */ jsx("a", { href: "/login", children: "\u8FD4\u56DE\u767B\u5F55" }) })
73
+ ] })
74
+ ] })
75
+ }
76
+ );
77
+ }
78
+ export {
79
+ RegisterPage as default
80
+ };
81
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/auth/auth-pages/register.tsx"],"sourcesContent":["// auth/auth-pages/register.tsx\n\nimport { redirect } from \"next/navigation\";\nimport type { AuthPageContext } from \"./types\";\n\nexport interface RegisterPageProps {\n ui: AuthPageContext[\"ui\"];\n searchParams: { error?: string };\n registerAction: AuthPageContext[\"actions\"] extends infer A\n ? A extends { register: infer F }\n ? F\n : never\n : never;\n isAuthenticated?: AuthPageContext[\"isAuthenticated\"];\n redirectWhenAuthenticated?: string;\n}\n\nexport default async function RegisterPage({\n ui,\n searchParams,\n registerAction,\n isAuthenticated,\n redirectWhenAuthenticated = \"/admin\",\n}: RegisterPageProps) {\n if (isAuthenticated && (await isAuthenticated())) {\n redirect(redirectWhenAuthenticated);\n }\n const { error } = searchParams;\n const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent, Turnstile } = ui;\n\n return (\n <main\n style={{\n minHeight: \"100vh\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n background: \"linear-gradient(to bottom right, var(--background), var(--muted))\",\n }}\n >\n <Card className=\"w-full max-w-sm\">\n <CardHeader>\n <CardTitle>邮箱注册</CardTitle>\n <CardDescription>\n 注册后会发送验证邮件,验证完成即可进入后台。\n </CardDescription>\n </CardHeader>\n <CardContent>\n <form action={registerAction as unknown as (formData: FormData) => void}>\n <Label htmlFor=\"email\">邮箱</Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n autoFocus\n required\n />\n <Label htmlFor=\"password\">密码</Label>\n <Input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n placeholder=\"至少 8 位,包含字母和数字\"\n required\n />\n <Label htmlFor=\"confirmPassword\">确认密码</Label>\n <Input\n id=\"confirmPassword\"\n name=\"confirmPassword\"\n type=\"password\"\n placeholder=\"再次输入密码\"\n required\n />\n {error && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n {error}\n </div>\n )}\n {Turnstile && <Turnstile action=\"register\" />}\n <Button type=\"submit\" className=\"w-full\">注册并发送验证邮件</Button>\n </form>\n\n <Button asChild variant=\"ghost\" className=\"w-full\">\n <a href=\"/login\">返回登录</a>\n </Button>\n </CardContent>\n </Card>\n </main>\n );\n}\n"],"mappings":";AAEA,SAAS,gBAAgB;AAwCjB,SACE,KADF;AAzBR,eAAO,aAAoC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,4BAA4B;AAC9B,GAAsB;AACpB,MAAI,mBAAoB,MAAM,gBAAgB,GAAI;AAChD,aAAS,yBAAyB;AAAA,EACpC;AACA,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,EAAE,QAAQ,OAAO,OAAO,MAAM,YAAY,WAAW,iBAAiB,aAAa,UAAU,IAAI;AAEvG,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,WAAW;AAAA,QACX,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,YAAY;AAAA,MACd;AAAA,MAEA,+BAAC,QAAK,WAAU,mBACd;AAAA,6BAAC,cACC;AAAA,8BAAC,aAAU,sCAAI;AAAA,UACf,oBAAC,mBAAgB,kJAEjB;AAAA,WACF;AAAA,QACA,qBAAC,eACC;AAAA,+BAAC,UAAK,QAAQ,gBACZ;AAAA,gCAAC,SAAM,SAAQ,SAAQ,0BAAE;AAAA,YACzB;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,aAAY;AAAA,gBACZ,WAAS;AAAA,gBACT,UAAQ;AAAA;AAAA,YACV;AAAA,YACA,oBAAC,SAAM,SAAQ,YAAW,0BAAE;AAAA,YAC5B;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,aAAY;AAAA,gBACZ,UAAQ;AAAA;AAAA,YACV;AAAA,YACA,oBAAC,SAAM,SAAQ,mBAAkB,sCAAI;AAAA,YACrC;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,aAAY;AAAA,gBACZ,UAAQ;AAAA;AAAA,YACV;AAAA,YACC,SACC,oBAAC,SAAI,WAAU,mEACZ,iBACH;AAAA,YAED,aAAa,oBAAC,aAAU,QAAO,YAAW;AAAA,YAC3C,oBAAC,UAAO,MAAK,UAAS,WAAU,UAAS,oEAAS;AAAA,aACpD;AAAA,UAEA,oBAAC,UAAO,SAAO,MAAC,SAAQ,SAAQ,WAAU,UACxC,8BAAC,OAAE,MAAK,UAAS,sCAAI,GACvB;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -0,0 +1,18 @@
1
+ import * as react from 'react';
2
+ import { A as AuthPageContext } from '../../types-BsAcZSNX.js';
3
+
4
+ interface ResetPasswordPageProps {
5
+ ui: AuthPageContext["ui"];
6
+ searchParams: {
7
+ token?: string;
8
+ error?: string;
9
+ };
10
+ resetPasswordAction: AuthPageContext["actions"] extends infer A ? A extends {
11
+ resetPassword: infer F;
12
+ } ? F : never : never;
13
+ isAuthenticated?: AuthPageContext["isAuthenticated"];
14
+ redirectWhenAuthenticated?: string;
15
+ }
16
+ declare function ResetPasswordPage({ ui, searchParams, resetPasswordAction, isAuthenticated, redirectWhenAuthenticated, }: ResetPasswordPageProps): Promise<react.JSX.Element>;
17
+
18
+ export { type ResetPasswordPageProps, ResetPasswordPage as default };
@@ -0,0 +1,72 @@
1
+ // src/auth/auth-pages/reset-password.tsx
2
+ import { redirect } from "next/navigation";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ async function ResetPasswordPage({
5
+ ui,
6
+ searchParams,
7
+ resetPasswordAction,
8
+ isAuthenticated,
9
+ redirectWhenAuthenticated = "/admin"
10
+ }) {
11
+ if (isAuthenticated && await isAuthenticated()) {
12
+ redirect(redirectWhenAuthenticated);
13
+ }
14
+ const { token, error } = searchParams;
15
+ if (!token) {
16
+ redirect("/forgot-password?error=missing");
17
+ }
18
+ const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent } = ui;
19
+ return /* @__PURE__ */ jsx(
20
+ "main",
21
+ {
22
+ style: {
23
+ minHeight: "100vh",
24
+ display: "flex",
25
+ alignItems: "center",
26
+ justifyContent: "center",
27
+ padding: "1rem",
28
+ background: "linear-gradient(to bottom right, var(--background), var(--muted))"
29
+ },
30
+ children: /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-sm", children: [
31
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
32
+ /* @__PURE__ */ jsx(CardTitle, { children: "\u8BBE\u7F6E\u65B0\u5BC6\u7801" }),
33
+ /* @__PURE__ */ jsx(CardDescription, { children: "\u8BF7\u8F93\u5165\u65B0\u5BC6\u7801\uFF0C\u63D0\u4EA4\u540E\u5C06\u81EA\u52A8\u767B\u5F55\u3002" })
34
+ ] }),
35
+ /* @__PURE__ */ jsxs(CardContent, { children: [
36
+ error && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error }),
37
+ /* @__PURE__ */ jsxs("form", { action: resetPasswordAction, children: [
38
+ /* @__PURE__ */ jsx("input", { type: "hidden", name: "token", value: token }),
39
+ /* @__PURE__ */ jsx(Label, { htmlFor: "password", children: "\u65B0\u5BC6\u7801" }),
40
+ /* @__PURE__ */ jsx(
41
+ Input,
42
+ {
43
+ id: "password",
44
+ name: "password",
45
+ type: "password",
46
+ placeholder: "\u81F3\u5C11 8 \u4F4D\uFF0C\u5305\u542B\u5B57\u6BCD\u548C\u6570\u5B57",
47
+ required: true
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsx(Label, { htmlFor: "confirmPassword", children: "\u786E\u8BA4\u65B0\u5BC6\u7801" }),
51
+ /* @__PURE__ */ jsx(
52
+ Input,
53
+ {
54
+ id: "confirmPassword",
55
+ name: "confirmPassword",
56
+ type: "password",
57
+ placeholder: "\u518D\u6B21\u8F93\u5165\u65B0\u5BC6\u7801",
58
+ required: true
59
+ }
60
+ ),
61
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "w-full", children: "\u66F4\u65B0\u5BC6\u7801\u5E76\u767B\u5F55" })
62
+ ] }),
63
+ /* @__PURE__ */ jsx(Button, { asChild: true, variant: "ghost", className: "w-full", children: /* @__PURE__ */ jsx("a", { href: "/login", children: "\u8FD4\u56DE\u767B\u5F55" }) })
64
+ ] })
65
+ ] })
66
+ }
67
+ );
68
+ }
69
+ export {
70
+ ResetPasswordPage as default
71
+ };
72
+ //# sourceMappingURL=reset-password.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/auth/auth-pages/reset-password.tsx"],"sourcesContent":["// auth/auth-pages/reset-password.tsx\n\nimport { redirect } from \"next/navigation\";\nimport type { AuthPageContext } from \"./types\";\n\nexport interface ResetPasswordPageProps {\n ui: AuthPageContext[\"ui\"];\n searchParams: {\n token?: string;\n error?: string;\n };\n resetPasswordAction: AuthPageContext[\"actions\"] extends infer A\n ? A extends { resetPassword: infer F }\n ? F\n : never\n : never;\n isAuthenticated?: AuthPageContext[\"isAuthenticated\"];\n redirectWhenAuthenticated?: string;\n}\n\nexport default async function ResetPasswordPage({\n ui,\n searchParams,\n resetPasswordAction,\n isAuthenticated,\n redirectWhenAuthenticated = \"/admin\",\n}: ResetPasswordPageProps) {\n if (isAuthenticated && (await isAuthenticated())) {\n redirect(redirectWhenAuthenticated);\n }\n const { token, error } = searchParams;\n if (!token) {\n redirect(\"/forgot-password?error=missing\");\n }\n const { Button, Input, Label, Card, CardHeader, CardTitle, CardDescription, CardContent } = ui;\n\n return (\n <main\n style={{\n minHeight: \"100vh\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n background: \"linear-gradient(to bottom right, var(--background), var(--muted))\",\n }}\n >\n <Card className=\"w-full max-w-sm\">\n <CardHeader>\n <CardTitle>设置新密码</CardTitle>\n <CardDescription>请输入新密码,提交后将自动登录。</CardDescription>\n </CardHeader>\n <CardContent>\n {error && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n {error}\n </div>\n )}\n\n <form action={resetPasswordAction as unknown as (formData: FormData) => void}>\n <input type=\"hidden\" name=\"token\" value={token} />\n <Label htmlFor=\"password\">新密码</Label>\n <Input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n placeholder=\"至少 8 位,包含字母和数字\"\n required\n />\n <Label htmlFor=\"confirmPassword\">确认新密码</Label>\n <Input\n id=\"confirmPassword\"\n name=\"confirmPassword\"\n type=\"password\"\n placeholder=\"再次输入新密码\"\n required\n />\n <Button type=\"submit\" className=\"w-full\">更新密码并登录</Button>\n </form>\n\n <Button asChild variant=\"ghost\" className=\"w-full\">\n <a href=\"/login\">返回登录</a>\n </Button>\n </CardContent>\n </Card>\n </main>\n );\n}\n"],"mappings":";AAEA,SAAS,gBAAgB;AA8CjB,SACE,KADF;AA5BR,eAAO,kBAAyC;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,4BAA4B;AAC9B,GAA2B;AACzB,MAAI,mBAAoB,MAAM,gBAAgB,GAAI;AAChD,aAAS,yBAAyB;AAAA,EACpC;AACA,QAAM,EAAE,OAAO,MAAM,IAAI;AACzB,MAAI,CAAC,OAAO;AACV,aAAS,gCAAgC;AAAA,EAC3C;AACA,QAAM,EAAE,QAAQ,OAAO,OAAO,MAAM,YAAY,WAAW,iBAAiB,YAAY,IAAI;AAE5F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,WAAW;AAAA,QACX,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,YAAY;AAAA,MACd;AAAA,MAEA,+BAAC,QAAK,WAAU,mBACd;AAAA,6BAAC,cACC;AAAA,8BAAC,aAAU,4CAAK;AAAA,UAChB,oBAAC,mBAAgB,8GAAgB;AAAA,WACnC;AAAA,QACA,qBAAC,eACE;AAAA,mBACC,oBAAC,SAAI,WAAU,mEACZ,iBACH;AAAA,UAGF,qBAAC,UAAK,QAAQ,qBACZ;AAAA,gCAAC,WAAM,MAAK,UAAS,MAAK,SAAQ,OAAO,OAAO;AAAA,YAChD,oBAAC,SAAM,SAAQ,YAAW,gCAAG;AAAA,YAC7B;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,aAAY;AAAA,gBACZ,UAAQ;AAAA;AAAA,YACV;AAAA,YACA,oBAAC,SAAM,SAAQ,mBAAkB,4CAAK;AAAA,YACtC;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,aAAY;AAAA,gBACZ,UAAQ;AAAA;AAAA,YACV;AAAA,YACA,oBAAC,UAAO,MAAK,UAAS,WAAU,UAAS,wDAAO;AAAA,aAClD;AAAA,UAEA,oBAAC,UAAO,SAAO,MAAC,SAAQ,SAAQ,WAAU,UACxC,8BAAC,OAAE,MAAK,UAAS,sCAAI,GACvB;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -0,0 +1,72 @@
1
+ import { AuthConfig } from '../types.js';
2
+ export { SessionUser } from './session.js';
3
+ export { ADMIN_COOKIE, AuthViewer, USER_COOKIE, checkPassword, clearSessionCookie, clearUserSessionCookie, getAuthViewer, getCurrentUser, isAuthenticated, setSessionCookie, setUserSessionCookie, signUserToken, verifyUserToken } from './user-session.js';
4
+ export { hashPassword, validatePasswordStrength, verifyPassword } from './passwords.js';
5
+ export { User, UserListItem, UserRole, authenticateEmailUser, changeUserPassword, createEmailUser, deleteUserAccount, getUserByEmail, getUserById, issuePasswordResetToken, issueVerificationToken, listUsers, listUsersWithPostCounts, normalizeUserRole, resetPasswordWithToken, revokeUserSessions, setUserRole, upsertGoogleUser, userToSession, verifyEmailUser } from './users.js';
6
+ export { AuthRateLimitKind, checkAuthRateLimit, clearAuthRateLimit, clearAuthRateLimits, enforceAuthRateLimits, recordAuthFailure, recordAuthFailures } from './rate-limit.js';
7
+ export { TurnstileRuntimeConfig, getTurnstileRuntimeConfig, verifyTurnstileFromForm, verifyTurnstileToken } from './turnstile.js';
8
+ import '../content/models.js';
9
+ import '../notion/types.js';
10
+ import '../platform/current.js';
11
+ import '../platform/runtime.js';
12
+ import '../env-C5qu-0R-.js';
13
+ import '../platform/selection.js';
14
+
15
+ /**
16
+ * Minimal user shape. Expanded in Task 3.2 to mirror the full D1 row.
17
+ */
18
+ interface AuthUser {
19
+ id: number;
20
+ email: string;
21
+ role: string | null;
22
+ }
23
+ /**
24
+ * Minimal viewer shape returned by `requireViewer` / `requireRole`.
25
+ * Expanded in Task 3.2 to include session info and role flags.
26
+ */
27
+ interface AuthViewer {
28
+ id: number;
29
+ email: string;
30
+ role: string;
31
+ isAdmin: boolean;
32
+ isVip: boolean;
33
+ }
34
+ /**
35
+ * Rate-limit verdict returned by `checkRateLimit`.
36
+ */
37
+ type AuthRateLimitResult = {
38
+ ok: true;
39
+ } | {
40
+ ok: false;
41
+ retryAfterSec: number;
42
+ scope: "email" | "ip";
43
+ };
44
+ /**
45
+ * Aggregate auth surface returned by `createAuth`. Each method is bound
46
+ * to the configured database and cookie settings; consumers should not
47
+ * need to pass these in again.
48
+ */
49
+ interface Auth {
50
+ requireViewer(request: Request): Promise<{
51
+ user: AuthViewer;
52
+ }>;
53
+ requireRole(request: Request, role: string): Promise<{
54
+ user: AuthViewer;
55
+ }>;
56
+ listUsers(): Promise<AuthUser[]>;
57
+ setUserRole(userId: number, role: string): Promise<void>;
58
+ checkRateLimit(key: string, limit: number, windowMs: number): Promise<AuthRateLimitResult>;
59
+ verifyTurnstile(token: string, ip: string | null): Promise<boolean>;
60
+ }
61
+ /**
62
+ * Build an `Auth` object bound to `config` and the current platform
63
+ * runtime. The returned methods share the same database/cookie context,
64
+ * removing the need for module-level singletons.
65
+ *
66
+ * Runtime/database validation is deferred to the first call so callers
67
+ * that only need the typed surface (e.g. tests) can still construct the
68
+ * factory in environments without a configured D1 binding.
69
+ */
70
+ declare function createAuth(config: AuthConfig): Auth;
71
+
72
+ export { type Auth, type AuthRateLimitResult, type AuthUser, createAuth };