dsh-plugin-image-workbench 0.0.0-stage → 1.1.6

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 (186) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +258 -0
  3. package/README.md +260 -2
  4. package/client.js +241 -0
  5. package/cordis.patch.yml +14 -0
  6. package/host.js +692 -0
  7. package/install.mjs +333 -0
  8. package/package.json +58 -4
  9. package/scripts/fetch-style-images.mjs +104 -0
  10. package/scripts/vendor-snapshot.py +154 -0
  11. package/server/access.py +26 -0
  12. package/server/app.py +1329 -0
  13. package/server/catalog.json +1 -0
  14. package/server/catalog.py +147 -0
  15. package/server/composer.py +391 -0
  16. package/server/drafts.py +158 -0
  17. package/server/history.py +575 -0
  18. package/server/imaging.py +172 -0
  19. package/server/pool.py +1335 -0
  20. package/server/pool_admin.py +921 -0
  21. package/server/pool_service.py +1073 -0
  22. package/server/requirements.txt +6 -0
  23. package/server/smoke_check.py +402 -0
  24. package/server/static/access.html +202 -0
  25. package/server/static/index.html +4725 -0
  26. package/vendor/chatgpt2api/README.md +13 -0
  27. package/vendor/chatgpt2api/SOURCE.json +11 -0
  28. package/vendor/chatgpt2api/runtime/uv.exe +0 -0
  29. package/vendor/chatgpt2api/source/.env.example +82 -0
  30. package/vendor/chatgpt2api/source/.python-version +1 -0
  31. package/vendor/chatgpt2api/source/LICENSE +21 -0
  32. package/vendor/chatgpt2api/source/README.md +391 -0
  33. package/vendor/chatgpt2api/source/VERSION +1 -0
  34. package/vendor/chatgpt2api/source/api/__init__.py +2 -0
  35. package/vendor/chatgpt2api/source/api/accounts.py +537 -0
  36. package/vendor/chatgpt2api/source/api/ai.py +217 -0
  37. package/vendor/chatgpt2api/source/api/app.py +62 -0
  38. package/vendor/chatgpt2api/source/api/errors.py +46 -0
  39. package/vendor/chatgpt2api/source/api/image_inputs.py +309 -0
  40. package/vendor/chatgpt2api/source/api/image_tasks.py +120 -0
  41. package/vendor/chatgpt2api/source/api/support.py +134 -0
  42. package/vendor/chatgpt2api/source/api/system.py +365 -0
  43. package/vendor/chatgpt2api/source/assets/atlascloud.svg +1 -0
  44. package/vendor/chatgpt2api/source/assets/ipwo.png +0 -0
  45. package/vendor/chatgpt2api/source/assets/kunrouai.svg +50 -0
  46. package/vendor/chatgpt2api/source/config.default.json +108 -0
  47. package/vendor/chatgpt2api/source/main.py +9 -0
  48. package/vendor/chatgpt2api/source/pyproject.toml +27 -0
  49. package/vendor/chatgpt2api/source/scripts/init_proxy_config.py +186 -0
  50. package/vendor/chatgpt2api/source/scripts/migrate_storage.py +163 -0
  51. package/vendor/chatgpt2api/source/scripts/privoxy-warp.conf +13 -0
  52. package/vendor/chatgpt2api/source/scripts/test_outlook_token_mailbox.py +385 -0
  53. package/vendor/chatgpt2api/source/scripts/test_storage.py +129 -0
  54. package/vendor/chatgpt2api/source/scripts/verify_oauth_refresh.py +92 -0
  55. package/vendor/chatgpt2api/source/services/__init__.py +0 -0
  56. package/vendor/chatgpt2api/source/services/account_service.py +1757 -0
  57. package/vendor/chatgpt2api/source/services/auth_service.py +253 -0
  58. package/vendor/chatgpt2api/source/services/backup_service.py +671 -0
  59. package/vendor/chatgpt2api/source/services/config.py +667 -0
  60. package/vendor/chatgpt2api/source/services/content_filter.py +242 -0
  61. package/vendor/chatgpt2api/source/services/cpa_service.py +315 -0
  62. package/vendor/chatgpt2api/source/services/editable_file_task_service.py +260 -0
  63. package/vendor/chatgpt2api/source/services/image_service.py +376 -0
  64. package/vendor/chatgpt2api/source/services/image_storage_service.py +405 -0
  65. package/vendor/chatgpt2api/source/services/image_tags_service.py +76 -0
  66. package/vendor/chatgpt2api/source/services/image_task_service.py +550 -0
  67. package/vendor/chatgpt2api/source/services/log_service.py +337 -0
  68. package/vendor/chatgpt2api/source/services/model_service.py +182 -0
  69. package/vendor/chatgpt2api/source/services/oauth_login_service.py +267 -0
  70. package/vendor/chatgpt2api/source/services/openai_backend_api.py +2763 -0
  71. package/vendor/chatgpt2api/source/services/openai_oauth.py +32 -0
  72. package/vendor/chatgpt2api/source/services/protocol/__init__.py +2 -0
  73. package/vendor/chatgpt2api/source/services/protocol/anthropic_v1_messages.py +307 -0
  74. package/vendor/chatgpt2api/source/services/protocol/chat_completion_cache.py +238 -0
  75. package/vendor/chatgpt2api/source/services/protocol/conversation.py +1644 -0
  76. package/vendor/chatgpt2api/source/services/protocol/error_response.py +124 -0
  77. package/vendor/chatgpt2api/source/services/protocol/openai_search.py +19 -0
  78. package/vendor/chatgpt2api/source/services/protocol/openai_v1_chat_complete.py +316 -0
  79. package/vendor/chatgpt2api/source/services/protocol/openai_v1_image_edit.py +86 -0
  80. package/vendor/chatgpt2api/source/services/protocol/openai_v1_image_generations.py +42 -0
  81. package/vendor/chatgpt2api/source/services/protocol/openai_v1_models.py +53 -0
  82. package/vendor/chatgpt2api/source/services/protocol/openai_v1_response.py +453 -0
  83. package/vendor/chatgpt2api/source/services/protocol/web_search_tool.py +160 -0
  84. package/vendor/chatgpt2api/source/services/proxy_service.py +648 -0
  85. package/vendor/chatgpt2api/source/services/storage/__init__.py +5 -0
  86. package/vendor/chatgpt2api/source/services/storage/base.py +38 -0
  87. package/vendor/chatgpt2api/source/services/storage/database_storage.py +194 -0
  88. package/vendor/chatgpt2api/source/services/storage/factory.py +104 -0
  89. package/vendor/chatgpt2api/source/services/storage/git_storage.py +183 -0
  90. package/vendor/chatgpt2api/source/services/storage/json_storage.py +95 -0
  91. package/vendor/chatgpt2api/source/services/sub2api_service.py +531 -0
  92. package/vendor/chatgpt2api/source/utils/__init__.py +0 -0
  93. package/vendor/chatgpt2api/source/utils/helper.py +469 -0
  94. package/vendor/chatgpt2api/source/utils/image_tokens.py +346 -0
  95. package/vendor/chatgpt2api/source/utils/log.py +110 -0
  96. package/vendor/chatgpt2api/source/utils/pkce.py +19 -0
  97. package/vendor/chatgpt2api/source/utils/pow.py +207 -0
  98. package/vendor/chatgpt2api/source/utils/sentinel.py +159 -0
  99. package/vendor/chatgpt2api/source/utils/turnstile.py +184 -0
  100. package/vendor/chatgpt2api/source/uv.lock +904 -0
  101. package/vendor/chatgpt2api/source/web/bun.lock +1501 -0
  102. package/vendor/chatgpt2api/source/web/components.json +24 -0
  103. package/vendor/chatgpt2api/source/web/eslint.config.mjs +17 -0
  104. package/vendor/chatgpt2api/source/web/next.config.ts +40 -0
  105. package/vendor/chatgpt2api/source/web/package.json +58 -0
  106. package/vendor/chatgpt2api/source/web/postcss.config.mjs +5 -0
  107. package/vendor/chatgpt2api/source/web/public/github.svg +1 -0
  108. package/vendor/chatgpt2api/source/web/public/next.svg +1 -0
  109. package/vendor/chatgpt2api/source/web/public/openai.svg +1 -0
  110. package/vendor/chatgpt2api/source/web/public/vercel.svg +1 -0
  111. package/vendor/chatgpt2api/source/web/src/app/accounts/components/account-import-dialog.tsx +925 -0
  112. package/vendor/chatgpt2api/source/web/src/app/accounts/page.tsx +1307 -0
  113. package/vendor/chatgpt2api/source/web/src/app/debug/components/chat-panel.tsx +224 -0
  114. package/vendor/chatgpt2api/source/web/src/app/debug/components/editable-file-panel.tsx +444 -0
  115. package/vendor/chatgpt2api/source/web/src/app/debug/components/ppt-panel.tsx +9 -0
  116. package/vendor/chatgpt2api/source/web/src/app/debug/components/psd-panel.tsx +9 -0
  117. package/vendor/chatgpt2api/source/web/src/app/debug/components/search-panel.tsx +172 -0
  118. package/vendor/chatgpt2api/source/web/src/app/debug/components/skill-panel.tsx +169 -0
  119. package/vendor/chatgpt2api/source/web/src/app/debug/components/types.ts +39 -0
  120. package/vendor/chatgpt2api/source/web/src/app/debug/page.tsx +59 -0
  121. package/vendor/chatgpt2api/source/web/src/app/favicon.ico +0 -0
  122. package/vendor/chatgpt2api/source/web/src/app/globals.css +231 -0
  123. package/vendor/chatgpt2api/source/web/src/app/image/components/image-composer.tsx +542 -0
  124. package/vendor/chatgpt2api/source/web/src/app/image/components/image-results.tsx +554 -0
  125. package/vendor/chatgpt2api/source/web/src/app/image/components/image-sidebar.tsx +176 -0
  126. package/vendor/chatgpt2api/source/web/src/app/image/page.tsx +1799 -0
  127. package/vendor/chatgpt2api/source/web/src/app/image-manager/page.tsx +732 -0
  128. package/vendor/chatgpt2api/source/web/src/app/layout.tsx +50 -0
  129. package/vendor/chatgpt2api/source/web/src/app/login/page.tsx +101 -0
  130. package/vendor/chatgpt2api/source/web/src/app/logs/page.tsx +356 -0
  131. package/vendor/chatgpt2api/source/web/src/app/page.tsx +30 -0
  132. package/vendor/chatgpt2api/source/web/src/app/settings/components/api-docs-card.tsx +341 -0
  133. package/vendor/chatgpt2api/source/web/src/app/settings/components/backup-settings-card.tsx +447 -0
  134. package/vendor/chatgpt2api/source/web/src/app/settings/components/base-url-card.tsx +71 -0
  135. package/vendor/chatgpt2api/source/web/src/app/settings/components/config-card.tsx +491 -0
  136. package/vendor/chatgpt2api/source/web/src/app/settings/components/cpa-pool-dialog.tsx +108 -0
  137. package/vendor/chatgpt2api/source/web/src/app/settings/components/cpa-pools-card.tsx +169 -0
  138. package/vendor/chatgpt2api/source/web/src/app/settings/components/import-browser-dialog.tsx +185 -0
  139. package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-runtime-card.tsx +379 -0
  140. package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-settings-card.tsx +127 -0
  141. package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-settings.tsx +200 -0
  142. package/vendor/chatgpt2api/source/web/src/app/settings/components/settings-header.tsx +12 -0
  143. package/vendor/chatgpt2api/source/web/src/app/settings/components/sub2api-connections.tsx +886 -0
  144. package/vendor/chatgpt2api/source/web/src/app/settings/components/third-party-apps-card.tsx +81 -0
  145. package/vendor/chatgpt2api/source/web/src/app/settings/components/user-keys-card.tsx +422 -0
  146. package/vendor/chatgpt2api/source/web/src/app/settings/page.tsx +135 -0
  147. package/vendor/chatgpt2api/source/web/src/app/settings/store.ts +1069 -0
  148. package/vendor/chatgpt2api/source/web/src/components/date-range-filter.tsx +49 -0
  149. package/vendor/chatgpt2api/source/web/src/components/header-actions.tsx +24 -0
  150. package/vendor/chatgpt2api/source/web/src/components/image-lightbox.tsx +434 -0
  151. package/vendor/chatgpt2api/source/web/src/components/image-thumbnail.tsx +46 -0
  152. package/vendor/chatgpt2api/source/web/src/components/theme-script.tsx +18 -0
  153. package/vendor/chatgpt2api/source/web/src/components/theme-toggle.tsx +14 -0
  154. package/vendor/chatgpt2api/source/web/src/components/top-nav.tsx +263 -0
  155. package/vendor/chatgpt2api/source/web/src/components/ui/animated-theme-toggler.tsx +256 -0
  156. package/vendor/chatgpt2api/source/web/src/components/ui/badge.tsx +41 -0
  157. package/vendor/chatgpt2api/source/web/src/components/ui/button.tsx +59 -0
  158. package/vendor/chatgpt2api/source/web/src/components/ui/calendar.tsx +46 -0
  159. package/vendor/chatgpt2api/source/web/src/components/ui/card.tsx +61 -0
  160. package/vendor/chatgpt2api/source/web/src/components/ui/checkbox.tsx +27 -0
  161. package/vendor/chatgpt2api/source/web/src/components/ui/dia-text-reveal.tsx +267 -0
  162. package/vendor/chatgpt2api/source/web/src/components/ui/dialog.tsx +132 -0
  163. package/vendor/chatgpt2api/source/web/src/components/ui/field.tsx +13 -0
  164. package/vendor/chatgpt2api/source/web/src/components/ui/input.tsx +19 -0
  165. package/vendor/chatgpt2api/source/web/src/components/ui/label.tsx +24 -0
  166. package/vendor/chatgpt2api/source/web/src/components/ui/morphing-text.tsx +149 -0
  167. package/vendor/chatgpt2api/source/web/src/components/ui/popover.tsx +38 -0
  168. package/vendor/chatgpt2api/source/web/src/components/ui/select.tsx +175 -0
  169. package/vendor/chatgpt2api/source/web/src/components/ui/sheet.tsx +143 -0
  170. package/vendor/chatgpt2api/source/web/src/components/ui/table.tsx +29 -0
  171. package/vendor/chatgpt2api/source/web/src/components/ui/tabs.tsx +91 -0
  172. package/vendor/chatgpt2api/source/web/src/components/ui/text-3d-flip.tsx +269 -0
  173. package/vendor/chatgpt2api/source/web/src/components/ui/textarea.tsx +18 -0
  174. package/vendor/chatgpt2api/source/web/src/components/version-release-dialog.tsx +127 -0
  175. package/vendor/chatgpt2api/source/web/src/constants/common-env.ts +6 -0
  176. package/vendor/chatgpt2api/source/web/src/hooks/use-version-check.ts +85 -0
  177. package/vendor/chatgpt2api/source/web/src/lib/api.ts +926 -0
  178. package/vendor/chatgpt2api/source/web/src/lib/auth-session.ts +26 -0
  179. package/vendor/chatgpt2api/source/web/src/lib/release.ts +25 -0
  180. package/vendor/chatgpt2api/source/web/src/lib/request.ts +93 -0
  181. package/vendor/chatgpt2api/source/web/src/lib/use-auth-guard.ts +89 -0
  182. package/vendor/chatgpt2api/source/web/src/lib/utils.ts +6 -0
  183. package/vendor/chatgpt2api/source/web/src/store/auth.ts +112 -0
  184. package/vendor/chatgpt2api/source/web/src/store/editable-file-history.ts +33 -0
  185. package/vendor/chatgpt2api/source/web/src/store/image-conversations.ts +318 -0
  186. package/vendor/chatgpt2api/source/web/tsconfig.json +42 -0
@@ -0,0 +1,26 @@
1
+ "use client";
2
+
3
+ import { login } from "@/lib/api";
4
+ import { clearStoredAuthSession, getStoredAuthSession, setStoredAuthSession, type StoredAuthSession } from "@/store/auth";
5
+
6
+ export async function getValidatedAuthSession(): Promise<StoredAuthSession | null> {
7
+ const storedSession = await getStoredAuthSession();
8
+ if (!storedSession) {
9
+ return null;
10
+ }
11
+
12
+ try {
13
+ const data = await login(storedSession.key);
14
+ const nextSession: StoredAuthSession = {
15
+ key: storedSession.key,
16
+ role: data.role,
17
+ subjectId: data.subject_id,
18
+ name: data.name,
19
+ };
20
+ await setStoredAuthSession(nextSession);
21
+ return nextSession;
22
+ } catch {
23
+ await clearStoredAuthSession();
24
+ return null;
25
+ }
26
+ }
@@ -0,0 +1,25 @@
1
+ export type ReleaseInfo = {
2
+ version: string;
3
+ date: string;
4
+ items: { type: string; content: string }[];
5
+ };
6
+
7
+ export function parseChangelog(content: string): ReleaseInfo[] {
8
+ return content
9
+ .split(/^## /m)
10
+ .slice(1)
11
+ .map((block) => {
12
+ const [title = "", ...lines] = block.trim().split("\n");
13
+ const [, version = title.trim(), date = ""] =
14
+ title.match(/^(.+?)(?:\s+-\s+(.+))?$/) || [];
15
+ return {
16
+ version: version.trim(),
17
+ date: date.trim(),
18
+ items: lines
19
+ .map((line) => line.trim().match(/^\+\s+\[(.+?)\]\s+(.+)$/))
20
+ .filter((match): match is RegExpMatchArray => Boolean(match))
21
+ .map((match) => ({ type: match[1], content: match[2] })),
22
+ };
23
+ })
24
+ .filter((release) => release.items.length);
25
+ }
@@ -0,0 +1,93 @@
1
+ import axios, {AxiosError, type AxiosRequestConfig} from "axios";
2
+
3
+ import webConfig from "@/constants/common-env";
4
+ import {clearStoredAuthSession, getStoredAuthKey} from "@/store/auth";
5
+
6
+ type RequestConfig = AxiosRequestConfig & {
7
+ redirectOnUnauthorized?: boolean;
8
+ };
9
+
10
+ type ErrorPayload = {
11
+ detail?: string | { error?: string | { message?: string } };
12
+ error?: string | { message?: string };
13
+ message?: string;
14
+ };
15
+
16
+ function errorMessageFromValue(value: unknown): string {
17
+ if (typeof value === "string") {
18
+ return value;
19
+ }
20
+ if (!value || typeof value !== "object") {
21
+ return "";
22
+ }
23
+
24
+ const item = value as { error?: unknown; message?: unknown };
25
+ if (typeof item.message === "string") {
26
+ return item.message;
27
+ }
28
+ return errorMessageFromValue(item.error);
29
+ }
30
+
31
+ export const request = axios.create({
32
+ baseURL: webConfig.apiUrl.replace(/\/$/, ""),
33
+ });
34
+
35
+ request.interceptors.request.use(async (config) => {
36
+ const nextConfig = {...config};
37
+ const authKey = await getStoredAuthKey();
38
+ const headers = {...(nextConfig.headers || {})} as Record<string, string>;
39
+ if (authKey && !headers.Authorization) {
40
+ headers.Authorization = `Bearer ${authKey}`;
41
+ }
42
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
43
+ // @ts-expect-error
44
+ nextConfig.headers = headers;
45
+ return nextConfig;
46
+ });
47
+
48
+ request.interceptors.response.use(
49
+ (response) => response,
50
+ async (error: AxiosError<ErrorPayload>) => {
51
+ const status = error.response?.status;
52
+ const shouldRedirect = (error.config as RequestConfig | undefined)?.redirectOnUnauthorized !== false;
53
+ if (status === 401 && shouldRedirect && typeof window !== "undefined") {
54
+ // Avoid redirect loop — only redirect if not already on /login
55
+ if (!window.location.pathname.startsWith("/login")) {
56
+ await clearStoredAuthSession();
57
+ window.location.replace("/login");
58
+ // Return a never-resolving promise to prevent further error handling
59
+ // while the browser navigates away
60
+ return new Promise(() => {});
61
+ }
62
+ }
63
+
64
+ const payload = error.response?.data;
65
+ const message =
66
+ errorMessageFromValue(payload?.detail) ||
67
+ errorMessageFromValue(payload?.error) ||
68
+ payload?.message ||
69
+ error.message ||
70
+ `请求失败 (${status || 500})`;
71
+ return Promise.reject(new Error(message));
72
+ },
73
+ );
74
+
75
+ type RequestOptions = {
76
+ method?: string;
77
+ body?: unknown;
78
+ headers?: Record<string, string>;
79
+ redirectOnUnauthorized?: boolean;
80
+ };
81
+
82
+ export async function httpRequest<T>(path: string, options: RequestOptions = {}) {
83
+ const {method = "GET", body, headers, redirectOnUnauthorized = true} = options;
84
+ const config: RequestConfig = {
85
+ url: path,
86
+ method,
87
+ data: body,
88
+ headers,
89
+ redirectOnUnauthorized,
90
+ };
91
+ const response = await request.request<T>(config);
92
+ return response.data;
93
+ }
@@ -0,0 +1,89 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { useRouter } from "next/navigation";
5
+
6
+ import { getValidatedAuthSession } from "@/lib/auth-session";
7
+ import {
8
+ getDefaultRouteForRole,
9
+ type AuthRole,
10
+ type StoredAuthSession,
11
+ } from "@/store/auth";
12
+
13
+ type UseAuthGuardResult = {
14
+ isCheckingAuth: boolean;
15
+ session: StoredAuthSession | null;
16
+ };
17
+
18
+ export function useAuthGuard(allowedRoles?: AuthRole[]): UseAuthGuardResult {
19
+ const router = useRouter();
20
+ const [session, setSession] = useState<StoredAuthSession | null>(null);
21
+ const [isCheckingAuth, setIsCheckingAuth] = useState(true);
22
+ const allowedRolesKey = (allowedRoles || []).join(",");
23
+
24
+ useEffect(() => {
25
+ let active = true;
26
+
27
+ const load = async () => {
28
+ const roleList = allowedRolesKey ? (allowedRolesKey.split(",") as AuthRole[]) : [];
29
+ const storedSession = await getValidatedAuthSession();
30
+ if (!active) {
31
+ return;
32
+ }
33
+
34
+ if (!storedSession) {
35
+ setSession(null);
36
+ setIsCheckingAuth(false);
37
+ router.replace("/login");
38
+ return;
39
+ }
40
+
41
+ if (roleList.length > 0 && !roleList.includes(storedSession.role)) {
42
+ setSession(storedSession);
43
+ setIsCheckingAuth(false);
44
+ router.replace(getDefaultRouteForRole(storedSession.role));
45
+ return;
46
+ }
47
+
48
+ setSession(storedSession);
49
+ setIsCheckingAuth(false);
50
+ };
51
+
52
+ void load();
53
+ return () => {
54
+ active = false;
55
+ };
56
+ }, [allowedRolesKey, router]);
57
+
58
+ return { isCheckingAuth, session };
59
+ }
60
+
61
+ export function useRedirectIfAuthenticated() {
62
+ const router = useRouter();
63
+ const [isCheckingAuth, setIsCheckingAuth] = useState(true);
64
+
65
+ useEffect(() => {
66
+ let active = true;
67
+
68
+ const load = async () => {
69
+ const storedSession = await getValidatedAuthSession();
70
+ if (!active) {
71
+ return;
72
+ }
73
+
74
+ if (storedSession) {
75
+ router.replace(getDefaultRouteForRole(storedSession.role));
76
+ return;
77
+ }
78
+
79
+ setIsCheckingAuth(false);
80
+ };
81
+
82
+ void load();
83
+ return () => {
84
+ active = false;
85
+ };
86
+ }, [router]);
87
+
88
+ return { isCheckingAuth };
89
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,112 @@
1
+ "use client";
2
+
3
+ import localforage from "localforage";
4
+
5
+ export type AuthRole = "admin" | "user";
6
+
7
+ export type StoredAuthSession = {
8
+ key: string;
9
+ role: AuthRole;
10
+ subjectId: string;
11
+ name: string;
12
+ };
13
+
14
+ export const AUTH_KEY_STORAGE_KEY = "chatgpt2api_auth_key";
15
+ export const AUTH_SESSION_STORAGE_KEY = "chatgpt2api_auth_session";
16
+
17
+ const authStorage = localforage.createInstance({
18
+ name: "chatgpt2api",
19
+ storeName: "auth",
20
+ });
21
+
22
+ function normalizeSession(value: unknown, fallbackKey = ""): StoredAuthSession | null {
23
+ if (!value || typeof value !== "object") {
24
+ return null;
25
+ }
26
+
27
+ const candidate = value as Partial<StoredAuthSession>;
28
+ const key = String(candidate.key || fallbackKey || "").trim();
29
+ const role = candidate.role === "admin" || candidate.role === "user" ? candidate.role : null;
30
+ if (!key || !role) {
31
+ return null;
32
+ }
33
+
34
+ return {
35
+ key,
36
+ role,
37
+ subjectId: String(candidate.subjectId || "").trim(),
38
+ name: String(candidate.name || "").trim(),
39
+ };
40
+ }
41
+
42
+ export function getDefaultRouteForRole(role: AuthRole) {
43
+ return role === "admin" ? "/accounts" : "/image";
44
+ }
45
+
46
+ export async function getStoredAuthKey() {
47
+ if (typeof window === "undefined") {
48
+ return "";
49
+ }
50
+ const value = await authStorage.getItem<string>(AUTH_KEY_STORAGE_KEY);
51
+ return String(value || "").trim();
52
+ }
53
+
54
+ export async function getStoredAuthSession() {
55
+ if (typeof window === "undefined") {
56
+ return null;
57
+ }
58
+
59
+ const [storedKey, storedSession] = await Promise.all([
60
+ authStorage.getItem<string>(AUTH_KEY_STORAGE_KEY),
61
+ authStorage.getItem<StoredAuthSession>(AUTH_SESSION_STORAGE_KEY),
62
+ ]);
63
+
64
+ const normalizedSession = normalizeSession(storedSession, String(storedKey || ""));
65
+ if (normalizedSession) {
66
+ if (normalizedSession.key !== String(storedKey || "").trim()) {
67
+ await authStorage.setItem(AUTH_KEY_STORAGE_KEY, normalizedSession.key);
68
+ }
69
+ return normalizedSession;
70
+ }
71
+
72
+ if (String(storedKey || "").trim()) {
73
+ await clearStoredAuthSession();
74
+ }
75
+ return null;
76
+ }
77
+
78
+ export async function setStoredAuthSession(session: StoredAuthSession) {
79
+ const normalizedSession = normalizeSession(session);
80
+ if (!normalizedSession) {
81
+ await clearStoredAuthSession();
82
+ return;
83
+ }
84
+
85
+ await Promise.all([
86
+ authStorage.setItem(AUTH_KEY_STORAGE_KEY, normalizedSession.key),
87
+ authStorage.setItem(AUTH_SESSION_STORAGE_KEY, normalizedSession),
88
+ ]);
89
+ }
90
+
91
+ export async function setStoredAuthKey(authKey: string) {
92
+ const normalizedAuthKey = String(authKey || "").trim();
93
+ if (!normalizedAuthKey) {
94
+ await clearStoredAuthSession();
95
+ return;
96
+ }
97
+ await authStorage.setItem(AUTH_KEY_STORAGE_KEY, normalizedAuthKey);
98
+ }
99
+
100
+ export async function clearStoredAuthSession() {
101
+ if (typeof window === "undefined") {
102
+ return;
103
+ }
104
+ await Promise.all([
105
+ authStorage.removeItem(AUTH_KEY_STORAGE_KEY),
106
+ authStorage.removeItem(AUTH_SESSION_STORAGE_KEY),
107
+ ]);
108
+ }
109
+
110
+ export async function clearStoredAuthKey() {
111
+ await clearStoredAuthSession();
112
+ }
@@ -0,0 +1,33 @@
1
+ "use client";
2
+
3
+ import localforage from "localforage";
4
+
5
+ export type EditableFileDraft = {
6
+ prompt?: string;
7
+ images?: string[];
8
+ title?: string;
9
+ };
10
+
11
+ const editableFileStorage = localforage.createInstance({
12
+ name: "chatgpt2api",
13
+ storeName: "editable_file_history",
14
+ });
15
+
16
+ const draftsKey = (kind: string) => `drafts:${kind}`;
17
+ const deletedKey = (kind: string) => `deleted:${kind}`;
18
+
19
+ export async function listEditableFileDrafts(kind: string): Promise<Record<string, EditableFileDraft>> {
20
+ return (await editableFileStorage.getItem<Record<string, EditableFileDraft>>(draftsKey(kind))) || {};
21
+ }
22
+
23
+ export async function saveEditableFileDrafts(kind: string, drafts: Record<string, EditableFileDraft>): Promise<void> {
24
+ await editableFileStorage.setItem(draftsKey(kind), drafts);
25
+ }
26
+
27
+ export async function listDeletedEditableFileIds(kind: string): Promise<Set<string>> {
28
+ return new Set((await editableFileStorage.getItem<string[]>(deletedKey(kind))) || []);
29
+ }
30
+
31
+ export async function saveDeletedEditableFileIds(kind: string, ids: Set<string>): Promise<void> {
32
+ await editableFileStorage.setItem(deletedKey(kind), [...ids]);
33
+ }