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
@@ -0,0 +1,38 @@
1
+ import { getOrCreatePool } from "../db.js";
2
+ import { sendError, sendSuccess } from "../utils/errors.js";
3
+ import { getIndexesList } from "../services/indexes.service.js";
4
+ /**
5
+ * Register indexes routes.
6
+ */
7
+ export async function indexesRoutes(app) {
8
+ const bodySchema = {
9
+ type: "object",
10
+ required: ["connectionString"],
11
+ properties: {
12
+ connectionString: { type: "string" },
13
+ },
14
+ };
15
+ /**
16
+ * POST /api/indexes/list
17
+ * Returns all indexes with columns, size, and scan statistics
18
+ */
19
+ app.post("/api/indexes/list", { schema: { body: bodySchema } }, async (request, reply) => {
20
+ const { connectionString } = request.body;
21
+ if (!connectionString) {
22
+ return sendError(reply, 400, new Error("Connection string is required"));
23
+ }
24
+ try {
25
+ const pool = getOrCreatePool(connectionString);
26
+ await pool.query("SELECT 1 as test");
27
+ request.log.info("Fetching indexes list...");
28
+ const data = await getIndexesList(pool);
29
+ request.log.info({ indexCount: data.indexes.length, schemaCount: data.schemas.length }, "Indexes list fetched");
30
+ return sendSuccess(reply, data);
31
+ }
32
+ catch (error) {
33
+ request.log.error(error, "Error fetching indexes list");
34
+ return sendError(reply, 500, error);
35
+ }
36
+ });
37
+ }
38
+ //# sourceMappingURL=indexes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexes.js","sourceRoot":"","sources":["../../routes/indexes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAMhE;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAoB;IACtD,MAAM,UAAU,GAAG;QACjB,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,CAAC,kBAAkB,CAAC;QAC9B,UAAU,EAAE;YACV,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SACrC;KACF,CAAC;IAEF;;;OAGG;IACH,GAAG,CAAC,IAAI,CACN,mBAAmB,EACnB,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAChC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;QAE1C,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QAC3E,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;YAC/C,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YAErC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;YAC7C,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,IAAI,CACd,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EACrE,sBAAsB,CACvB,CAAC;YAEF,OAAO,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAC;YACxD,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ /**
3
+ * Register schema-related routes.
4
+ */
5
+ export declare function schemaRoutes(app: FastifyInstance): Promise<void>;
6
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../routes/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAM/C;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA6CtE"}
@@ -0,0 +1,41 @@
1
+ import { getOrCreatePool } from "../db.js";
2
+ import { getSchemaData } from "../services/schema.service.js";
3
+ import { sendError, sendSuccess } from "../utils/errors.js";
4
+ /**
5
+ * Register schema-related routes.
6
+ */
7
+ export async function schemaRoutes(app) {
8
+ app.post("/api/schema", {
9
+ schema: {
10
+ body: {
11
+ type: "object",
12
+ required: ["connectionString"],
13
+ properties: {
14
+ connectionString: { type: "string" },
15
+ },
16
+ },
17
+ },
18
+ }, async (request, reply) => {
19
+ const { connectionString } = request.body;
20
+ if (!connectionString) {
21
+ return sendError(reply, 400, new Error("Connection string is required"));
22
+ }
23
+ try {
24
+ request.log.info("Fetching schema data...");
25
+ // Get or create pool for this connection string
26
+ const pool = getOrCreatePool(connectionString);
27
+ // Test the pool connection first
28
+ request.log.debug("Testing pool connection...");
29
+ await pool.query("SELECT 1 as test");
30
+ // Get schema data
31
+ const schemaData = await getSchemaData(pool);
32
+ request.log.info({ tableCount: schemaData.tables.length, fkCount: schemaData.foreignKeys.length }, "Successfully fetched schema data");
33
+ return sendSuccess(reply, schemaData);
34
+ }
35
+ catch (error) {
36
+ request.log.error(error, "Error fetching schema data");
37
+ return sendError(reply, 500, error);
38
+ }
39
+ });
40
+ }
41
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../routes/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAG5D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAoB;IACrD,GAAG,CAAC,IAAI,CACN,aAAa,EACb;QACE,MAAM,EAAE;YACN,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,CAAC,kBAAkB,CAAC;gBAC9B,UAAU,EAAE;oBACV,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBACrC;aACF;SACF;KACF,EACD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;QAE1C,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QAC3E,CAAC;QAED,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;YAE5C,gDAAgD;YAChD,MAAM,IAAI,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;YAE/C,iCAAiC;YACjC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAChD,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YAErC,kBAAkB;YAClB,MAAM,UAAU,GAAe,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,IAAI,CACd,EAAE,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,EAChF,kCAAkC,CACnC,CAAC;YAEF,OAAO,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC;YACvD,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ /**
3
+ * Register table data routes.
4
+ */
5
+ export declare function tableDataRoutes(app: FastifyInstance): Promise<void>;
6
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../routes/tables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAuB/C;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA+IzE"}
@@ -0,0 +1,121 @@
1
+ import { getOrCreatePool } from "../db.js";
2
+ import { sendError, sendSuccess } from "../utils/errors.js";
3
+ /** Allow only safe identifier characters to prevent SQL injection */
4
+ const SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
5
+ const DEFAULT_ROW_LIMIT = 1000;
6
+ const MAX_ROW_LIMIT = 10_000;
7
+ /**
8
+ * Register table data routes.
9
+ */
10
+ export async function tableDataRoutes(app) {
11
+ app.post("/api/tables/data", {
12
+ schema: {
13
+ body: {
14
+ type: "object",
15
+ required: ["connectionString", "schema", "table"],
16
+ properties: {
17
+ connectionString: { type: "string" },
18
+ schema: { type: "string" },
19
+ table: { type: "string" },
20
+ limit: { type: "number" },
21
+ },
22
+ },
23
+ },
24
+ }, async (request, reply) => {
25
+ const { connectionString, schema, table, limit: limitParam } = request.body;
26
+ if (!connectionString) {
27
+ return sendError(reply, 400, new Error("Connection string is required"));
28
+ }
29
+ if (!schema || !SAFE_IDENTIFIER.test(schema)) {
30
+ return sendError(reply, 400, new Error("Invalid or missing schema name"));
31
+ }
32
+ if (!table || !SAFE_IDENTIFIER.test(table)) {
33
+ return sendError(reply, 400, new Error("Invalid or missing table name"));
34
+ }
35
+ const limit = Math.min(Math.max(1, limitParam ?? DEFAULT_ROW_LIMIT), MAX_ROW_LIMIT);
36
+ try {
37
+ const pool = getOrCreatePool(connectionString);
38
+ await pool.query("SELECT 1 as test");
39
+ // Identifiers are validated; safe to quote and interpolate
40
+ const quotedSchema = `"${schema.replace(/"/g, '""')}"`;
41
+ const quotedTable = `"${table.replace(/"/g, '""')}"`;
42
+ const sql = `SELECT * FROM ${quotedSchema}.${quotedTable} LIMIT $1`;
43
+ request.log.info({ schema, table, limit }, "Fetching table data");
44
+ const result = await pool.query(sql, [limit]);
45
+ const rows = result.rows;
46
+ request.log.info({ rowCount: rows.length }, "Table data fetched");
47
+ return sendSuccess(reply, rows);
48
+ }
49
+ catch (error) {
50
+ request.log.error(error, "Error fetching table data");
51
+ return sendError(reply, 500, error);
52
+ }
53
+ });
54
+ /**
55
+ * Get RLS (Row Level Security) info for a table
56
+ */
57
+ app.post("/api/tables/rls", {
58
+ schema: {
59
+ body: {
60
+ type: "object",
61
+ required: ["connectionString", "schema", "table"],
62
+ properties: {
63
+ connectionString: { type: "string" },
64
+ schema: { type: "string" },
65
+ table: { type: "string" },
66
+ },
67
+ },
68
+ },
69
+ }, async (request, reply) => {
70
+ const { connectionString, schema, table } = request.body;
71
+ if (!connectionString) {
72
+ return sendError(reply, 400, new Error("Connection string is required"));
73
+ }
74
+ if (!schema || !SAFE_IDENTIFIER.test(schema)) {
75
+ return sendError(reply, 400, new Error("Invalid or missing schema name"));
76
+ }
77
+ if (!table || !SAFE_IDENTIFIER.test(table)) {
78
+ return sendError(reply, 400, new Error("Invalid or missing table name"));
79
+ }
80
+ try {
81
+ const pool = getOrCreatePool(connectionString);
82
+ // Check if RLS is enabled on the table
83
+ const quotedSchema = `"${schema.replace(/"/g, '""')}"`;
84
+ const quotedTable = `"${table.replace(/"/g, '""')}"`;
85
+ const rlsStatusResult = await pool.query(`SELECT relrowsecurity, relforcerowsecurity
86
+ FROM pg_class
87
+ WHERE oid = $1::regclass`, [`${quotedSchema}.${quotedTable}`]);
88
+ const rlsStatus = rlsStatusResult.rows[0] ?? { relrowsecurity: false, relforcerowsecurity: false };
89
+ // Get RLS policies
90
+ const policiesResult = await pool.query(`SELECT
91
+ policyname,
92
+ permissive,
93
+ roles,
94
+ cmd,
95
+ qual,
96
+ with_check
97
+ FROM pg_policies
98
+ WHERE schemaname = $1 AND tablename = $2
99
+ ORDER BY policyname`, [schema, table]);
100
+ const policies = policiesResult.rows.map((row) => ({
101
+ name: row.policyname,
102
+ permissive: row.permissive,
103
+ roles: row.roles,
104
+ command: row.cmd,
105
+ using: row.qual,
106
+ withCheck: row.with_check,
107
+ }));
108
+ request.log.info({ schema, table, policyCount: policies.length }, "Fetched RLS info");
109
+ return sendSuccess(reply, {
110
+ enabled: rlsStatus.relrowsecurity,
111
+ forced: rlsStatus.relforcerowsecurity,
112
+ policies,
113
+ });
114
+ }
115
+ catch (error) {
116
+ request.log.error(error, "Error fetching RLS info");
117
+ return sendError(reply, 500, error);
118
+ }
119
+ });
120
+ }
121
+ //# sourceMappingURL=tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.js","sourceRoot":"","sources":["../../routes/tables.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE5D,qEAAqE;AACrE,MAAM,eAAe,GAAG,0BAA0B,CAAC;AAEnD,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAC/B,MAAM,aAAa,GAAG,MAAM,CAAC;AAe7B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAoB;IACxD,GAAG,CAAC,IAAI,CACN,kBAAkB,EAClB;QACE,MAAM,EAAE;YACN,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,CAAC,kBAAkB,EAAE,QAAQ,EAAE,OAAO,CAAC;gBACjD,UAAU,EAAE;oBACV,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACpC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC1B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACzB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC1B;aACF;SACF;KACF,EACD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;QAE5E,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7C,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,CAAC,KAAK,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3C,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,IAAI,iBAAiB,CAAC,EAC5C,aAAa,CACd,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;YAC/C,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YAErC,2DAA2D;YAC3D,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;YACvD,MAAM,WAAW,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;YACrD,MAAM,GAAG,GAAG,iBAAiB,YAAY,IAAI,WAAW,WAAW,CAAC;YAEpE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,qBAAqB,CAAC,CAAC;YAElE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAiC,CAAC;YAEtD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAC;YAElE,OAAO,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,2BAA2B,CAAC,CAAC;YACtD,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CACF,CAAC;IAEF;;OAEG;IACH,GAAG,CAAC,IAAI,CACN,iBAAiB,EACjB;QACE,MAAM,EAAE;YACN,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,CAAC,kBAAkB,EAAE,QAAQ,EAAE,OAAO,CAAC;gBACjD,UAAU,EAAE;oBACV,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACpC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC1B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC1B;aACF;SACF;KACF,EACD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;QAEzD,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7C,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,CAAC,KAAK,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3C,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QAC3E,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;YAE/C,uCAAuC;YACvC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;YACvD,MAAM,WAAW,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;YAErD,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,KAAK,CACtC;;oCAE0B,EAC1B,CAAC,GAAG,YAAY,IAAI,WAAW,EAAE,CAAC,CACnC,CAAC;YAEF,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC;YAEnG,mBAAmB;YACnB,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,KAAK,CACrC;;;;;;;;;8BASoB,EACpB,CAAC,MAAM,EAAE,KAAK,CAAC,CAChB,CAAC;YAEF,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBACjD,IAAI,EAAE,GAAG,CAAC,UAAoB;gBAC9B,UAAU,EAAE,GAAG,CAAC,UAA0C;gBAC1D,KAAK,EAAE,GAAG,CAAC,KAAiB;gBAC5B,OAAO,EAAE,GAAG,CAAC,GAAwD;gBACrE,KAAK,EAAE,GAAG,CAAC,IAAqB;gBAChC,SAAS,EAAE,GAAG,CAAC,UAA2B;aAC3C,CAAC,CAAC,CAAC;YAEJ,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC;YAEtF,OAAO,WAAW,CAAC,KAAK,EAAE;gBACxB,OAAO,EAAE,SAAS,CAAC,cAAyB;gBAC5C,MAAM,EAAE,SAAS,CAAC,mBAA8B;gBAChD,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,yBAAyB,CAAC,CAAC;YACpD,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ export interface ConnectionConfig {
2
+ id: string;
3
+ connectionString: string;
4
+ name?: string;
5
+ timestamp: number;
6
+ lastUsed?: number;
7
+ success?: boolean;
8
+ databaseName?: string;
9
+ }
10
+ export interface ConnectionsConfig {
11
+ connections: ConnectionConfig[];
12
+ }
13
+ /**
14
+ * Loads connections from the config file.
15
+ * Ensures every connection has an id (adds one for legacy entries).
16
+ */
17
+ export declare function loadConnections(): Promise<ConnectionsConfig>;
18
+ /**
19
+ * Saves or updates a connection.
20
+ */
21
+ export declare function saveConnection(connectionString: string, success?: boolean, databaseName?: string, name?: string): Promise<ConnectionConfig>;
22
+ /**
23
+ * Removes a connection by id.
24
+ * Returns true if a connection was removed, false if not found.
25
+ */
26
+ export declare function removeConnectionById(id: string): Promise<boolean>;
27
+ /**
28
+ * Updates a connection's name.
29
+ */
30
+ export declare function updateConnectionName(connectionString: string, name: string): Promise<ConnectionConfig | null>;
31
+ //# sourceMappingURL=connections.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.service.d.ts","sourceRoot":"","sources":["../../services/connections.service.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC;AAsCD;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAgBlE;AAUD;;GAEG;AACH,wBAAsB,cAAc,CAClC,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,OAAe,EACxB,YAAY,CAAC,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,CAAC,CAoC3B;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASvE;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,MAAM,EACxB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAalC"}
@@ -0,0 +1,125 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ import os from "os";
4
+ import crypto from "crypto";
5
+ const CONFIG_DIR = path.join(os.homedir(), ".fakebase-studio");
6
+ const CONFIG_FILE = path.join(CONFIG_DIR, "connections.json");
7
+ /**
8
+ * Ensures the config directory exists.
9
+ */
10
+ async function ensureConfigDir() {
11
+ try {
12
+ await fs.mkdir(CONFIG_DIR, { recursive: true });
13
+ }
14
+ catch (error) {
15
+ // Directory might already exist
16
+ }
17
+ }
18
+ /** Generate a stable id for a connection string (for legacy entries without id). */
19
+ function stableIdFromConnectionString(connectionString) {
20
+ return "legacy-" + crypto.createHash("sha256").update(connectionString.trim()).digest("hex").slice(0, 12);
21
+ }
22
+ /**
23
+ * Ensures every connection has an id (migrates legacy configs).
24
+ */
25
+ function ensureConnectionIds(connections) {
26
+ return connections.map((conn) => {
27
+ if (conn.id) {
28
+ return conn;
29
+ }
30
+ return {
31
+ ...conn,
32
+ id: stableIdFromConnectionString(conn.connectionString),
33
+ };
34
+ });
35
+ }
36
+ /**
37
+ * Loads connections from the config file.
38
+ * Ensures every connection has an id (adds one for legacy entries).
39
+ */
40
+ export async function loadConnections() {
41
+ try {
42
+ await ensureConfigDir();
43
+ const data = await fs.readFile(CONFIG_FILE, "utf-8");
44
+ const parsed = JSON.parse(data);
45
+ const raw = parsed.connections || [];
46
+ const hadMissingId = raw.length > 0 && raw.some((c) => !c.id);
47
+ const connections = ensureConnectionIds(raw);
48
+ if (hadMissingId) {
49
+ await saveConnectionsConfig({ connections });
50
+ }
51
+ return { connections };
52
+ }
53
+ catch (error) {
54
+ // File doesn't exist or is invalid, return empty config
55
+ return { connections: [] };
56
+ }
57
+ }
58
+ /**
59
+ * Saves the connections config to file.
60
+ */
61
+ async function saveConnectionsConfig(config) {
62
+ await ensureConfigDir();
63
+ await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
64
+ }
65
+ /**
66
+ * Saves or updates a connection.
67
+ */
68
+ export async function saveConnection(connectionString, success = false, databaseName, name) {
69
+ const config = await loadConnections();
70
+ // Check if connection string already exists
71
+ const existingIndex = config.connections.findIndex((conn) => conn.connectionString === connectionString);
72
+ const existing = existingIndex >= 0 ? config.connections[existingIndex] : null;
73
+ const connectionConfig = {
74
+ id: existing?.id ?? crypto.randomUUID(),
75
+ connectionString,
76
+ name: name || existing?.name,
77
+ timestamp: existing?.timestamp ?? Date.now(),
78
+ lastUsed: Date.now(),
79
+ success,
80
+ databaseName,
81
+ };
82
+ if (existingIndex >= 0) {
83
+ config.connections[existingIndex] = {
84
+ ...existing,
85
+ ...connectionConfig,
86
+ id: existing.id,
87
+ name: name ?? existing.name,
88
+ };
89
+ }
90
+ else {
91
+ config.connections.push(connectionConfig);
92
+ }
93
+ // Sort by last used (most recent first)
94
+ config.connections.sort((a, b) => (b.lastUsed || 0) - (a.lastUsed || 0));
95
+ await saveConnectionsConfig(config);
96
+ return connectionConfig;
97
+ }
98
+ /**
99
+ * Removes a connection by id.
100
+ * Returns true if a connection was removed, false if not found.
101
+ */
102
+ export async function removeConnectionById(id) {
103
+ const config = await loadConnections();
104
+ const originalLength = config.connections.length;
105
+ config.connections = config.connections.filter((conn) => conn.id !== id);
106
+ const wasRemoved = config.connections.length < originalLength;
107
+ if (wasRemoved) {
108
+ await saveConnectionsConfig(config);
109
+ }
110
+ return wasRemoved;
111
+ }
112
+ /**
113
+ * Updates a connection's name.
114
+ */
115
+ export async function updateConnectionName(connectionString, name) {
116
+ const config = await loadConnections();
117
+ const connection = config.connections.find((conn) => conn.connectionString === connectionString);
118
+ if (connection) {
119
+ connection.name = name;
120
+ await saveConnectionsConfig(config);
121
+ return connection;
122
+ }
123
+ return null;
124
+ }
125
+ //# sourceMappingURL=connections.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.service.js","sourceRoot":"","sources":["../../services/connections.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { Pool } from "pg";
2
+ import type { IndexesListData } from "../types/api.js";
3
+ /**
4
+ * Fetches all indexes with their columns, size, and scan statistics.
5
+ * Uses pg_catalog views and pg_stat_user_indexes for metrics.
6
+ */
7
+ export declare function getIndexesList(pool: Pool): Promise<IndexesListData>;
8
+ //# sourceMappingURL=indexes.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexes.service.d.ts","sourceRoot":"","sources":["../../services/indexes.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,KAAK,EAAiB,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEtE;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC,CAoDzE"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Fetches all indexes with their columns, size, and scan statistics.
3
+ * Uses pg_catalog views and pg_stat_user_indexes for metrics.
4
+ */
5
+ export async function getIndexesList(pool) {
6
+ // Query to get indexes with their columns, size, and usage stats
7
+ // Uses generate_series to iterate over index columns via pg_get_indexdef
8
+ const indexesQuery = `
9
+ WITH index_columns AS (
10
+ SELECT
11
+ i.indexrelid,
12
+ array_agg(
13
+ pg_get_indexdef(i.indexrelid, k.n, true)
14
+ ORDER BY k.n
15
+ ) FILTER (WHERE pg_get_indexdef(i.indexrelid, k.n, true) IS NOT NULL AND pg_get_indexdef(i.indexrelid, k.n, true) != '') AS columns
16
+ FROM pg_index i
17
+ CROSS JOIN LATERAL generate_series(1, i.indnkeyatts) AS k(n)
18
+ GROUP BY i.indexrelid
19
+ )
20
+ SELECT
21
+ n.nspname AS schema,
22
+ t.relname AS table_name,
23
+ i.relname AS index_name,
24
+ COALESCE(ic.columns, ARRAY[]::text[]) AS columns,
25
+ pg_relation_size(i.oid) AS size_bytes,
26
+ COALESCE(s.idx_scan, 0) AS index_scans,
27
+ ix.indisunique AS is_unique,
28
+ ix.indisprimary AS is_primary
29
+ FROM pg_class i
30
+ JOIN pg_index ix ON ix.indexrelid = i.oid
31
+ JOIN pg_class t ON t.oid = ix.indrelid
32
+ JOIN pg_namespace n ON n.oid = t.relnamespace
33
+ LEFT JOIN pg_stat_user_indexes s ON s.indexrelid = i.oid
34
+ LEFT JOIN index_columns ic ON ic.indexrelid = i.oid
35
+ WHERE i.relkind = 'i'
36
+ AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
37
+ ORDER BY pg_relation_size(i.oid) DESC
38
+ `;
39
+ const result = await pool.query(indexesQuery);
40
+ const indexes = result.rows.map((row) => ({
41
+ schema: row.schema,
42
+ table: row.table_name,
43
+ indexName: row.index_name,
44
+ columns: row.columns || [],
45
+ sizeBytes: Number(row.size_bytes),
46
+ indexScans: Number(row.index_scans),
47
+ isUnique: row.is_unique === true,
48
+ isPrimary: row.is_primary === true,
49
+ }));
50
+ // Extract unique schemas for the dropdown
51
+ const schemas = [...new Set(indexes.map((idx) => idx.schema))].sort();
52
+ return { indexes, schemas };
53
+ }
54
+ //# sourceMappingURL=indexes.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexes.service.js","sourceRoot":"","sources":["../../services/indexes.service.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAU;IAC7C,iEAAiE;IACjE,yEAAyE;IACzE,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BpB,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAE9C,MAAM,OAAO,GAAoB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACzD,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,KAAK,EAAE,GAAG,CAAC,UAAU;QACrB,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE;QAC1B,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC;QACjC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC;QACnC,QAAQ,EAAE,GAAG,CAAC,SAAS,KAAK,IAAI;QAChC,SAAS,EAAE,GAAG,CAAC,UAAU,KAAK,IAAI;KACnC,CAAC,CAAC,CAAC;IAEJ,0CAA0C;IAC1C,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAEtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { Pool } from "pg";
2
+ import type { SchemaData } from "../types/database.js";
3
+ /**
4
+ * Get complete schema data including tables, columns, and relationships
5
+ */
6
+ export declare function getSchemaData(pool: Pool): Promise<SchemaData>;
7
+ //# sourceMappingURL=schema.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.service.d.ts","sourceRoot":"","sources":["../../services/schema.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,KAAK,EAA6B,UAAU,EAAE,MAAM,sBAAsB,CAAC;AA0HlF;;GAEG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CA8DnE"}