fakebase-studio 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 (70) hide show
  1. package/README.md +85 -0
  2. package/bin/fakebase.js +92 -0
  3. package/package.json +44 -0
  4. package/server/dist/db.d.ts +18 -0
  5. package/server/dist/db.d.ts.map +1 -0
  6. package/server/dist/db.js +83 -0
  7. package/server/dist/db.js.map +1 -0
  8. package/server/dist/index.d.ts +2 -0
  9. package/server/dist/index.d.ts.map +1 -0
  10. package/server/dist/index.js +62 -0
  11. package/server/dist/index.js.map +1 -0
  12. package/server/dist/routes/connect.d.ts +6 -0
  13. package/server/dist/routes/connect.d.ts.map +1 -0
  14. package/server/dist/routes/connect.js +40 -0
  15. package/server/dist/routes/connect.js.map +1 -0
  16. package/server/dist/routes/connections.d.ts +3 -0
  17. package/server/dist/routes/connections.d.ts.map +1 -0
  18. package/server/dist/routes/connections.js +106 -0
  19. package/server/dist/routes/connections.js.map +1 -0
  20. package/server/dist/routes/index.d.ts +6 -0
  21. package/server/dist/routes/index.d.ts.map +1 -0
  22. package/server/dist/routes/index.js +16 -0
  23. package/server/dist/routes/index.js.map +1 -0
  24. package/server/dist/routes/indexes.d.ts +6 -0
  25. package/server/dist/routes/indexes.d.ts.map +1 -0
  26. package/server/dist/routes/indexes.js +38 -0
  27. package/server/dist/routes/indexes.js.map +1 -0
  28. package/server/dist/routes/schema.d.ts +6 -0
  29. package/server/dist/routes/schema.d.ts.map +1 -0
  30. package/server/dist/routes/schema.js +41 -0
  31. package/server/dist/routes/schema.js.map +1 -0
  32. package/server/dist/routes/tables.d.ts +6 -0
  33. package/server/dist/routes/tables.d.ts.map +1 -0
  34. package/server/dist/routes/tables.js +121 -0
  35. package/server/dist/routes/tables.js.map +1 -0
  36. package/server/dist/services/connections.service.d.ts +31 -0
  37. package/server/dist/services/connections.service.d.ts.map +1 -0
  38. package/server/dist/services/connections.service.js +125 -0
  39. package/server/dist/services/connections.service.js.map +1 -0
  40. package/server/dist/services/indexes.service.d.ts +8 -0
  41. package/server/dist/services/indexes.service.d.ts.map +1 -0
  42. package/server/dist/services/indexes.service.js +54 -0
  43. package/server/dist/services/indexes.service.js.map +1 -0
  44. package/server/dist/services/schema.service.d.ts +7 -0
  45. package/server/dist/services/schema.service.d.ts.map +1 -0
  46. package/server/dist/services/schema.service.js +165 -0
  47. package/server/dist/services/schema.service.js.map +1 -0
  48. package/server/dist/types/api.d.ts +36 -0
  49. package/server/dist/types/api.d.ts.map +1 -0
  50. package/server/dist/types/api.js +5 -0
  51. package/server/dist/types/api.js.map +1 -0
  52. package/server/dist/types/database.d.ts +29 -0
  53. package/server/dist/types/database.d.ts.map +1 -0
  54. package/server/dist/types/database.js +5 -0
  55. package/server/dist/types/database.js.map +1 -0
  56. package/server/dist/utils/errors.d.ts +15 -0
  57. package/server/dist/utils/errors.d.ts.map +1 -0
  58. package/server/dist/utils/errors.js +63 -0
  59. package/server/dist/utils/errors.js.map +1 -0
  60. package/ui/dist/assets/index-D3BM4q8p.js +70 -0
  61. package/ui/dist/assets/index-DvydtHdZ.css +1 -0
  62. package/ui/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  63. package/ui/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  64. package/ui/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  65. package/ui/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  66. package/ui/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  67. package/ui/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  68. package/ui/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  69. package/ui/dist/index.html +14 -0
  70. package/ui/dist/vite.svg +1 -0
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # fakebase-studio
2
+
3
+ A PostgreSQL database browser and schema visualizer. Connect to any PostgreSQL database and explore tables, schema relationships, and indexes with a modern UI.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx fakebase-studio
9
+ ```
10
+
11
+ This will start the server and open the browser automatically at `http://localhost:4321`.
12
+
13
+ ## CLI Options
14
+
15
+ ```bash
16
+ npx fakebase-studio [options]
17
+
18
+ Options:
19
+ --port, -p <number> Port to run the server on (default: 4321)
20
+ --host, -h <string> Host to bind to (default: 0.0.0.0)
21
+ --open Open browser automatically (default)
22
+ --no-open Don't open browser automatically
23
+ --help Show help message
24
+ ```
25
+
26
+ ### Examples
27
+
28
+ ```bash
29
+ # Run on a different port
30
+ npx fakebase-studio --port 3000
31
+
32
+ # Run without opening browser
33
+ npx fakebase-studio --no-open
34
+
35
+ # Bind to localhost only
36
+ npx fakebase-studio --host 127.0.0.1
37
+ ```
38
+
39
+ ## Features
40
+
41
+ - **Schema Visualization**: View your database schema as an interactive flow diagram
42
+ - **Table Browser**: Browse table data with sorting and filtering
43
+ - **Index Management**: View and analyze database indexes
44
+ - **Connection Management**: Save and manage multiple database connections
45
+ - **Supabase Support**: Works with Supabase PostgreSQL databases
46
+
47
+ ## Requirements
48
+
49
+ - Node.js 18 or higher
50
+ - A PostgreSQL database to connect to
51
+
52
+ ## Usage
53
+
54
+ 1. Run `npx fakebase-studio` to start the application
55
+ 2. Enter your PostgreSQL connection string in the UI
56
+ 3. Explore your database schema, tables, and indexes
57
+
58
+ ## Connection String Format
59
+
60
+ ```
61
+ postgresql://user:password@host:port/database
62
+ ```
63
+
64
+ For Supabase:
65
+ ```
66
+ postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
67
+ ```
68
+
69
+ ## Development
70
+
71
+ ```bash
72
+ # Install dependencies
73
+ npm install
74
+ cd ui && npm install && cd ..
75
+
76
+ # Start development server (with hot reload)
77
+ npm run dev
78
+
79
+ # Build for production
80
+ npm run build
81
+ ```
82
+
83
+ ## License
84
+
85
+ MIT
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from "node:child_process";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const pkgRoot = path.resolve(__dirname, "..");
10
+
11
+ // Parse CLI arguments
12
+ const args = process.argv.slice(2);
13
+ let port = 4321;
14
+ let host = "0.0.0.0";
15
+ let openBrowser = true;
16
+
17
+ for (let i = 0; i < args.length; i++) {
18
+ const arg = args[i];
19
+ if (arg === "--port" || arg === "-p") {
20
+ const value = args[++i];
21
+ if (value) port = parseInt(value, 10);
22
+ } else if (arg.startsWith("--port=")) {
23
+ port = parseInt(arg.split("=")[1], 10);
24
+ } else if (arg === "--host" || arg === "-h") {
25
+ const value = args[++i];
26
+ if (value) host = value;
27
+ } else if (arg.startsWith("--host=")) {
28
+ host = arg.split("=")[1];
29
+ } else if (arg === "--no-open") {
30
+ openBrowser = false;
31
+ } else if (arg === "--open") {
32
+ openBrowser = true;
33
+ } else if (arg === "--help") {
34
+ console.log(`
35
+ fakebase-studio - PostgreSQL database browser
36
+
37
+ Usage:
38
+ npx fakebase-studio [options]
39
+
40
+ Options:
41
+ --port, -p <number> Port to run the server on (default: 4321)
42
+ --host, -h <string> Host to bind to (default: 0.0.0.0)
43
+ --open Open browser automatically (default)
44
+ --no-open Don't open browser automatically
45
+ --help Show this help message
46
+ `);
47
+ process.exit(0);
48
+ }
49
+ }
50
+
51
+ const serverEntry = path.join(pkgRoot, "server", "dist", "index.js");
52
+
53
+ console.log(`Starting fakebase-studio on http://localhost:${port}`);
54
+
55
+ const child = spawn(process.execPath, [serverEntry], {
56
+ cwd: pkgRoot,
57
+ stdio: "inherit",
58
+ env: {
59
+ ...process.env,
60
+ NODE_ENV: "production",
61
+ PORT: String(port),
62
+ HOST: host,
63
+ },
64
+ });
65
+
66
+ // Open browser after a short delay to let the server start
67
+ if (openBrowser) {
68
+ setTimeout(() => {
69
+ const url = `http://localhost:${port}`;
70
+ const openCommand =
71
+ process.platform === "darwin"
72
+ ? "open"
73
+ : process.platform === "win32"
74
+ ? "start"
75
+ : "xdg-open";
76
+
77
+ spawn(openCommand, [url], { stdio: "ignore", shell: true });
78
+ }, 1500);
79
+ }
80
+
81
+ child.on("error", (error) => {
82
+ console.error("Failed to start server:", error.message);
83
+ process.exit(1);
84
+ });
85
+
86
+ child.on("exit", (code) => {
87
+ process.exit(code ?? 0);
88
+ });
89
+
90
+ // Forward signals to child process
91
+ process.on("SIGINT", () => child.kill("SIGINT"));
92
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "fakebase-studio",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "fakebase-studio": "bin/fakebase.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "server/dist",
11
+ "ui/dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "scripts": {
19
+ "dev": "concurrently -n server,ui -c blue,green \"npm run dev:server\" \"npm run dev:ui\"",
20
+ "dev:server": "tsx watch server/index.ts",
21
+ "dev:ui": "cd ui && npm run dev",
22
+ "build:server": "tsc -p server/tsconfig.json",
23
+ "build:ui": "npm ci --prefix ui && npm run build --prefix ui",
24
+ "build": "npm run build:server && npm run build:ui",
25
+ "prepack": "npm run build"
26
+ },
27
+ "dependencies": {
28
+ "@fastify/cors": "^11.2.0",
29
+ "@fastify/static": "^9.0.0",
30
+ "class-variance-authority": "^0.7.1",
31
+ "clsx": "^2.1.1",
32
+ "fastify": "^5.7.2",
33
+ "lucide-react": "^0.563.0",
34
+ "pg": "^8.17.2",
35
+ "tailwind-merge": "^3.4.0",
36
+ "tw-animate-css": "^1.4.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/pg": "^8.16.0",
40
+ "concurrently": "^9.1.2",
41
+ "tsx": "^4.21.0",
42
+ "typescript": "^5.4.0"
43
+ }
44
+ }
@@ -0,0 +1,18 @@
1
+ import { Pool } from "pg";
2
+ /**
3
+ * Get or create a connection pool for the given connection string.
4
+ * Pools are cached and reused for the same connection string.
5
+ */
6
+ export declare function getOrCreatePool(connectionString: string): Pool;
7
+ /**
8
+ * Connect to database and test the connection.
9
+ * This is used by the /api/connect endpoint to validate connections.
10
+ * For actual queries, use getOrCreatePool() instead.
11
+ */
12
+ export declare function connectToDatabase(connectionString: string): Promise<any>;
13
+ /**
14
+ * Get the legacy single pool (for backward compatibility).
15
+ * @deprecated Use getOrCreatePool(connectionString) instead.
16
+ */
17
+ export declare function getDb(): Pool;
18
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAQ1B;;;GAGG;AACH,wBAAgB,eAAe,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAsB9D;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,gBAwC/D;AAED;;;GAGG;AACH,wBAAgB,KAAK,SAGpB"}
@@ -0,0 +1,83 @@
1
+ import { Pool } from "pg";
2
+ // Cache connection pools per connection string
3
+ const connectionPools = new Map();
4
+ // Legacy single pool for backward compatibility with connect endpoint
5
+ let pool = null;
6
+ /**
7
+ * Get or create a connection pool for the given connection string.
8
+ * Pools are cached and reused for the same connection string.
9
+ */
10
+ export function getOrCreatePool(connectionString) {
11
+ if (!connectionPools.has(connectionString)) {
12
+ // Parse connection string to check if it's Supabase or needs SSL
13
+ const isSupabase = connectionString.includes("supabase.co");
14
+ const newPool = new Pool({
15
+ connectionString,
16
+ // Supabase requires SSL connections
17
+ ssl: isSupabase ? { rejectUnauthorized: false } : undefined,
18
+ });
19
+ // Handle pool errors
20
+ newPool.on("error", (err) => {
21
+ console.error("Unexpected error on idle client", err);
22
+ // Remove pool from cache on error
23
+ connectionPools.delete(connectionString);
24
+ });
25
+ connectionPools.set(connectionString, newPool);
26
+ }
27
+ return connectionPools.get(connectionString);
28
+ }
29
+ /**
30
+ * Connect to database and test the connection.
31
+ * This is used by the /api/connect endpoint to validate connections.
32
+ * For actual queries, use getOrCreatePool() instead.
33
+ */
34
+ export async function connectToDatabase(connectionString) {
35
+ // Close existing pool if any (legacy behavior for connect endpoint)
36
+ if (pool) {
37
+ try {
38
+ await pool.end();
39
+ }
40
+ catch (error) {
41
+ // Ignore errors when closing old pool
42
+ console.warn("Error closing existing pool:", error);
43
+ }
44
+ pool = null;
45
+ }
46
+ // Parse connection string to check if it's Supabase or needs SSL
47
+ const isSupabase = connectionString.includes("supabase.co");
48
+ pool = new Pool({
49
+ connectionString,
50
+ // Supabase requires SSL connections
51
+ ssl: isSupabase ? { rejectUnauthorized: false } : undefined,
52
+ });
53
+ try {
54
+ // Test connection
55
+ await pool.query("SELECT 1");
56
+ // Get database name
57
+ const result = await pool.query("SELECT current_database() as name");
58
+ return result.rows[0].name;
59
+ }
60
+ catch (error) {
61
+ // Clean up pool on error
62
+ if (pool) {
63
+ try {
64
+ await pool.end();
65
+ }
66
+ catch (closeError) {
67
+ // Ignore cleanup errors
68
+ }
69
+ pool = null;
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+ /**
75
+ * Get the legacy single pool (for backward compatibility).
76
+ * @deprecated Use getOrCreatePool(connectionString) instead.
77
+ */
78
+ export function getDb() {
79
+ if (!pool)
80
+ throw new Error("Database not connected");
81
+ return pool;
82
+ }
83
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAE1B,+CAA+C;AAC/C,MAAM,eAAe,GAAG,IAAI,GAAG,EAAgB,CAAC;AAEhD,sEAAsE;AACtE,IAAI,IAAI,GAAgB,IAAI,CAAC;AAE7B;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,gBAAwB;IACtD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC3C,iEAAiE;QACjE,MAAM,UAAU,GAAG,gBAAgB,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAE5D,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC;YACvB,gBAAgB;YAChB,oCAAoC;YACpC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;SAC5D,CAAC,CAAC;QAEH,qBAAqB;QACrB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAC1B,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;YACtD,kCAAkC;YAClC,eAAe,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,eAAe,CAAC,GAAG,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,eAAe,CAAC,GAAG,CAAC,gBAAgB,CAAE,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,gBAAwB;IAC9D,oEAAoE;IACpE,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sCAAsC;YACtC,OAAO,CAAC,IAAI,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;IAED,iEAAiE;IACjE,MAAM,UAAU,GAAG,gBAAgB,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAE5D,IAAI,GAAG,IAAI,IAAI,CAAC;QACd,gBAAgB;QAChB,oCAAoC;QACpC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;KAC5D,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,kBAAkB;QAClB,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAE7B,oBAAoB;QACpB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACrE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yBAAyB;QACzB,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;YACnB,CAAC;YAAC,OAAO,UAAU,EAAE,CAAC;gBACpB,wBAAwB;YAC1B,CAAC;YACD,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK;IACnB,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function startServer(): Promise<void>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAUA,wBAAsB,WAAW,kBAsDhC"}
@@ -0,0 +1,62 @@
1
+ import Fastify from "fastify";
2
+ import fastifyStatic from "@fastify/static";
3
+ import fastifyCors from "@fastify/cors";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { registerRoutes } from "./routes/index.js";
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ export async function startServer() {
10
+ const app = Fastify({
11
+ logger: true,
12
+ });
13
+ // Global error handler
14
+ app.setErrorHandler((error, request, reply) => {
15
+ request.log.error(error, "Unhandled error");
16
+ reply.status(500).send({
17
+ success: false,
18
+ error: error instanceof Error ? error.message : "Internal server error",
19
+ });
20
+ });
21
+ // Enable CORS for development
22
+ await app.register(fastifyCors, {
23
+ origin: true,
24
+ });
25
+ // Register API routes
26
+ await registerRoutes(app);
27
+ // Serve static files in production
28
+ // Use process.cwd() since the CLI sets cwd to the package root
29
+ const isProduction = process.env.NODE_ENV === "production";
30
+ const staticRoot = isProduction
31
+ ? path.join(process.cwd(), "ui/dist")
32
+ : path.join(__dirname, "../ui/dist");
33
+ if (isProduction) {
34
+ await app.register(fastifyStatic, {
35
+ root: staticRoot,
36
+ wildcard: false, // We'll handle SPA fallback manually
37
+ });
38
+ // SPA fallback: serve index.html for non-API routes (React Router support)
39
+ app.setNotFoundHandler((request, reply) => {
40
+ // Return 404 for API routes
41
+ if (request.url.startsWith("/api/")) {
42
+ return reply.status(404).send({
43
+ success: false,
44
+ error: "Not found",
45
+ });
46
+ }
47
+ // Serve index.html for all other routes (SPA)
48
+ return reply.sendFile("index.html");
49
+ });
50
+ }
51
+ // Start server
52
+ const port = Number(process.env.PORT) || 4321;
53
+ const host = process.env.HOST || "0.0.0.0";
54
+ await app.listen({ port, host });
55
+ console.log(`Server running: http://localhost:${port}`);
56
+ }
57
+ // Start server when this file is executed directly
58
+ startServer().catch((error) => {
59
+ console.error("Failed to start server:", error);
60
+ process.exit(1);
61
+ });
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ /**
3
+ * Register connection-related routes.
4
+ */
5
+ export declare function connectRoutes(app: FastifyInstance): Promise<void>;
6
+ //# sourceMappingURL=connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../routes/connect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAK/C;;GAEG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAwCvE"}
@@ -0,0 +1,40 @@
1
+ import { connectToDatabase } from "../db.js";
2
+ import { sendError, sendSuccess } from "../utils/errors.js";
3
+ /**
4
+ * Register connection-related routes.
5
+ */
6
+ export async function connectRoutes(app) {
7
+ // Test endpoint
8
+ app.get("/api/test", async () => {
9
+ return { status: "ok", message: "Server is running" };
10
+ });
11
+ // Connect to database
12
+ app.post("/api/connect", {
13
+ schema: {
14
+ body: {
15
+ type: "object",
16
+ required: ["connectionString"],
17
+ properties: {
18
+ connectionString: { type: "string" },
19
+ },
20
+ },
21
+ },
22
+ }, async (request, reply) => {
23
+ const { connectionString } = request.body;
24
+ if (!connectionString) {
25
+ return sendError(reply, 400, new Error("Connection string is required"));
26
+ }
27
+ try {
28
+ request.log.info("Attempting to connect to database...");
29
+ const databaseName = await connectToDatabase(connectionString);
30
+ request.log.info({ databaseName }, "Successfully connected to database");
31
+ const data = { databaseName };
32
+ return sendSuccess(reply, data);
33
+ }
34
+ catch (error) {
35
+ request.log.error(error, "Error connecting to database");
36
+ return sendError(reply, 500, error);
37
+ }
38
+ });
39
+ }
40
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../../routes/connect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAG5D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAoB;IACpD,gBAAgB;IAChB,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QAC5B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,sBAAsB;IACtB,GAAG,CAAC,IAAI,CACJ,cAAc,EACd;QACI,MAAM,EAAE;YACJ,IAAI,EAAE;gBACF,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,CAAC,kBAAkB,CAAC;gBAC9B,UAAU,EAAE;oBACR,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBACvC;aACJ;SACJ;KACJ,EACD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACrB,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;QAE1C,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACpB,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QAC7E,CAAC;QAED,IAAI,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;YACzD,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;YAC/D,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,EAAE,oCAAoC,CAAC,CAAC;YAEzE,MAAM,IAAI,GAAuB,EAAE,YAAY,EAAE,CAAC;YAClD,OAAO,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,8BAA8B,CAAC,CAAC;YACzD,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACxC,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { FastifyInstance } from "fastify";
2
+ export declare function connectionsRoutes(fastify: FastifyInstance): Promise<void>;
3
+ //# sourceMappingURL=connections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../routes/connections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAS1C,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,eAAe,iBAyI/D"}
@@ -0,0 +1,106 @@
1
+ import { loadConnections, saveConnection, removeConnectionById, updateConnectionName, } from "../services/connections.service.js";
2
+ import { sendError } from "../utils/errors.js";
3
+ export async function connectionsRoutes(fastify) {
4
+ // Get all connections
5
+ fastify.get("/api/connections", async (request, reply) => {
6
+ try {
7
+ const config = await loadConnections();
8
+ return reply.send({
9
+ success: true,
10
+ data: config.connections,
11
+ });
12
+ }
13
+ catch (error) {
14
+ return sendError(reply, 500, error);
15
+ }
16
+ });
17
+ // Save/update a connection
18
+ fastify.post("/api/connections", {
19
+ schema: {
20
+ body: {
21
+ type: "object",
22
+ required: ["connectionString"],
23
+ properties: {
24
+ connectionString: { type: "string" },
25
+ success: { type: "boolean" },
26
+ databaseName: { type: "string" },
27
+ name: { type: "string" },
28
+ },
29
+ },
30
+ },
31
+ }, async (request, reply) => {
32
+ const { connectionString, success, databaseName, name } = request.body;
33
+ if (!connectionString) {
34
+ return sendError(reply, 400, "Connection string is required");
35
+ }
36
+ try {
37
+ const connection = await saveConnection(connectionString, success ?? false, databaseName, name);
38
+ request.log.info(`Saved connection: ${name || databaseName || "unnamed"}`);
39
+ return reply.send({
40
+ success: true,
41
+ data: connection,
42
+ });
43
+ }
44
+ catch (error) {
45
+ return sendError(reply, 500, error);
46
+ }
47
+ });
48
+ // Delete a connection by id (avoids sending connection string in request)
49
+ fastify.delete("/api/connections/:id", {
50
+ schema: {
51
+ params: {
52
+ type: "object",
53
+ required: ["id"],
54
+ properties: {
55
+ id: { type: "string" },
56
+ },
57
+ },
58
+ },
59
+ }, async (request, reply) => {
60
+ const { id } = request.params;
61
+ if (!id) {
62
+ return sendError(reply, 400, "Connection id is required");
63
+ }
64
+ try {
65
+ const wasRemoved = await removeConnectionById(id);
66
+ if (!wasRemoved) {
67
+ return sendError(reply, 404, "Connection not found");
68
+ }
69
+ request.log.info("Removed connection");
70
+ return reply.send({ success: true });
71
+ }
72
+ catch (error) {
73
+ return sendError(reply, 500, error);
74
+ }
75
+ });
76
+ // Update connection name
77
+ fastify.patch("/api/connections", {
78
+ schema: {
79
+ body: {
80
+ type: "object",
81
+ required: ["connectionString", "name"],
82
+ properties: {
83
+ connectionString: { type: "string" },
84
+ name: { type: "string" },
85
+ },
86
+ },
87
+ },
88
+ }, async (request, reply) => {
89
+ const { connectionString, name } = request.body;
90
+ try {
91
+ const connection = await updateConnectionName(connectionString, name);
92
+ if (!connection) {
93
+ return sendError(reply, 404, "Connection not found");
94
+ }
95
+ request.log.info(`Updated connection name to: ${name}`);
96
+ return reply.send({
97
+ success: true,
98
+ data: connection,
99
+ });
100
+ }
101
+ catch (error) {
102
+ return sendError(reply, 500, error);
103
+ }
104
+ });
105
+ }
106
+ //# sourceMappingURL=connections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.js","sourceRoot":"","sources":["../../routes/connections.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ /**
3
+ * Register all API routes.
4
+ */
5
+ export declare function registerRoutes(app: FastifyInstance): Promise<void>;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAO/C;;GAEG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxE"}
@@ -0,0 +1,16 @@
1
+ import { connectRoutes } from "./connect.js";
2
+ import { schemaRoutes } from "./schema.js";
3
+ import { connectionsRoutes } from "./connections.js";
4
+ import { tableDataRoutes } from "./tables.js";
5
+ import { indexesRoutes } from "./indexes.js";
6
+ /**
7
+ * Register all API routes.
8
+ */
9
+ export async function registerRoutes(app) {
10
+ await connectRoutes(app);
11
+ await schemaRoutes(app);
12
+ await connectionsRoutes(app);
13
+ await tableDataRoutes(app);
14
+ await indexesRoutes(app);
15
+ }
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../routes/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAoB;IACvD,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;IACxB,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ /**
3
+ * Register indexes routes.
4
+ */
5
+ export declare function indexesRoutes(app: FastifyInstance): Promise<void>;
6
+ //# sourceMappingURL=indexes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexes.d.ts","sourceRoot":"","sources":["../../routes/indexes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAS/C;;GAEG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAyCvE"}