ta_data_mas 0.0.2

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 (219) hide show
  1. package/README.md +42 -0
  2. package/apps/api/.prettierrc +4 -0
  3. package/apps/api/README.md +98 -0
  4. package/apps/api/drizzle/0000_right_drax.sql +87 -0
  5. package/apps/api/drizzle/meta/0000_snapshot.json +588 -0
  6. package/apps/api/drizzle/meta/_journal.json +13 -0
  7. package/apps/api/drizzle.config.ts +12 -0
  8. package/apps/api/eslint.config.mjs +35 -0
  9. package/apps/api/nest-cli.json +8 -0
  10. package/apps/api/package.json +99 -0
  11. package/apps/api/src/app.controller.spec.ts +25 -0
  12. package/apps/api/src/app.controller.ts +9 -0
  13. package/apps/api/src/app.module.ts +37 -0
  14. package/apps/api/src/app.service.ts +8 -0
  15. package/apps/api/src/auth/auth.controller.ts +113 -0
  16. package/apps/api/src/auth/auth.module.ts +15 -0
  17. package/apps/api/src/auth/auth.service.ts +309 -0
  18. package/apps/api/src/auth/decorators/current-user-decorator.ts +10 -0
  19. package/apps/api/src/auth/decorators/require-org-role-decorator.ts +7 -0
  20. package/apps/api/src/auth/dto/login.dto.ts +10 -0
  21. package/apps/api/src/auth/dto/register.dto.ts +14 -0
  22. package/apps/api/src/auth/guards/jwt-auth-guard.ts +40 -0
  23. package/apps/api/src/auth/guards/org-role.guards.ts +60 -0
  24. package/apps/api/src/db/db.module.ts +9 -0
  25. package/apps/api/src/db/drizzle.service.ts +16 -0
  26. package/apps/api/src/db/schema/index.ts +6 -0
  27. package/apps/api/src/db/schema/organizations.ts +30 -0
  28. package/apps/api/src/db/schema/projects.ts +26 -0
  29. package/apps/api/src/db/schema/query-history.ts +16 -0
  30. package/apps/api/src/db/schema/storage-buckets.ts +17 -0
  31. package/apps/api/src/db/schema/storage-objects.ts +18 -0
  32. package/apps/api/src/db/schema/users.ts +14 -0
  33. package/apps/api/src/main.ts +51 -0
  34. package/apps/api/src/members/dto/invite-member-dto.ts +6 -0
  35. package/apps/api/src/members/dto/update-role.dto.ts +8 -0
  36. package/apps/api/src/members/invite.service.ts +147 -0
  37. package/apps/api/src/members/members.controller.ts +56 -0
  38. package/apps/api/src/members/members.module.ts +13 -0
  39. package/apps/api/src/members/members.service.ts +85 -0
  40. package/apps/api/src/orgs/dto/create-org.dto.ts +8 -0
  41. package/apps/api/src/orgs/orgs.controller.ts +27 -0
  42. package/apps/api/src/orgs/orgs.module.ts +12 -0
  43. package/apps/api/src/orgs/orgs.service.ts +86 -0
  44. package/apps/api/src/project-api/project-api.controller.ts +95 -0
  45. package/apps/api/src/project-api/project-api.module.ts +12 -0
  46. package/apps/api/src/project-api/project-api.service.ts +196 -0
  47. package/apps/api/src/project-api/project-key.guard.ts +45 -0
  48. package/apps/api/src/project-api/query-parser.ts +129 -0
  49. package/apps/api/src/project-auth/dto/magic-link.dto.ts +7 -0
  50. package/apps/api/src/project-auth/dto/signin.dto.ts +10 -0
  51. package/apps/api/src/project-auth/dto/signup.dto.ts +11 -0
  52. package/apps/api/src/project-auth/project-auth-dashboard.controller.ts +35 -0
  53. package/apps/api/src/project-auth/project-auth.controller.ts +111 -0
  54. package/apps/api/src/project-auth/project-auth.module.ts +13 -0
  55. package/apps/api/src/project-auth/project-auth.service.ts +440 -0
  56. package/apps/api/src/projects/dto/create-project.dto.ts +9 -0
  57. package/apps/api/src/projects/projects.controller.ts +34 -0
  58. package/apps/api/src/projects/projects.module.ts +12 -0
  59. package/apps/api/src/projects/projects.service.ts +136 -0
  60. package/apps/api/src/realtime/realtime-socket.type.ts +14 -0
  61. package/apps/api/src/realtime/realtime.controller.ts +63 -0
  62. package/apps/api/src/realtime/realtime.gateway.ts +150 -0
  63. package/apps/api/src/realtime/realtime.module.ts +14 -0
  64. package/apps/api/src/realtime/realtime.service.ts +101 -0
  65. package/apps/api/src/realtime/trigger.service.ts +95 -0
  66. package/apps/api/src/sql-editor/dto/execute-query.dto.ts +7 -0
  67. package/apps/api/src/sql-editor/sql-editor.controller.ts +28 -0
  68. package/apps/api/src/sql-editor/sql-editor.module.ts +12 -0
  69. package/apps/api/src/sql-editor/sql-editor.service.ts +154 -0
  70. package/apps/api/src/storage/project-storage.controller.ts +140 -0
  71. package/apps/api/src/storage/storage.controller.ts +118 -0
  72. package/apps/api/src/storage/storage.module.ts +15 -0
  73. package/apps/api/src/storage/storage.service.ts +191 -0
  74. package/apps/api/src/storage/uploadthing.ts +23 -0
  75. package/apps/api/src/table-editor/dto/alter-table.dto.ts +15 -0
  76. package/apps/api/src/table-editor/dto/create-table.dto.ts +54 -0
  77. package/apps/api/src/table-editor/table-editor.controller.ts +117 -0
  78. package/apps/api/src/table-editor/table-editor.module.ts +12 -0
  79. package/apps/api/src/table-editor/table-editor.service.ts +366 -0
  80. package/apps/api/test/app.e2e-spec.ts +29 -0
  81. package/apps/api/test/jest-e2e.json +9 -0
  82. package/apps/api/tsconfig.build.json +4 -0
  83. package/apps/api/tsconfig.json +25 -0
  84. package/apps/web/.env.example +12 -0
  85. package/apps/web/AGENTS.md +9 -0
  86. package/apps/web/CLAUDE.md +1 -0
  87. package/apps/web/README.md +36 -0
  88. package/apps/web/components.json +25 -0
  89. package/apps/web/eslint.config.mjs +18 -0
  90. package/apps/web/next.config.ts +11 -0
  91. package/apps/web/package.json +48 -0
  92. package/apps/web/postcss.config.mjs +7 -0
  93. package/apps/web/public/file.svg +1 -0
  94. package/apps/web/public/globe.svg +1 -0
  95. package/apps/web/public/logo.svg +4 -0
  96. package/apps/web/public/next.svg +1 -0
  97. package/apps/web/public/vercel.svg +1 -0
  98. package/apps/web/public/window.svg +1 -0
  99. package/apps/web/src/app/(dashboard)/layout.tsx +33 -0
  100. package/apps/web/src/app/(dashboard)/organizations/(list)/layout.tsx +7 -0
  101. package/apps/web/src/app/(dashboard)/organizations/(list)/new/page.tsx +26 -0
  102. package/apps/web/src/app/(dashboard)/organizations/(list)/page.tsx +44 -0
  103. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/api/page.tsx +13 -0
  104. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/layout.tsx +19 -0
  105. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/page.tsx +10 -0
  106. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/providers/page.tsx +19 -0
  107. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/url-configuration/page.tsx +24 -0
  108. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/auth/users/page.tsx +13 -0
  109. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/database/page.tsx +29 -0
  110. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/realtime/page.tsx +28 -0
  111. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/sql/page.tsx +21 -0
  112. package/apps/web/src/app/(dashboard)/organizations/[slug]/[projectSlug]/storage/page.tsx +28 -0
  113. package/apps/web/src/app/(dashboard)/organizations/[slug]/layout.tsx +20 -0
  114. package/apps/web/src/app/(dashboard)/organizations/[slug]/projects/page.tsx +62 -0
  115. package/apps/web/src/app/(dashboard)/organizations/[slug]/settings/members/page.tsx +28 -0
  116. package/apps/web/src/app/(dashboard)/organizations/[slug]/settings/page.tsx +10 -0
  117. package/apps/web/src/app/(dashboard)/settings/page.tsx +12 -0
  118. package/apps/web/src/app/dashboard/page.tsx +14 -0
  119. package/apps/web/src/app/favicon.ico +0 -0
  120. package/apps/web/src/app/globals.css +115 -0
  121. package/apps/web/src/app/layout.tsx +36 -0
  122. package/apps/web/src/app/login/page.tsx +199 -0
  123. package/apps/web/src/app/page.tsx +100 -0
  124. package/apps/web/src/app/register/page.tsx +218 -0
  125. package/apps/web/src/components/app-sidebar.tsx +301 -0
  126. package/apps/web/src/components/navbar.tsx +63 -0
  127. package/apps/web/src/components/topNav.tsx +68 -0
  128. package/apps/web/src/components/ui/avatar.tsx +112 -0
  129. package/apps/web/src/components/ui/badge.tsx +48 -0
  130. package/apps/web/src/components/ui/button.tsx +66 -0
  131. package/apps/web/src/components/ui/card.tsx +102 -0
  132. package/apps/web/src/components/ui/checkbox.tsx +32 -0
  133. package/apps/web/src/components/ui/dialog.tsx +168 -0
  134. package/apps/web/src/components/ui/dropdown-menu.tsx +269 -0
  135. package/apps/web/src/components/ui/field.tsx +238 -0
  136. package/apps/web/src/components/ui/input.tsx +19 -0
  137. package/apps/web/src/components/ui/label.tsx +24 -0
  138. package/apps/web/src/components/ui/resizable.tsx +50 -0
  139. package/apps/web/src/components/ui/scroll-area.tsx +55 -0
  140. package/apps/web/src/components/ui/select.tsx +192 -0
  141. package/apps/web/src/components/ui/separator.tsx +28 -0
  142. package/apps/web/src/components/ui/sheet.tsx +147 -0
  143. package/apps/web/src/components/ui/sidebar.tsx +703 -0
  144. package/apps/web/src/components/ui/skeleton.tsx +13 -0
  145. package/apps/web/src/components/ui/switch.tsx +33 -0
  146. package/apps/web/src/components/ui/table.tsx +116 -0
  147. package/apps/web/src/components/ui/tooltip.tsx +57 -0
  148. package/apps/web/src/features/api-docs/api-docs-client.tsx +227 -0
  149. package/apps/web/src/features/api-docs/api-docs-helpers.server.ts +86 -0
  150. package/apps/web/src/features/auth/action.ts +67 -0
  151. package/apps/web/src/features/auth/client.schema.ts +13 -0
  152. package/apps/web/src/features/auth/constants.ts +6 -0
  153. package/apps/web/src/features/auth/cookies.ts +86 -0
  154. package/apps/web/src/features/auth/server.schema.ts +11 -0
  155. package/apps/web/src/features/auth/sign-out.ts +17 -0
  156. package/apps/web/src/features/members/action.ts +66 -0
  157. package/apps/web/src/features/members/client.schema.ts +11 -0
  158. package/apps/web/src/features/members/constants.ts +8 -0
  159. package/apps/web/src/features/members/members-client.tsx +198 -0
  160. package/apps/web/src/features/members/members-helpers.server.ts +21 -0
  161. package/apps/web/src/features/members/server.schema.ts +19 -0
  162. package/apps/web/src/features/organization/action.ts +45 -0
  163. package/apps/web/src/features/organization/client.schema.ts +5 -0
  164. package/apps/web/src/features/organization/constants.tsx +3 -0
  165. package/apps/web/src/features/organization/create-org-form.tsx +66 -0
  166. package/apps/web/src/features/organization/organization-helpers.server.ts +35 -0
  167. package/apps/web/src/features/organization/server.schema.ts +10 -0
  168. package/apps/web/src/features/project-auth/project-auth-action.ts +78 -0
  169. package/apps/web/src/features/project-auth/project-auth-client.schema.ts +18 -0
  170. package/apps/web/src/features/project-auth/project-auth-constants.ts +8 -0
  171. package/apps/web/src/features/project-auth/project-auth-helpers.server.ts +39 -0
  172. package/apps/web/src/features/project-auth/project-auth-providers.tsx +222 -0
  173. package/apps/web/src/features/project-auth/project-auth-server.schema.ts +22 -0
  174. package/apps/web/src/features/project-auth/project-auth-shell.tsx +106 -0
  175. package/apps/web/src/features/project-auth/project-auth-url-config.tsx +151 -0
  176. package/apps/web/src/features/project-auth/project-auth-users.tsx +138 -0
  177. package/apps/web/src/features/projects/action.ts +46 -0
  178. package/apps/web/src/features/projects/client.schema.ts +9 -0
  179. package/apps/web/src/features/projects/constants.ts +6 -0
  180. package/apps/web/src/features/projects/create-project-modal.tsx +117 -0
  181. package/apps/web/src/features/projects/project-helpers.server.ts +38 -0
  182. package/apps/web/src/features/projects/server.schema.ts +10 -0
  183. package/apps/web/src/features/realtime/realtime-client.tsx +294 -0
  184. package/apps/web/src/features/realtime/realtime-helpers.server.ts +40 -0
  185. package/apps/web/src/features/sql-editor/sql-editor-client.tsx +324 -0
  186. package/apps/web/src/features/sql-editor/sql-editor-helpers.server.ts +22 -0
  187. package/apps/web/src/features/storage/storage-client.tsx +500 -0
  188. package/apps/web/src/features/storage/storage-helpers.server.ts +36 -0
  189. package/apps/web/src/features/table-editor/action.ts +97 -0
  190. package/apps/web/src/features/table-editor/add-column-dialog.tsx +184 -0
  191. package/apps/web/src/features/table-editor/client.schema.ts +36 -0
  192. package/apps/web/src/features/table-editor/create-table-drawer.tsx +542 -0
  193. package/apps/web/src/features/table-editor/server.schema.ts +41 -0
  194. package/apps/web/src/features/table-editor/table-editor-client.tsx +737 -0
  195. package/apps/web/src/features/table-editor/table-editor-helpers.server.ts +39 -0
  196. package/apps/web/src/hooks/use-mobile.ts +19 -0
  197. package/apps/web/src/lib/axios.ts +8 -0
  198. package/apps/web/src/lib/utils.ts +6 -0
  199. package/apps/web/src/proxy.ts +72 -0
  200. package/apps/web/src/server-utils/utils.ts +10 -0
  201. package/apps/web/tsconfig.json +34 -0
  202. package/package.json +22 -0
  203. package/packages/apiDatabase-js/package.json +39 -0
  204. package/packages/apiDatabase-js/src/auth/index.ts +121 -0
  205. package/packages/apiDatabase-js/src/client.ts +25 -0
  206. package/packages/apiDatabase-js/src/db/index.ts +15 -0
  207. package/packages/apiDatabase-js/src/db/query-builder.ts +194 -0
  208. package/packages/apiDatabase-js/src/index.ts +16 -0
  209. package/packages/apiDatabase-js/src/realtime/index.ts +84 -0
  210. package/packages/apiDatabase-js/src/storage/index.ts +130 -0
  211. package/packages/apiDatabase-js/tsconfig.json +13 -0
  212. package/packages/apiDatabase-js/tsdown.config.ts +20 -0
  213. package/packages/constants/package.json +6 -0
  214. package/packages/constants/src/index.ts +76 -0
  215. package/packages/tsconfig.json +13 -0
  216. package/packages/tsdown.config.ts +20 -0
  217. package/packages/types/package.json +6 -0
  218. package/packages/types/src/index.ts +254 -0
  219. package/pnpm-workspace.yaml +10 -0
@@ -0,0 +1,33 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Switch as SwitchPrimitive } from "radix-ui"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ function Switch({
9
+ className,
10
+ size = "default",
11
+ ...props
12
+ }: React.ComponentProps<typeof SwitchPrimitive.Root> & {
13
+ size?: "sm" | "default"
14
+ }) {
15
+ return (
16
+ <SwitchPrimitive.Root
17
+ data-slot="switch"
18
+ data-size={size}
19
+ className={cn(
20
+ "peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
21
+ className
22
+ )}
23
+ {...props}
24
+ >
25
+ <SwitchPrimitive.Thumb
26
+ data-slot="switch-thumb"
27
+ className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
28
+ />
29
+ </SwitchPrimitive.Root>
30
+ )
31
+ }
32
+
33
+ export { Switch }
@@ -0,0 +1,116 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
8
+ return (
9
+ <div
10
+ data-slot="table-container"
11
+ className="relative w-full overflow-x-auto"
12
+ >
13
+ <table
14
+ data-slot="table"
15
+ className={cn("w-full caption-bottom text-sm", className)}
16
+ {...props}
17
+ />
18
+ </div>
19
+ )
20
+ }
21
+
22
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
23
+ return (
24
+ <thead
25
+ data-slot="table-header"
26
+ className={cn("[&_tr]:border-b", className)}
27
+ {...props}
28
+ />
29
+ )
30
+ }
31
+
32
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
33
+ return (
34
+ <tbody
35
+ data-slot="table-body"
36
+ className={cn("[&_tr:last-child]:border-0", className)}
37
+ {...props}
38
+ />
39
+ )
40
+ }
41
+
42
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
43
+ return (
44
+ <tfoot
45
+ data-slot="table-footer"
46
+ className={cn(
47
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
48
+ className
49
+ )}
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+
55
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
56
+ return (
57
+ <tr
58
+ data-slot="table-row"
59
+ className={cn(
60
+ "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
61
+ className
62
+ )}
63
+ {...props}
64
+ />
65
+ )
66
+ }
67
+
68
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
69
+ return (
70
+ <th
71
+ data-slot="table-head"
72
+ className={cn(
73
+ "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
74
+ className
75
+ )}
76
+ {...props}
77
+ />
78
+ )
79
+ }
80
+
81
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
82
+ return (
83
+ <td
84
+ data-slot="table-cell"
85
+ className={cn(
86
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
87
+ className
88
+ )}
89
+ {...props}
90
+ />
91
+ )
92
+ }
93
+
94
+ function TableCaption({
95
+ className,
96
+ ...props
97
+ }: React.ComponentProps<"caption">) {
98
+ return (
99
+ <caption
100
+ data-slot="table-caption"
101
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
102
+ {...props}
103
+ />
104
+ )
105
+ }
106
+
107
+ export {
108
+ Table,
109
+ TableHeader,
110
+ TableBody,
111
+ TableFooter,
112
+ TableHead,
113
+ TableRow,
114
+ TableCell,
115
+ TableCaption,
116
+ }
@@ -0,0 +1,57 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Tooltip as TooltipPrimitive } from "radix-ui"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ function TooltipProvider({
9
+ delayDuration = 0,
10
+ ...props
11
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12
+ return (
13
+ <TooltipPrimitive.Provider
14
+ data-slot="tooltip-provider"
15
+ delayDuration={delayDuration}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ function Tooltip({
22
+ ...props
23
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
24
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
25
+ }
26
+
27
+ function TooltipTrigger({
28
+ ...props
29
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
30
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
31
+ }
32
+
33
+ function TooltipContent({
34
+ className,
35
+ sideOffset = 0,
36
+ children,
37
+ ...props
38
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
39
+ return (
40
+ <TooltipPrimitive.Portal>
41
+ <TooltipPrimitive.Content
42
+ data-slot="tooltip-content"
43
+ sideOffset={sideOffset}
44
+ className={cn(
45
+ "z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
46
+ className
47
+ )}
48
+ {...props}
49
+ >
50
+ {children}
51
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
52
+ </TooltipPrimitive.Content>
53
+ </TooltipPrimitive.Portal>
54
+ )
55
+ }
56
+
57
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }
@@ -0,0 +1,227 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Eye, EyeOff, Copy, Check } from "lucide-react";
5
+ import { Button } from "@/components/ui/button";
6
+ import type { ProjectApiDocs } from "@apiDatabase/types";
7
+
8
+ export function ApiDocsClient({ docs }: { docs: ProjectApiDocs }) {
9
+ const [showAnonKey, setShowAnonKey] = useState(false);
10
+ const [showServiceKey, setShowServiceKey] = useState(false);
11
+ const [copiedKey, setCopiedKey] = useState<string | null>(null);
12
+
13
+ const copyToClipboard = async (text: string, key: string) => {
14
+ await navigator.clipboard.writeText(text);
15
+ setCopiedKey(key);
16
+ setTimeout(() => setCopiedKey(null), 2000);
17
+ };
18
+
19
+ const methodColors: Record<string, string> = {
20
+ GET: "bg-blue-100 text-blue-700",
21
+ POST: "bg-green-100 text-green-700",
22
+ PATCH: "bg-yellow-100 text-yellow-700",
23
+ DELETE: "bg-red-100 text-red-700",
24
+ };
25
+
26
+ return (
27
+ <div className="mx-auto w-full min-w-0 max-w-4xl space-y-8 overflow-x-hidden">
28
+ <div>
29
+ <h1 className="text-2xl font-medium">API</h1>
30
+ <p className="mt-1 text-sm text-muted-foreground">
31
+ Auto-generated REST endpoints for your project tables
32
+ </p>
33
+ </div>
34
+
35
+ <div className="space-y-4">
36
+ <h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
37
+ API Keys
38
+ </h2>
39
+
40
+ <div className="space-y-1 rounded-xl border border-border p-4">
41
+ <p className="text-xs text-muted-foreground">Project URL</p>
42
+ <div className="flex min-w-0 items-center justify-between gap-2">
43
+ <code className="min-w-0 break-all font-mono text-sm">
44
+ {docs.projectUrl}
45
+ </code>
46
+ <Button
47
+ variant="ghost"
48
+ size="icon"
49
+ className="h-7 w-7 shrink-0"
50
+ onClick={() => void copyToClipboard(docs.projectUrl, "url")}
51
+ >
52
+ {copiedKey === "url" ? <Check size={12} /> : <Copy size={12} />}
53
+ </Button>
54
+ </div>
55
+ </div>
56
+
57
+ <div className="space-y-1 rounded-xl border border-border p-4">
58
+ <div className="flex items-center justify-between gap-2">
59
+ <p className="text-xs text-muted-foreground">Anon Key</p>
60
+ <p className="shrink-0 text-xs text-muted-foreground">
61
+ Read-only (GET)
62
+ </p>
63
+ </div>
64
+ <div className="flex min-w-0 items-center justify-between gap-2">
65
+ <code className="min-w-0 break-all font-mono text-sm">
66
+ {showAnonKey ? docs.anonKey : "•".repeat(40)}
67
+ </code>
68
+ <div className="flex shrink-0 gap-1">
69
+ <Button
70
+ variant="ghost"
71
+ size="icon"
72
+ className="h-7 w-7"
73
+ onClick={() => setShowAnonKey((v) => !v)}
74
+ >
75
+ {showAnonKey ? <EyeOff size={12} /> : <Eye size={12} />}
76
+ </Button>
77
+ <Button
78
+ variant="ghost"
79
+ size="icon"
80
+ className="h-7 w-7"
81
+ onClick={() => void copyToClipboard(docs.anonKey, "anon")}
82
+ >
83
+ {copiedKey === "anon" ? (
84
+ <Check size={12} />
85
+ ) : (
86
+ <Copy size={12} />
87
+ )}
88
+ </Button>
89
+ </div>
90
+ </div>
91
+ </div>
92
+
93
+ <div className="space-y-1 rounded-xl border border-border p-4">
94
+ <div className="flex items-center justify-between gap-2">
95
+ <p className="text-xs text-muted-foreground">Service Role Key</p>
96
+ <p className="shrink-0 text-xs text-red-500">
97
+ Full access — server only
98
+ </p>
99
+ </div>
100
+ <div className="flex min-w-0 items-center justify-between gap-2">
101
+ <code className="min-w-0 break-all font-mono text-sm">
102
+ {showServiceKey ? docs.serviceRoleKey : "•".repeat(40)}
103
+ </code>
104
+ <div className="flex shrink-0 gap-1">
105
+ <Button
106
+ variant="ghost"
107
+ size="icon"
108
+ className="h-7 w-7"
109
+ onClick={() => setShowServiceKey((v) => !v)}
110
+ >
111
+ {showServiceKey ? <EyeOff size={12} /> : <Eye size={12} />}
112
+ </Button>
113
+ <Button
114
+ variant="ghost"
115
+ size="icon"
116
+ className="h-7 w-7"
117
+ onClick={() =>
118
+ void copyToClipboard(docs.serviceRoleKey, "service")
119
+ }
120
+ >
121
+ {copiedKey === "service" ? (
122
+ <Check size={12} />
123
+ ) : (
124
+ <Copy size={12} />
125
+ )}
126
+ </Button>
127
+ </div>
128
+ </div>
129
+ </div>
130
+ </div>
131
+
132
+ {docs.tables.length === 0 ? (
133
+ <div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-border py-16 text-center">
134
+ <p className="text-sm text-muted-foreground">No tables yet</p>
135
+ <p className="mt-1 text-xs text-muted-foreground">
136
+ Create a table in the Table Editor to see its endpoints here
137
+ </p>
138
+ </div>
139
+ ) : (
140
+ <div className="space-y-8">
141
+ <h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
142
+ Endpoints
143
+ </h2>
144
+ {docs.tables.map((table) => (
145
+ <div key={table.name} className="min-w-0 space-y-3">
146
+ <h3 className="text-base font-medium">{table.name}</h3>
147
+ <div className="space-y-2">
148
+ {table.endpoints.map((endpoint, i) => (
149
+ <EndpointCard
150
+ key={i}
151
+ endpoint={endpoint}
152
+ methodColors={methodColors}
153
+ onCopy={copyToClipboard}
154
+ copiedKey={copiedKey}
155
+ />
156
+ ))}
157
+ </div>
158
+ </div>
159
+ ))}
160
+ </div>
161
+ )}
162
+ </div>
163
+ );
164
+ }
165
+
166
+ function EndpointCard({
167
+ endpoint,
168
+ methodColors,
169
+ onCopy,
170
+ copiedKey,
171
+ }: {
172
+ endpoint: {
173
+ method: string;
174
+ path: string;
175
+ description: string;
176
+ example: string;
177
+ };
178
+ methodColors: Record<string, string>;
179
+ onCopy: (text: string, key: string) => Promise<void>;
180
+ copiedKey: string | null;
181
+ }) {
182
+ const [expanded, setExpanded] = useState(false);
183
+ const copyKey = `${endpoint.method}-${endpoint.path}`;
184
+
185
+ return (
186
+ <div
187
+ className="min-w-0 cursor-pointer overflow-hidden rounded-xl border border-border"
188
+ onClick={() => setExpanded((v) => !v)}
189
+ >
190
+ <div className="flex min-w-0 items-start gap-3 px-4 py-3 transition-colors hover:bg-accent/50">
191
+ <span
192
+ className={`mt-0.5 shrink-0 rounded px-2 py-0.5 font-mono text-xs font-bold ${methodColors[endpoint.method]}`}
193
+ >
194
+ {endpoint.method}
195
+ </span>
196
+ <div className="min-w-0 flex-1 space-y-1">
197
+ <code className="block break-all font-mono text-sm">
198
+ {endpoint.path}
199
+ </code>
200
+ <p className="text-xs text-muted-foreground">
201
+ {endpoint.description}
202
+ </p>
203
+ </div>
204
+ </div>
205
+ {expanded && (
206
+ <div className="border-t border-border bg-muted/30 px-4 py-3">
207
+ <div className="flex min-w-0 items-start justify-between gap-2">
208
+ <pre className="min-w-0 flex-1 overflow-x-auto whitespace-pre-wrap break-all font-mono text-xs text-muted-foreground">
209
+ {endpoint.example}
210
+ </pre>
211
+ <Button
212
+ variant="ghost"
213
+ size="icon"
214
+ className="h-7 w-7 shrink-0"
215
+ onClick={(e) => {
216
+ e.stopPropagation();
217
+ void onCopy(endpoint.example, copyKey);
218
+ }}
219
+ >
220
+ {copiedKey === copyKey ? <Check size={12} /> : <Copy size={12} />}
221
+ </Button>
222
+ </div>
223
+ </div>
224
+ )}
225
+ </div>
226
+ );
227
+ }
@@ -0,0 +1,86 @@
1
+ import { redirect } from "next/navigation";
2
+ import apiClient from "@/lib/axios";
3
+ import { retrieveTokenFromCookie } from "@/server-utils/utils";
4
+ import { COOKIE_KEYS } from "@apiDatabase/constants";
5
+ import type {
6
+ ProjectApiDocs,
7
+ ProjectApiEndpoint,
8
+ ProjectBySlugResponse,
9
+ } from "@apiDatabase/types";
10
+
11
+ function buildEndpoints(
12
+ projectUrl: string,
13
+ tableName: string,
14
+ ): ProjectApiEndpoint[] {
15
+ const base = `${projectUrl}/rest/${tableName}`;
16
+
17
+ return [
18
+ {
19
+ method: "GET",
20
+ path: base,
21
+ description: `Fetch rows from ${tableName}. Supports filters, ordering, pagination. Anon key OK.`,
22
+ example: `curl "${base}?limit=10&order=created_at.desc" \\\n -H "Authorization: Bearer <anon_key>"`,
23
+ },
24
+ {
25
+ method: "GET",
26
+ path: `${base}?col=eq.value`,
27
+ description:
28
+ "Filter rows. Operators: eq, neq, gt, gte, lt, lte, like, ilike, is.",
29
+ example: `curl "${base}?name=eq.Shirt&price=lt.100" \\\n -H "Authorization: Bearer <anon_key>"`,
30
+ },
31
+ {
32
+ method: "POST",
33
+ path: base,
34
+ description: `Insert a row into ${tableName}. Requires service role key.`,
35
+ example: `curl -X POST "${base}" \\\n -H "Authorization: Bearer <service_role_key>" \\\n -H "Content-Type: application/json" \\\n -d '{"name":"T-Shirt"}'`,
36
+ },
37
+ {
38
+ method: "PATCH",
39
+ path: `${base}/:id`,
40
+ description: `Update a row by primary key in ${tableName}. Requires service role key.`,
41
+ example: `curl -X PATCH "${base}/1" \\\n -H "Authorization: Bearer <service_role_key>" \\\n -H "Content-Type: application/json" \\\n -d '{"name":"Hoodie"}'`,
42
+ },
43
+ {
44
+ method: "DELETE",
45
+ path: `${base}/:id`,
46
+ description: `Delete a row by primary key from ${tableName}. Requires service role key.`,
47
+ example: `curl -X DELETE "${base}/1" \\\n -H "Authorization: Bearer <service_role_key>"`,
48
+ },
49
+ ];
50
+ }
51
+
52
+ export async function retrieveApiDocsFromApi(
53
+ orgSlug: string,
54
+ projectSlug: string,
55
+ ): Promise<ProjectApiDocs> {
56
+ const token = await retrieveTokenFromCookie();
57
+ const headers = { Cookie: `${COOKIE_KEYS.ACCESS_TOKEN}=${token}` };
58
+
59
+ try {
60
+ const [projectRes, tablesRes] = await Promise.all([
61
+ apiClient.get<ProjectBySlugResponse>(
62
+ `/orgs/${orgSlug}/projects/${projectSlug}`,
63
+ { headers },
64
+ ),
65
+ apiClient.get<string[]>(
66
+ `/orgs/${orgSlug}/projects/${projectSlug}/tables`,
67
+ { headers },
68
+ ),
69
+ ]);
70
+
71
+ const project = projectRes.data.projects;
72
+ const tableNames = tablesRes.data;
73
+
74
+ return {
75
+ projectUrl: project.projectUrl,
76
+ anonKey: project.anonKey,
77
+ serviceRoleKey: project.serviceRoleKey,
78
+ tables: tableNames.map((name) => ({
79
+ name,
80
+ endpoints: buildEndpoints(project.projectUrl, name),
81
+ })),
82
+ };
83
+ } catch {
84
+ redirect(`/organizations/${orgSlug}/projects`);
85
+ }
86
+ }
@@ -0,0 +1,67 @@
1
+ "use server";
2
+
3
+ import { redirect } from "next/navigation";
4
+ import { authServerSchema } from "./server.schema";
5
+ import { AUTH_INTENT } from "./constants";
6
+ import { applyAuthCookiesFromResponse } from "./cookies";
7
+
8
+ export type AuthActionState = {
9
+ error?: string;
10
+ fieldErrors?: Record<string, string[]>;
11
+ };
12
+
13
+ export async function authAction(
14
+ _prev: AuthActionState,
15
+ formData: FormData,
16
+ ): Promise<AuthActionState> {
17
+ const raw = Object.fromEntries(formData);
18
+ const parsed = authServerSchema.safeParse(raw);
19
+
20
+ if (!parsed.success) {
21
+ return {
22
+ fieldErrors: parsed.error.flatten().fieldErrors as Record<
23
+ string,
24
+ string[]
25
+ >,
26
+ };
27
+ }
28
+
29
+ const { intent, ...data } = parsed.data;
30
+
31
+ let res: Response;
32
+
33
+ switch (intent) {
34
+ case AUTH_INTENT.LOGIN: {
35
+ res = await fetch(`${process.env.API_URL}/auth/login`, {
36
+ method: "POST",
37
+ headers: { "Content-Type": "application/json" },
38
+ body: JSON.stringify(data),
39
+ });
40
+
41
+ if (!res.ok) {
42
+ const body = await res.json();
43
+ return { error: body.message ?? "Login failed" };
44
+ }
45
+
46
+ break;
47
+ }
48
+
49
+ case AUTH_INTENT.REGISTER: {
50
+ res = await fetch(`${process.env.API_URL}/auth/register`, {
51
+ method: "POST",
52
+ headers: { "Content-Type": "application/json" },
53
+ body: JSON.stringify(data),
54
+ });
55
+
56
+ if (!res.ok) {
57
+ const body = await res.json();
58
+ return { error: body.message ?? "Registration failed" };
59
+ }
60
+
61
+ break;
62
+ }
63
+ }
64
+
65
+ await applyAuthCookiesFromResponse(res!);
66
+ redirect("/dashboard");
67
+ }
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import type { LoginInput, RegisterInput } from "@apiDatabase/types";
3
+
4
+ export const loginSchema = z.object({
5
+ email: z.string().email("Invalid email"),
6
+ password: z.string().min(1, "Password is required"),
7
+ }) satisfies z.ZodType<LoginInput>;
8
+
9
+ export const registerSchema = z.object({
10
+ name: z.string().min(1, "Name is required"),
11
+ email: z.string().email("Invalid email"),
12
+ password: z.string().min(8, "Password must be at least 8 characters"),
13
+ }) satisfies z.ZodType<RegisterInput>;
@@ -0,0 +1,6 @@
1
+ export const AUTH_INTENT = {
2
+ LOGIN: "LOGIN",
3
+ REGISTER: "REGISTER",
4
+ } as const;
5
+
6
+ export type AuthIntent = (typeof AUTH_INTENT)[keyof typeof AUTH_INTENT];
@@ -0,0 +1,86 @@
1
+ import { COOKIE_KEYS } from "@apiDatabase/constants";
2
+ import { cookies } from "next/headers";
3
+ import type { NextResponse } from "next/server";
4
+
5
+ type ParsedCookie = {
6
+ name: string;
7
+ value: string;
8
+ options: {
9
+ path: string;
10
+ maxAge?: number;
11
+ httpOnly: boolean;
12
+ secure: boolean;
13
+ sameSite: "lax" | "strict" | "none";
14
+ };
15
+ };
16
+
17
+ export function parseSetCookieHeader(setCookie: string): ParsedCookie {
18
+ const parts = setCookie.split(";").map((part) => part.trim());
19
+ const [nameValue, ...attributes] = parts;
20
+
21
+ const separatorIndex = nameValue.indexOf("=");
22
+ const name = nameValue.slice(0, separatorIndex);
23
+ const value = nameValue.slice(separatorIndex + 1);
24
+
25
+ const options: ParsedCookie["options"] = {
26
+ path: "/",
27
+ httpOnly: false,
28
+ secure: false,
29
+ sameSite: "lax",
30
+ };
31
+
32
+ for (const attribute of attributes) {
33
+ const separator = attribute.indexOf("=");
34
+ const key = (
35
+ separator === -1 ? attribute : attribute.slice(0, separator)
36
+ ).toLowerCase();
37
+ const val = separator === -1 ? "" : attribute.slice(separator + 1);
38
+
39
+ switch (key) {
40
+ case "path":
41
+ options.path = val;
42
+ break;
43
+ case "max-age":
44
+ options.maxAge = Number.parseInt(val, 10);
45
+ break;
46
+ case "httponly":
47
+ options.httpOnly = true;
48
+ break;
49
+ case "secure":
50
+ options.secure = true;
51
+ break;
52
+ case "samesite":
53
+ options.sameSite =
54
+ val.toLowerCase() as ParsedCookie["options"]["sameSite"];
55
+ break;
56
+ }
57
+ }
58
+
59
+ return { name, value, options };
60
+ }
61
+
62
+ function isAuthCookie(name: string) {
63
+ return (
64
+ name === COOKIE_KEYS.ACCESS_TOKEN || name === COOKIE_KEYS.REFRESH_TOKEN
65
+ );
66
+ }
67
+
68
+ export async function applyAuthCookiesFromResponse(response: Response) {
69
+ const cookieStore = await cookies();
70
+ for (const header of response.headers.getSetCookie()) {
71
+ const parsed = parseSetCookieHeader(header);
72
+ if (!isAuthCookie(parsed.name)) continue;
73
+ cookieStore.set(parsed.name, parsed.value, parsed.options);
74
+ }
75
+ }
76
+
77
+ export function applyAuthCookiesToResponse(
78
+ response: NextResponse,
79
+ setCookieHeaders: string[],
80
+ ) {
81
+ for (const header of setCookieHeaders) {
82
+ const parsed = parseSetCookieHeader(header);
83
+ if (!isAuthCookie(parsed.name)) continue;
84
+ response.cookies.set(parsed.name, parsed.value, parsed.options);
85
+ }
86
+ }