inicontent 1.0.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 (205) hide show
  1. package/.github/pull_request.yml +18 -0
  2. package/CONTRIBUTING.md +67 -0
  3. package/LICENSE +21 -0
  4. package/README.md +142 -0
  5. package/app/app.vue +106 -0
  6. package/app/assets/main.css +974 -0
  7. package/app/components/Api/EndpointCard.vue +169 -0
  8. package/app/components/Api/SchemaDocsTable.vue +208 -0
  9. package/app/components/Asset/Card.vue +1363 -0
  10. package/app/components/Asset/Grid.vue +295 -0
  11. package/app/components/Asset/Icon.vue +38 -0
  12. package/app/components/Asset/Preview.vue +486 -0
  13. package/app/components/Asset/Scanner.vue +700 -0
  14. package/app/components/Asset/Thumb.vue +708 -0
  15. package/app/components/Auth/Reset.vue +99 -0
  16. package/app/components/Auth/index.vue +346 -0
  17. package/app/components/ChatInterface.vue +2326 -0
  18. package/app/components/ChatPagePreview.vue +34 -0
  19. package/app/components/Column/Array.vue +29 -0
  20. package/app/components/Column/Asset.vue +21 -0
  21. package/app/components/Column/Boolean.vue +12 -0
  22. package/app/components/Column/Color.vue +9 -0
  23. package/app/components/Column/Date.vue +57 -0
  24. package/app/components/Column/Edit.vue +215 -0
  25. package/app/components/Column/Email.vue +9 -0
  26. package/app/components/Column/Html.vue +27 -0
  27. package/app/components/Column/Id.vue +20 -0
  28. package/app/components/Column/Locale.vue +32 -0
  29. package/app/components/Column/Object.vue +18 -0
  30. package/app/components/Column/Password.vue +7 -0
  31. package/app/components/Column/Role.vue +15 -0
  32. package/app/components/Column/S.vue +14 -0
  33. package/app/components/Column/Table/Single.vue +49 -0
  34. package/app/components/Column/Table/index.vue +42 -0
  35. package/app/components/Column/Tags.vue +31 -0
  36. package/app/components/Column/Text.vue +9 -0
  37. package/app/components/Column/Url.vue +9 -0
  38. package/app/components/Column/index.vue +41 -0
  39. package/app/components/Dashboard/Editor.vue +221 -0
  40. package/app/components/Dashboard/Grid.vue +270 -0
  41. package/app/components/Dashboard/View.vue +131 -0
  42. package/app/components/Dashboard/Widget/BarChart.vue +73 -0
  43. package/app/components/Dashboard/Widget/Counter.vue +51 -0
  44. package/app/components/Dashboard/Widget/LineChart.vue +76 -0
  45. package/app/components/Dashboard/Widget/PieChart.vue +75 -0
  46. package/app/components/Dashboard/Widget/Table.vue +94 -0
  47. package/app/components/Dashboard/WidgetEditor.vue +274 -0
  48. package/app/components/Data/Array.vue +102 -0
  49. package/app/components/Data/Html.vue +9 -0
  50. package/app/components/Data/Icon.vue +9 -0
  51. package/app/components/Data/Object.vue +10 -0
  52. package/app/components/Data/S.vue +16 -0
  53. package/app/components/Data/Table.vue +53 -0
  54. package/app/components/Data/Value.vue +34 -0
  55. package/app/components/Data/index.vue +14 -0
  56. package/app/components/Field/Array.vue +324 -0
  57. package/app/components/Field/Asset/Actions.vue +97 -0
  58. package/app/components/Field/Asset/index.vue +639 -0
  59. package/app/components/Field/Boolean.vue +26 -0
  60. package/app/components/Field/Checkbox.vue +48 -0
  61. package/app/components/Field/Color.vue +27 -0
  62. package/app/components/Field/Date.vue +30 -0
  63. package/app/components/Field/Email.vue +51 -0
  64. package/app/components/Field/EmailTemplate.vue +420 -0
  65. package/app/components/Field/Html/ColorPicker.vue +127 -0
  66. package/app/components/Field/Html/Editor.vue +544 -0
  67. package/app/components/Field/Html/index.vue +25 -0
  68. package/app/components/Field/Icon.vue +114 -0
  69. package/app/components/Field/JSON.vue +113 -0
  70. package/app/components/Field/Mention.vue +36 -0
  71. package/app/components/Field/Number.vue +27 -0
  72. package/app/components/Field/Object.vue +92 -0
  73. package/app/components/Field/Password.vue +55 -0
  74. package/app/components/Field/Radio.vue +47 -0
  75. package/app/components/Field/S.vue +44 -0
  76. package/app/components/Field/Select.vue +49 -0
  77. package/app/components/Field/Table.vue +348 -0
  78. package/app/components/Field/Tags.vue +113 -0
  79. package/app/components/Field/Text.vue +31 -0
  80. package/app/components/Field/Textarea.vue +33 -0
  81. package/app/components/Field/Url.vue +40 -0
  82. package/app/components/Field/Wrapper.vue +128 -0
  83. package/app/components/Field/index.vue +68 -0
  84. package/app/components/FloatingChatbot.vue +73 -0
  85. package/app/components/Form/Card.vue +151 -0
  86. package/app/components/Form/Drawer.vue +195 -0
  87. package/app/components/Form/index.vue +621 -0
  88. package/app/components/Header.vue +328 -0
  89. package/app/components/Offline/ConflictModal.vue +200 -0
  90. package/app/components/Offline/SyncStatus.vue +249 -0
  91. package/app/components/PlatformFeatureLanding.vue +65 -0
  92. package/app/components/Table/Backups.vue +638 -0
  93. package/app/components/Table/Flows.vue +814 -0
  94. package/app/components/Table/Grid.vue +296 -0
  95. package/app/components/Table/Icon.vue +36 -0
  96. package/app/components/Table/Logs.vue +181 -0
  97. package/app/components/Table/Schedules.vue +722 -0
  98. package/app/components/Table/Search/Button.vue +256 -0
  99. package/app/components/Table/Search/Items.vue +286 -0
  100. package/app/components/Table/Search/index.vue +87 -0
  101. package/app/components/Table/Settings/Schema.vue +801 -0
  102. package/app/components/Table/Settings/index.vue +631 -0
  103. package/app/components/Table/TranslateDrawer.vue +526 -0
  104. package/app/components/Table/Views/Kanban.vue +289 -0
  105. package/app/components/Table/Views/Table.vue +1273 -0
  106. package/app/components/Table/index.vue +890 -0
  107. package/app/composables/Translation/define.ts +41 -0
  108. package/app/composables/Translation/fetch.ts +107 -0
  109. package/app/composables/Translation/languages.ts +1 -0
  110. package/app/composables/Translation/loadCoreTranslations.ts +38 -0
  111. package/app/composables/Translation/translate.ts +250 -0
  112. package/app/composables/databaseCookies.ts +79 -0
  113. package/app/composables/fieldValidator.ts +72 -0
  114. package/app/composables/fieldsList.ts +301 -0
  115. package/app/composables/formatDatabase.ts +101 -0
  116. package/app/composables/index.ts +444 -0
  117. package/app/composables/openDrawer.ts +118 -0
  118. package/app/composables/optimizeFile.ts +287 -0
  119. package/app/composables/renderLabel.ts +88 -0
  120. package/app/composables/schemaClipboard.ts +95 -0
  121. package/app/composables/search.ts +353 -0
  122. package/app/composables/setThemeConfig.ts +48 -0
  123. package/app/composables/translationValue.ts +169 -0
  124. package/app/composables/useAssetPreview.ts +84 -0
  125. package/app/composables/useAssetUploader.ts +56 -0
  126. package/app/composables/useDashboardData.ts +310 -0
  127. package/app/composables/useDocumentThumbnail.ts +351 -0
  128. package/app/composables/useEcharts.ts +33 -0
  129. package/app/composables/useLocalDatabaseConfig.ts +51 -0
  130. package/app/composables/useOcr.ts +300 -0
  131. package/app/composables/useOfflineFetch.ts +194 -0
  132. package/app/composables/useOfflineItem.ts +107 -0
  133. package/app/composables/useOfflineQueue.ts +302 -0
  134. package/app/composables/useOfflineSync.ts +235 -0
  135. package/app/composables/usePasskeyAuth.ts +156 -0
  136. package/app/composables/usePdfCompressor.ts +180 -0
  137. package/app/composables/usePlatformRedirect.ts +72 -0
  138. package/app/composables/useRealtimeSync.ts +233 -0
  139. package/app/composables/useReferencedItems.ts +180 -0
  140. package/app/composables/useScanner.ts +664 -0
  141. package/app/composables/useSubscription.ts +650 -0
  142. package/app/composables/useThumbnailCache.ts +136 -0
  143. package/app/composables/useVideoCompressor.ts +194 -0
  144. package/app/error.vue +112 -0
  145. package/app/layouts/api.vue +188 -0
  146. package/app/layouts/dashboard.vue +91 -0
  147. package/app/layouts/default.vue +228 -0
  148. package/app/layouts/table.vue +471 -0
  149. package/app/locales/ar.ts +996 -0
  150. package/app/locales/en.ts +655 -0
  151. package/app/locales/es.ts +996 -0
  152. package/app/locales/fr.ts +997 -0
  153. package/app/middleware/dashboard.ts +33 -0
  154. package/app/middleware/database.ts +83 -0
  155. package/app/middleware/global.ts +1 -0
  156. package/app/middleware/sid-ingest.global.ts +29 -0
  157. package/app/middleware/table.ts +54 -0
  158. package/app/middleware/user.ts +26 -0
  159. package/app/pages/[[database]]/admin/api/auth/index.vue +306 -0
  160. package/app/pages/[[database]]/admin/api/index.vue +18 -0
  161. package/app/pages/[[database]]/admin/api/tables/[table]/index.vue +497 -0
  162. package/app/pages/[[database]]/admin/api/tables/index.vue +87 -0
  163. package/app/pages/[[database]]/admin/billing/index.vue +16 -0
  164. package/app/pages/[[database]]/admin/dashboards/[id]/edit.vue +66 -0
  165. package/app/pages/[[database]]/admin/dashboards/[id]/index.vue +117 -0
  166. package/app/pages/[[database]]/admin/dashboards/index.vue +20 -0
  167. package/app/pages/[[database]]/admin/index.vue +28 -0
  168. package/app/pages/[[database]]/admin/settings.vue +526 -0
  169. package/app/pages/[[database]]/admin/tables/[table]/[id]/edit.vue +143 -0
  170. package/app/pages/[[database]]/admin/tables/[table]/[id]/index.vue +146 -0
  171. package/app/pages/[[database]]/admin/tables/[table]/flows.vue +10 -0
  172. package/app/pages/[[database]]/admin/tables/[table]/index.vue +10 -0
  173. package/app/pages/[[database]]/admin/tables/[table]/new.vue +12 -0
  174. package/app/pages/[[database]]/admin/tables/[table]/schedules.vue +10 -0
  175. package/app/pages/[[database]]/admin/tables/[table]/settings.vue +30 -0
  176. package/app/pages/[[database]]/admin/tables/assets/[...path].vue +24 -0
  177. package/app/pages/[[database]]/admin/tables/assets/flows.vue +10 -0
  178. package/app/pages/[[database]]/admin/tables/backups/index.vue +10 -0
  179. package/app/pages/[[database]]/admin/tables/blocks/index.vue +16 -0
  180. package/app/pages/[[database]]/admin/tables/index.vue +20 -0
  181. package/app/pages/[[database]]/admin/tables/pages/index.vue +16 -0
  182. package/app/pages/[[database]]/admin/tables/templates/index.vue +16 -0
  183. package/app/pages/[[database]]/api/index.vue +120 -0
  184. package/app/pages/[[database]]/auth/index.vue +12 -0
  185. package/app/pages/[[database]]/auth/reset.vue +10 -0
  186. package/app/pages/[[database]]/index.vue +10 -0
  187. package/app/plugins/offline-warmup.client.ts +53 -0
  188. package/app/plugins/template-globals.ts +9 -0
  189. package/app/spa-loading-template.html +93 -0
  190. package/biome.json +53 -0
  191. package/index.d.ts +380 -0
  192. package/modules/naiveui.ts +41 -0
  193. package/nuxt.config.ts +269 -0
  194. package/package.json +56 -0
  195. package/pnpm-workspace.yaml +5 -0
  196. package/public/apple-touch-icon.png +0 -0
  197. package/public/favicon-16x16.png +0 -0
  198. package/public/favicon-32x32.png +0 -0
  199. package/public/favicon.ico +0 -0
  200. package/public/pwa-192x192.png +0 -0
  201. package/public/pwa-512x512.png +0 -0
  202. package/public/pwa-maskable-192x192.png +0 -0
  203. package/public/pwa-maskable-512x512.png +0 -0
  204. package/public/vendor/opencv.js +48 -0
  205. package/tsconfig.json +9 -0
@@ -0,0 +1,33 @@
1
+ export default defineNuxtRouteMiddleware(async (to, from) => {
2
+ const user = useState<User>("user");
3
+ const database = useState<Database>("database");
4
+ const redirectTo = useScopedCookie<string | null>("redirectTo", database.value?.slug);
5
+ const isSafeRedirect = (value?: string) =>
6
+ !!value &&
7
+ value.startsWith("/") &&
8
+ !value.startsWith("//") &&
9
+ !value.includes("/auth");
10
+ const queryRedirectTo =
11
+ typeof to.query.redirectTo === "string" ? to.query.redirectTo : undefined;
12
+ if (user.value) {
13
+ if (["auth", "auth-reset", "database-auth", "database-auth-reset"].includes(to.name?.toString() ?? "")) {
14
+ const target = isSafeRedirect(queryRedirectTo)
15
+ ? queryRedirectTo
16
+ : isSafeRedirect(redirectTo.value ?? undefined)
17
+ ? redirectTo.value
18
+ : undefined;
19
+ return navigateTo(
20
+ target
21
+ ? target
22
+ : to.params.database
23
+ ? `/${database.value.slug}/admin`
24
+ : "/admin",
25
+ );
26
+ }
27
+ } else if (!["auth", "auth-reset", "database-auth", "database-auth-reset"].includes(to.name?.toString() ?? "")) {
28
+ redirectTo.value = from.fullPath;
29
+ return navigateTo(
30
+ to.params.database ? `/${database.value.slug}/auth` : "/auth",
31
+ );
32
+ }
33
+ });
@@ -0,0 +1,83 @@
1
+ import {
2
+ loadDatabaseConfigLocally,
3
+ saveDatabaseConfigLocally,
4
+ } from "~/composables/useLocalDatabaseConfig";
5
+ import { isJunkResponse, isNetworkError } from "~/composables/useOfflineFetch";
6
+
7
+ export default defineNuxtRouteMiddleware(async (to) => {
8
+ const database = useState<Database>("database");
9
+ const config = useRuntimeConfig();
10
+ const currentDatabaseSlug = String(
11
+ config.public.database === "inicontent"
12
+ ? to.params.database || config.public.database
13
+ : config.public.database || to.params.database,
14
+ );
15
+
16
+ const sessionID = useScopedCookie<string>("sid", currentDatabaseSlug);
17
+ const query: Record<string, string> = {};
18
+
19
+ if (sessionID.value) query[`${currentDatabaseSlug}_sid`] = sessionID.value;
20
+
21
+ if (!database.value) {
22
+ try {
23
+ database.value = (
24
+ await $fetch<apiResponse<Database>>(
25
+ `${config.public.apiBase}inicontent/databases/${currentDatabaseSlug}`,
26
+ {
27
+ credentials: "include",
28
+ query,
29
+ },
30
+ )
31
+ ).result;
32
+ // Persist a local copy of the database config so the app can still
33
+ // boot offline from local storage even when the service worker's
34
+ // config cache is missing or evicted.
35
+ saveDatabaseConfigLocally(currentDatabaseSlug, database.value);
36
+ } catch (error) {
37
+ // Offline (or a hung network). Prefer the locally-persisted copy of
38
+ // the database config — with it the app boots normally with the real
39
+ // metadata. In production the service worker also serves this request
40
+ // from its config cache, so this local copy is a second, independent
41
+ // fallback that survives cache eviction.
42
+ //
43
+ // Never THROW from here: an error thrown during the *initial*
44
+ // navigation rejects nuxt's `app:created` hook, which skips
45
+ // `vueApp.mount()` entirely in entry.js — the app never renders
46
+ // and the user just sees a blank page (NUXT_E1005). Instead,
47
+ // register the error via `showError()` so `payload.error` is set
48
+ // *before* mount (NuxtRoot then swaps in error.vue), and return
49
+ // `true` so the navigation itself completes and mounting proceeds.
50
+ const localConfig = loadDatabaseConfigLocally(currentDatabaseSlug);
51
+ if (localConfig) {
52
+ database.value = localConfig;
53
+ } else {
54
+ showError(
55
+ isNetworkError(error) || isJunkResponse(error)
56
+ ? createError({
57
+ statusCode: 503,
58
+ statusMessage: "offline",
59
+ })
60
+ : (error as Error),
61
+ );
62
+ return true;
63
+ }
64
+ }
65
+ }
66
+
67
+ if (!database.value) {
68
+ showError(createError({ statusCode: 404, statusMessage: "database" }));
69
+ return true;
70
+ }
71
+
72
+ formatDatabase();
73
+ syncCookiesFromDatabase(database.value.slug);
74
+
75
+ useState<ThemeConfig>("ThemeConfig", () => ({
76
+ primaryColor: "#FF9800",
77
+ primaryColorHover: "#F7A42A",
78
+ primaryColorPressed: "#E19421",
79
+ primaryColorSuppl: "#CB7900",
80
+ }));
81
+
82
+ setThemeConfig();
83
+ });
@@ -0,0 +1 @@
1
+ export default defineNuxtRouteMiddleware(() => {})
@@ -0,0 +1,29 @@
1
+ // Global middleware that restores sessions passed across deployments as URL
2
+ // query params. When the tenant app redirects to the platform app (pages,
3
+ // blocks, templates, billing), cookies cannot travel cross-origin, so the
4
+ // redirect URL carries `?{db}_sid=...&inicontent_sid=...` (see
5
+ // usePlatformRedirect). This middleware writes them back into the scoped
6
+ // cookies so the target boots already signed in. It runs before the route
7
+ // level `database` / `user` middlewares and is a no-op when the params are
8
+ // absent, so it is safe on every deployment of this app.
9
+ export default defineNuxtRouteMiddleware((to) => {
10
+ const config = useRuntimeConfig();
11
+ const databaseSlug = String(
12
+ config.public.database === "inicontent"
13
+ ? to.params.database || config.public.database
14
+ : config.public.database || to.params.database,
15
+ );
16
+
17
+ const ingest = (key: string) => {
18
+ const value = to.query[key];
19
+ if (typeof value !== "string" || !value.trim()) return;
20
+ const cookie = useCookie<string | null>(key, { sameSite: true });
21
+ // Never overwrite an existing (fresher) local session.
22
+ if (!cookie.value) cookie.value = value;
23
+ };
24
+
25
+ ingest(`${databaseSlug}_sid`);
26
+ // On the platform database the database sid key IS the platform key, so
27
+ // only the platform key is ingested there.
28
+ if (databaseSlug !== "inicontent") ingest("inicontent_sid");
29
+ });
@@ -0,0 +1,54 @@
1
+ export default defineNuxtRouteMiddleware(async (to) => {
2
+ const database = useState<Database>("database")
3
+ const table = useState<Table>("table")
4
+ let currentTableInPath: string | undefined = decodeURIComponent(
5
+ (to.params.table as string) ||
6
+ to.path.split("/admin/tables/")[1]?.split("/")[0] ||
7
+ "",
8
+ )
9
+
10
+ if (currentTableInPath === "undefined") currentTableInPath = undefined
11
+
12
+ if (!currentTableInPath && to.path.replace(/\/$/, "").includes("/auth"))
13
+ currentTableInPath = "users"
14
+
15
+ // The backups page is a database-level admin surface. New databases ship
16
+ // with a "backups" table in their meta, but legacy databases created before
17
+ // the feature only get a lazy table — so never 404 on its dedicated pages.
18
+ if (currentTableInPath === "backups") {
19
+ table.value =
20
+ database.value.tables?.find(({ slug }) => slug === "backups") ??
21
+ ({ slug: "backups", label: "backups" } as Table)
22
+ return
23
+ }
24
+
25
+ if (
26
+ !database.value.tables ||
27
+ !currentTableInPath ||
28
+ !database.value.tables?.find(({ slug }) => slug === currentTableInPath)
29
+ ) {
30
+ // The 404 error page renders inside the table layout. Drop any stale
31
+ // table state (e.g. the previously visited table) so the sidebar
32
+ // doesn't keep a now-mismatched table highlighted.
33
+ clearNuxtState("table")
34
+ // The redirect intent is dead (the table no longer exists) — drop it
35
+ // so a later login doesn't bounce back into this same 404 loop.
36
+ useScopedCookie<string | null>("redirectTo", database.value?.slug).value =
37
+ undefined
38
+ // Do NOT `throw` here: an error thrown from a route middleware during
39
+ // *client-side* navigation is swallowed by the router (nothing renders
40
+ // the error page), so navigating here from /auth after a redirectTo
41
+ // would abort silently and leave the user stuck on the auth page.
42
+ // Register the error with `showError()` (NuxtRoot swaps in error.vue)
43
+ // and `return true` so the navigation itself completes.
44
+ showError(createError({
45
+ statusCode: 404,
46
+ statusMessage: "table",
47
+ }))
48
+ return true
49
+ }
50
+
51
+ table.value = database.value.tables.find(
52
+ ({ slug }) => slug === currentTableInPath,
53
+ ) as Table
54
+ })
@@ -0,0 +1,26 @@
1
+ export default defineNuxtRouteMiddleware(async () => {
2
+ const user = useState<User>("user");
3
+ const database = useState<Database>("database");
4
+ const config = useRuntimeConfig();
5
+
6
+ const sessionID = useScopedCookie<string|undefined>("sid", database.value?.slug);
7
+
8
+ if (!user.value) {
9
+ const platformSessionID = useScopedCookie<string|undefined>("sid", "inicontent");
10
+
11
+ if (!sessionID.value && !platformSessionID.value) return;
12
+
13
+ user.value = (
14
+ await $fetch<apiResponse<User>>(
15
+ `${config.public.apiBase}${database.value.slug}/auth/current`,
16
+ {
17
+ credentials: "include",
18
+ query: sessionID.value ? { [`${database.value.slug}_sid`]: sessionID.value } : { "inicontent_sid": platformSessionID.value },
19
+ },
20
+ )
21
+ ).result;
22
+ if (!sessionID.value && user.value?.sessionID)
23
+ sessionID.value = user.value.sessionID;
24
+ if (!user.value) sessionID.value = undefined;
25
+ }
26
+ });
@@ -0,0 +1,306 @@
1
+ <template>
2
+ <main class="api-auth-doc-page">
3
+ <NCard :bordered="false">
4
+ <template #header>
5
+ <NFlex align="center" :size="8">
6
+ <Icon name="tabler:key" size="18" />
7
+ <NH2 style="margin: 0">{{ t('apiDocs.auth.title') }}</NH2>
8
+ </NFlex>
9
+ </template>
10
+ <NH5>{{ t('apiDocs.auth.description', { param: sessionParamName }) }}</NH5>
11
+ </NCard>
12
+
13
+ <section class="api-auth-doc-page__endpoints">
14
+ <ApiEndpointCard
15
+ :endpoint="signinEndpoint"
16
+ method="PUT"
17
+ :description="t('apiDocs.auth.signinDescription')"
18
+ :request-example="signinRequest"
19
+ :response-example="signinResponse"
20
+ :params="signinParams"
21
+ />
22
+
23
+ <ApiEndpointCard
24
+ :endpoint="signupEndpoint"
25
+ method="POST"
26
+ :description="t('apiDocs.auth.signupDescription')"
27
+ :request-example="signupRequest"
28
+ :response-example="signupResponse"
29
+ />
30
+
31
+ <ApiEndpointCard
32
+ :endpoint="currentEndpoint"
33
+ method="GET"
34
+ :description="t('apiDocs.auth.currentDescription', { param: sessionParamName })"
35
+ :response-example="currentResponse"
36
+ :params="currentParams"
37
+ />
38
+
39
+ <ApiEndpointCard
40
+ :endpoint="signoutEndpoint"
41
+ method="GET"
42
+ :description="t('apiDocs.auth.signoutDescription')"
43
+ :response-example="signoutResponse"
44
+ />
45
+
46
+ <ApiEndpointCard
47
+ :endpoint="resetRequestEndpoint"
48
+ method="POST"
49
+ :description="t('apiDocs.auth.resetRequestDescription')"
50
+ :request-example="resetRequestRequest"
51
+ :response-example="resetRequestResponse"
52
+ />
53
+
54
+ <ApiEndpointCard
55
+ :endpoint="resetCompleteEndpoint"
56
+ method="PUT"
57
+ :description="t('apiDocs.auth.resetCompleteDescription')"
58
+ :request-example="resetCompleteRequest"
59
+ :response-example="resetCompleteResponse"
60
+ />
61
+ </section>
62
+
63
+ <section v-if="usersTable?.schema?.length" class="api-auth-doc-page__schema">
64
+ <NCard :bordered="false">
65
+ <template #header>
66
+ <NFlex align="center" :size="8">
67
+ <Icon name="tabler:users" size="18" />
68
+ <NH3 style="margin: 0">{{ t('apiDocs.auth.signupSchemaTitle') }}</NH3>
69
+ </NFlex>
70
+ </template>
71
+ <ApiSchemaDocsTable :schema="usersTable.schema" size="large" />
72
+ </NCard>
73
+ </section>
74
+
75
+ <NAlert type="info" :show-icon="false">
76
+ {{ t('apiDocs.auth.cookieHint', { param: sessionParamName }) }}
77
+ </NAlert>
78
+ </main>
79
+ </template>
80
+
81
+ <script setup lang="ts">
82
+ import { Icon } from "#components";
83
+
84
+ definePageMeta({
85
+ layout: "dashboard",
86
+ middleware: ["database", "user", "dashboard", "global"],
87
+ });
88
+
89
+ const route = useRoute();
90
+ const database = useState<Database>("database");
91
+ const config = useRuntimeConfig();
92
+
93
+ const sessionParamName = computed(() => {
94
+ const slug =
95
+ (route.params.database as string | undefined) ||
96
+ database.value?.slug ||
97
+ "inicontent";
98
+ return `${slug}_sid`;
99
+ });
100
+
101
+ const dbSlug = computed(
102
+ () =>
103
+ (route.params.database as string | undefined) ||
104
+ database.value?.slug ||
105
+ "inicontent",
106
+ );
107
+
108
+ const apiBase = computed(() => config.public.apiBase);
109
+
110
+ const usersTable = computed(() =>
111
+ database.value?.tables?.find((table) => table.slug === "users"),
112
+ );
113
+
114
+ const signinEndpoint = computed(() => `/${dbSlug.value}/auth/signin`);
115
+ const signupEndpoint = computed(() => `/${dbSlug.value}/auth/signup`);
116
+ const currentEndpoint = computed(() => `/${dbSlug.value}/auth/current`);
117
+ const signoutEndpoint = computed(() => `/${dbSlug.value}/auth/signout`);
118
+ const resetRequestEndpoint = computed(() => `/${dbSlug.value}/auth/reset`);
119
+ const resetCompleteEndpoint = computed(() => `/${dbSlug.value}/auth/reset`);
120
+
121
+ const signinRequest = `{
122
+ "username": "demo",
123
+ "password": "mypassword123"
124
+ }`;
125
+
126
+ const signinResponse = `{
127
+ "result": {
128
+ "id": "a1b2c3d4",
129
+ "username": "demo",
130
+ "email": "demo@example.com",
131
+ "role": "editor",
132
+ "createdBy": "...",
133
+ "sessionID": "e5f6g7h8i9j0"
134
+ },
135
+ "message": "Signed in successfully",
136
+ "options": {},
137
+ "code": 200
138
+ }`;
139
+
140
+ const signinParams = computed(() => [
141
+ {
142
+ name: "locale",
143
+ type: "string",
144
+ required: false,
145
+ description: t("apiDocs.localeParam"),
146
+ },
147
+ ]);
148
+
149
+ const signupRequest = `{
150
+ "username": "newuser",
151
+ "password": "securepass123",
152
+ "email": "newuser@example.com",
153
+ "role": "editor"
154
+ }`;
155
+
156
+ const signupResponse = `{
157
+ "result": {
158
+ "id": "b2c3d4e5",
159
+ "username": "newuser",
160
+ "email": "newuser@example.com",
161
+ "role": "editor",
162
+ "createdBy": "..."
163
+ },
164
+ "message": "User created successfully",
165
+ "options": {},
166
+ "code": 200
167
+ }`;
168
+
169
+ const currentResponse = `{
170
+ "result": {
171
+ "id": "a1b2c3d4",
172
+ "username": "demo",
173
+ "email": "demo@example.com",
174
+ "role": "editor",
175
+ "createdBy": "..."
176
+ },
177
+ "message": "Session is valid",
178
+ "options": {},
179
+ "code": 200
180
+ }`;
181
+
182
+ const currentParams = computed(() => [
183
+ {
184
+ name: sessionParamName.value,
185
+ type: "string",
186
+ required: true,
187
+ description: "Session ID",
188
+ },
189
+ {
190
+ name: "isSignedIn",
191
+ type: "boolean",
192
+ required: false,
193
+ description: t("apiDocs.isSignedInParam"),
194
+ },
195
+ ]);
196
+
197
+ const signoutResponse = `{
198
+ "result": true,
199
+ "message": "Signed out successfully",
200
+ "options": {},
201
+ "code": 200
202
+ }`;
203
+
204
+ const resetRequestRequest = `{
205
+ "email": "demo@example.com"
206
+ }`;
207
+
208
+ const resetRequestResponse = `{
209
+ "result": true,
210
+ "message": "Password reset email sent",
211
+ "options": {},
212
+ "code": 200
213
+ }`;
214
+
215
+ const resetCompleteRequest = `{
216
+ "token": "abc123def456",
217
+ "password": "newpassword123"
218
+ }`;
219
+
220
+ const resetCompleteResponse = `{
221
+ "result": true,
222
+ "message": "Password has been reset successfully",
223
+ "options": {},
224
+ "code": 200
225
+ }`;
226
+
227
+ const jsonLd = computed(() => ({
228
+ "@context": "https://schema.org",
229
+ "@type": "WebAPI",
230
+ name: `${dbSlug.value} Authentication API`,
231
+ description: "Authentication endpoints for user sessions",
232
+ baseUrl: `${apiBase.value}${dbSlug.value}/auth`,
233
+ endpoints: [
234
+ {
235
+ "@type": "EntryPoint",
236
+ name: "Sign in",
237
+ httpMethod: "PUT",
238
+ urlTemplate: `${apiBase.value}${dbSlug.value}/auth/signin`,
239
+ },
240
+ {
241
+ "@type": "EntryPoint",
242
+ name: "Sign up",
243
+ httpMethod: "POST",
244
+ urlTemplate: `${apiBase.value}${dbSlug.value}/auth/signup`,
245
+ },
246
+ {
247
+ "@type": "EntryPoint",
248
+ name: "Current user",
249
+ httpMethod: "GET",
250
+ urlTemplate: `${apiBase.value}${dbSlug.value}/auth/current`,
251
+ },
252
+ {
253
+ "@type": "EntryPoint",
254
+ name: "Sign out",
255
+ httpMethod: "GET",
256
+ urlTemplate: `${apiBase.value}${dbSlug.value}/auth/signout`,
257
+ },
258
+ {
259
+ "@type": "EntryPoint",
260
+ name: "Request password reset",
261
+ httpMethod: "POST",
262
+ urlTemplate: `${apiBase.value}${dbSlug.value}/auth/reset`,
263
+ },
264
+ {
265
+ "@type": "EntryPoint",
266
+ name: "Complete password reset",
267
+ httpMethod: "PUT",
268
+ urlTemplate: `${apiBase.value}${dbSlug.value}/auth/reset`,
269
+ },
270
+ ],
271
+ }));
272
+
273
+ useHead(() => ({
274
+ title: `${t("auth")} | ${t("apiDocumentation")}`,
275
+ meta: [
276
+ {
277
+ name: "description",
278
+ content: `API authentication endpoints for ${dbSlug.value}. Sign in, sign up, manage sessions, and reset passwords.`,
279
+ },
280
+ ],
281
+ script: [
282
+ {
283
+ type: "application/ld+json",
284
+ innerHTML: JSON.stringify(jsonLd.value),
285
+ },
286
+ ],
287
+ }));
288
+ </script>
289
+
290
+ <style scoped>
291
+ .api-auth-doc-page {
292
+ display: flex;
293
+ flex-direction: column;
294
+ gap: 24px;
295
+ }
296
+
297
+ .api-auth-doc-page__endpoints {
298
+ display: flex;
299
+ flex-direction: column;
300
+ gap: 16px;
301
+ }
302
+
303
+ .api-auth-doc-page__schema {
304
+ margin-top: 8px;
305
+ }
306
+ </style>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <NFlex justify="center" align="center" style="min-height: 50vh">
3
+ <NSpin size="large" />
4
+ </NFlex>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ definePageMeta({
9
+ layout: "dashboard",
10
+ middleware: ["database", "user", "dashboard", "global"],
11
+ });
12
+
13
+ const route = useRoute();
14
+
15
+ await navigateTo(
16
+ `${route.params.database ? `/${route.params.database}` : ""}/admin/api/tables`,
17
+ );
18
+ </script>