sql-kite 1.0.7 → 1.0.8
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.
- package/package.json +11 -3
- package/server/db/connections.js +114 -0
- package/server/db/meta-db.js +0 -0
- package/server/db/user-db.js +0 -0
- package/server/index.js +214 -0
- package/server/routes/branches.js +484 -0
- package/server/routes/compare.js +109 -0
- package/server/routes/export.js +201 -0
- package/server/routes/import.js +375 -0
- package/server/routes/migrations.js +332 -0
- package/server/routes/query.js +67 -0
- package/server/routes/schema.js +206 -0
- package/server/routes/snapshots.js +322 -0
- package/server/routes/tables.js +121 -0
- package/server/routes/timeline.js +108 -0
- package/server/server.js +0 -0
- package/src/commands/import-server.js +2 -5
- package/src/commands/import.js +5 -9
- package/src/commands/start.js +6 -7
- package/src/commands/stop.js +7 -2
- package/src/utils/paths.js +61 -1
- package/studio-out/404/index.html +1 -0
- package/studio-out/404.html +1 -0
- package/studio-out/__next.__PAGE__.txt +10 -0
- package/studio-out/__next._full.txt +20 -0
- package/studio-out/__next._head.txt +6 -0
- package/studio-out/__next._index.txt +6 -0
- package/studio-out/__next._tree.txt +3 -0
- package/studio-out/_next/static/LhecVBdPttfi1VZfXA-dL/_buildManifest.js +11 -0
- package/studio-out/_next/static/LhecVBdPttfi1VZfXA-dL/_clientMiddlewareManifest.json +1 -0
- package/studio-out/_next/static/LhecVBdPttfi1VZfXA-dL/_ssgManifest.js +1 -0
- package/studio-out/_next/static/chunks/118fc599da2f27aa.css +2 -0
- package/studio-out/_next/static/chunks/240f2fa81d4fb687.js +1 -0
- package/studio-out/_next/static/chunks/42c33ca704af9b68.js +1 -0
- package/studio-out/_next/static/chunks/99b69e65b599be96.js +5 -0
- package/studio-out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- package/studio-out/_next/static/chunks/a6dad97d9634a72d.js.map +1 -0
- package/studio-out/_next/static/chunks/b20313408e970968.css +1 -0
- package/studio-out/_next/static/chunks/d104f42a7b0c57b2.js +2 -0
- package/studio-out/_next/static/chunks/d4aa9be9c80c98d6.js +1 -0
- package/studio-out/_next/static/chunks/f2f58a7e93290fbb.js +1 -0
- package/studio-out/_next/static/chunks/f547e106c8e2aa8e.js +1 -0
- package/studio-out/_next/static/chunks/f5cb054219e2eeb8.js +109 -0
- package/studio-out/_next/static/chunks/turbopack-1577480078e795df.js +4 -0
- package/studio-out/_not-found/__next._full.txt +15 -0
- package/studio-out/_not-found/__next._head.txt +6 -0
- package/studio-out/_not-found/__next._index.txt +6 -0
- package/studio-out/_not-found/__next._not-found/__PAGE__.txt +5 -0
- package/studio-out/_not-found/__next._not-found.txt +4 -0
- package/studio-out/_not-found/__next._tree.txt +2 -0
- package/studio-out/_not-found/index.html +1 -0
- package/studio-out/_not-found/index.txt +15 -0
- package/studio-out/favicon.ico +10 -0
- package/studio-out/index.html +37 -0
- package/studio-out/index.txt +20 -0
- package/studio-out/logo.svg +5 -0
- package/studio-out/snapshots/__next._full.txt +15 -0
- package/studio-out/snapshots/__next._head.txt +6 -0
- package/studio-out/snapshots/__next._index.txt +6 -0
- package/studio-out/snapshots/__next._tree.txt +2 -0
- package/studio-out/snapshots/__next.snapshots/__PAGE__.txt +5 -0
- package/studio-out/snapshots/__next.snapshots.txt +4 -0
- package/studio-out/snapshots/index.html +1 -0
- package/studio-out/snapshots/index.txt +15 -0
- package/studio-out/sql/__next._full.txt +15 -0
- package/studio-out/sql/__next._head.txt +6 -0
- package/studio-out/sql/__next._index.txt +6 -0
- package/studio-out/sql/__next._tree.txt +2 -0
- package/studio-out/sql/__next.sql/__PAGE__.txt +5 -0
- package/studio-out/sql/__next.sql.txt +4 -0
- package/studio-out/sql/index.html +1 -0
- package/studio-out/sql/index.txt +15 -0
- package/studio-out/tables/__next._full.txt +15 -0
- package/studio-out/tables/__next._head.txt +6 -0
- package/studio-out/tables/__next._index.txt +6 -0
- package/studio-out/tables/__next._tree.txt +2 -0
- package/studio-out/tables/__next.tables/__PAGE__.txt +5 -0
- package/studio-out/tables/__next.tables.txt +4 -0
- package/studio-out/tables/index.html +1 -0
- package/studio-out/tables/index.txt +15 -0
- package/studio-out/timeline/__next._full.txt +15 -0
- package/studio-out/timeline/__next._head.txt +6 -0
- package/studio-out/timeline/__next._index.txt +6 -0
- package/studio-out/timeline/__next._tree.txt +2 -0
- package/studio-out/timeline/__next.timeline/__PAGE__.txt +5 -0
- package/studio-out/timeline/__next.timeline.txt +4 -0
- package/studio-out/timeline/index.html +1 -0
- package/studio-out/timeline/index.txt +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sql-kite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "SQL-Kite CLI — Local-first SQLite workspace with branches, migrations and snapshots.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"preferGlobal": true,
|
|
@@ -9,14 +9,16 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"dev": "node bin/sql-kite.js",
|
|
12
|
-
"start": "node bin/sql-kite.js"
|
|
12
|
+
"start": "node bin/sql-kite.js",
|
|
13
|
+
"prepublishOnly": "node scripts/prepare-publish.js"
|
|
13
14
|
},
|
|
14
15
|
"author": "D Krishna",
|
|
15
16
|
"license": "MIT",
|
|
16
17
|
"repository": {
|
|
17
18
|
"type": "git",
|
|
18
19
|
"url": "https://github.com/Ananta-V/sql-kite"
|
|
19
|
-
},
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/Ananta-V/sql-kite#readme",
|
|
20
22
|
"bugs": {
|
|
21
23
|
"url": "https://github.com/Ananta-V/sql-kite/issues"
|
|
22
24
|
},
|
|
@@ -36,14 +38,20 @@
|
|
|
36
38
|
"files": [
|
|
37
39
|
"bin/",
|
|
38
40
|
"src/",
|
|
41
|
+
"server/",
|
|
42
|
+
"studio-out/",
|
|
39
43
|
"README.md"
|
|
40
44
|
],
|
|
41
45
|
"dependencies": {
|
|
46
|
+
"@fastify/cors": "^10.0.1",
|
|
47
|
+
"@fastify/static": "^8.0.2",
|
|
42
48
|
"better-sqlite3": "^9.2.2",
|
|
43
49
|
"chalk": "^5.3.0",
|
|
44
50
|
"commander": "^12.0.0",
|
|
51
|
+
"fastify": "^5.7.4",
|
|
45
52
|
"find-free-port": "^2.0.0",
|
|
46
53
|
"inquirer": "^9.2.12",
|
|
54
|
+
"nanoid": "^5.0.4",
|
|
47
55
|
"open": "^10.0.3",
|
|
48
56
|
"ora": "^8.0.1"
|
|
49
57
|
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
const connections = new Map();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get the current active branch for a project
|
|
8
|
+
*/
|
|
9
|
+
export function getCurrentBranch(projectPath) {
|
|
10
|
+
try {
|
|
11
|
+
const metaDb = getMetaDb(projectPath);
|
|
12
|
+
const result = metaDb.prepare(`
|
|
13
|
+
SELECT value FROM settings WHERE key = 'current_branch'
|
|
14
|
+
`).get();
|
|
15
|
+
|
|
16
|
+
return result ? result.value : 'main';
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error('Error getting current branch:', error.message);
|
|
19
|
+
return 'main'; // Fallback to main
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get user database for current branch
|
|
25
|
+
*/
|
|
26
|
+
export function getUserDb(projectPath, branchName = null) {
|
|
27
|
+
const branch = branchName || getCurrentBranch(projectPath);
|
|
28
|
+
const key = `user-${projectPath}-${branch}`;
|
|
29
|
+
|
|
30
|
+
if (!connections.has(key)) {
|
|
31
|
+
const metaDb = getMetaDb(projectPath);
|
|
32
|
+
|
|
33
|
+
// Get the DB file for this branch
|
|
34
|
+
const branchInfo = metaDb.prepare(`
|
|
35
|
+
SELECT db_file FROM branches WHERE name = ?
|
|
36
|
+
`).get(branch);
|
|
37
|
+
|
|
38
|
+
if (!branchInfo) {
|
|
39
|
+
throw new Error(`Branch "${branch}" does not exist`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const dbPath = join(projectPath, branchInfo.db_file);
|
|
43
|
+
const db = new Database(dbPath);
|
|
44
|
+
db.pragma('journal_mode = WAL');
|
|
45
|
+
connections.set(key, db);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return connections.get(key);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getMetaDb(projectPath) {
|
|
52
|
+
const key = `meta-${projectPath}`;
|
|
53
|
+
|
|
54
|
+
if (!connections.has(key)) {
|
|
55
|
+
const dbPath = join(projectPath, '.studio', 'meta.db');
|
|
56
|
+
const db = new Database(dbPath);
|
|
57
|
+
db.pragma('journal_mode = WAL');
|
|
58
|
+
connections.set(key, db);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return connections.get(key);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Close and remove connection for a specific branch
|
|
66
|
+
* Used when switching branches
|
|
67
|
+
*/
|
|
68
|
+
export function closeBranchConnection(projectPath, branchName) {
|
|
69
|
+
const key = `user-${projectPath}-${branchName}`;
|
|
70
|
+
|
|
71
|
+
if (connections.has(key)) {
|
|
72
|
+
const db = connections.get(key);
|
|
73
|
+
db.close();
|
|
74
|
+
connections.delete(key);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get a read-only user database connection for compare mode
|
|
80
|
+
*/
|
|
81
|
+
export function getReadOnlyUserDb(projectPath, branchName) {
|
|
82
|
+
const branch = branchName || getCurrentBranch(projectPath);
|
|
83
|
+
const key = `readonly-${projectPath}-${branch}`;
|
|
84
|
+
|
|
85
|
+
if (!connections.has(key)) {
|
|
86
|
+
const metaDb = getMetaDb(projectPath);
|
|
87
|
+
|
|
88
|
+
const branchInfo = metaDb.prepare(`
|
|
89
|
+
SELECT db_file FROM branches WHERE name = ?
|
|
90
|
+
`).get(branch);
|
|
91
|
+
|
|
92
|
+
if (!branchInfo) {
|
|
93
|
+
throw new Error(`Branch "${branch}" does not exist`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const dbPath = join(projectPath, branchInfo.db_file);
|
|
97
|
+
const db = new Database(dbPath, { readonly: true, fileMustExist: true });
|
|
98
|
+
db.pragma('query_only = ON');
|
|
99
|
+
db.pragma('busy_timeout = 2000');
|
|
100
|
+
connections.set(key, db);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return connections.get(key);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function closeReadOnlyBranchConnection(projectPath, branchName) {
|
|
107
|
+
const key = `readonly-${projectPath}-${branchName}`;
|
|
108
|
+
|
|
109
|
+
if (connections.has(key)) {
|
|
110
|
+
const db = connections.get(key);
|
|
111
|
+
db.close();
|
|
112
|
+
connections.delete(key);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
File without changes
|
|
File without changes
|
package/server/index.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import Fastify from 'fastify';
|
|
2
|
+
import cors from '@fastify/cors';
|
|
3
|
+
import fastifyStatic from '@fastify/static';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
6
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
|
|
7
|
+
import { createRequire } from 'module';
|
|
8
|
+
|
|
9
|
+
import { getUserDb, getMetaDb, getCurrentBranch } from './db/connections.js';
|
|
10
|
+
import tablesRoutes from './routes/tables.js';
|
|
11
|
+
import queryRoutes from './routes/query.js';
|
|
12
|
+
import schemaRoutes from './routes/schema.js';
|
|
13
|
+
import timelineRoutes from './routes/timeline.js';
|
|
14
|
+
import migrationsRoutes from './routes/migrations.js';
|
|
15
|
+
import snapshotsRoutes from './routes/snapshots.js';
|
|
16
|
+
import branchesRoutes from './routes/branches.js';
|
|
17
|
+
import importRoutes from './routes/import.js';
|
|
18
|
+
import compareRoutes from './routes/compare.js';
|
|
19
|
+
import exportRoutes from './routes/export.js';
|
|
20
|
+
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
|
|
24
|
+
// Import meta migration - works in both layouts:
|
|
25
|
+
// monorepo: packages/server/src/ -> ../../cli/src/utils/meta-migration.js
|
|
26
|
+
// npm: sql-kite/server/ -> ../src/utils/meta-migration.js
|
|
27
|
+
let migrateMetaDb;
|
|
28
|
+
const metaMigrationPaths = [
|
|
29
|
+
join(__dirname, '..', 'src', 'utils', 'meta-migration.js'), // npm layout
|
|
30
|
+
join(__dirname, '..', '..', 'cli', 'src', 'utils', 'meta-migration.js') // monorepo
|
|
31
|
+
];
|
|
32
|
+
for (const p of metaMigrationPaths) {
|
|
33
|
+
if (existsSync(p)) {
|
|
34
|
+
const mod = await import(pathToFileURL(p).href);
|
|
35
|
+
migrateMetaDb = mod.migrateMetaDb;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!migrateMetaDb) {
|
|
40
|
+
console.error('Could not find meta-migration.js. Tried:', metaMigrationPaths);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const PROJECT_NAME = process.env.PROJECT_NAME;
|
|
45
|
+
const PROJECT_PATH = process.env.PROJECT_PATH;
|
|
46
|
+
const PORT = parseInt(process.env.PORT || '3000');
|
|
47
|
+
const IMPORT_MODE = process.env.IMPORT_MODE === 'true';
|
|
48
|
+
|
|
49
|
+
if (!IMPORT_MODE && (!PROJECT_NAME || !PROJECT_PATH)) {
|
|
50
|
+
console.error('Missing required environment variables: PROJECT_NAME, PROJECT_PATH');
|
|
51
|
+
console.error('Or set IMPORT_MODE=true to run in import-only mode');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Run meta database migration before starting server (skip in import mode)
|
|
56
|
+
if (!IMPORT_MODE) {
|
|
57
|
+
const metaDbPath = join(PROJECT_PATH, '.studio', 'meta.db');
|
|
58
|
+
try {
|
|
59
|
+
migrateMetaDb(metaDbPath);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error('Failed to migrate meta database:', error);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const fastify = Fastify({
|
|
67
|
+
logger: {
|
|
68
|
+
level: 'info'
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// CORS - Restrict to localhost origins only for security
|
|
73
|
+
// This prevents CSRF-style attacks from malicious websites
|
|
74
|
+
// Uses a function to allow any localhost port dynamically
|
|
75
|
+
await fastify.register(cors, {
|
|
76
|
+
origin: (origin, callback) => {
|
|
77
|
+
// Allow requests with no origin (same-origin, curl, Postman, etc.)
|
|
78
|
+
if (!origin) {
|
|
79
|
+
return callback(null, true);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Allow any localhost or 127.0.0.1 origin (any port)
|
|
83
|
+
const localhostPattern = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/;
|
|
84
|
+
if (localhostPattern.test(origin)) {
|
|
85
|
+
return callback(null, true);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Block all other origins
|
|
89
|
+
return callback(new Error('CORS not allowed'), false);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Store project info in fastify instance (if not in import mode)
|
|
94
|
+
if (!IMPORT_MODE) {
|
|
95
|
+
fastify.decorate('projectName', PROJECT_NAME);
|
|
96
|
+
fastify.decorate('projectPath', PROJECT_PATH);
|
|
97
|
+
// API always queries 'main' branch only - branches are for Studio development only
|
|
98
|
+
fastify.decorate('getUserDb', () => getUserDb(PROJECT_PATH, 'main'));
|
|
99
|
+
fastify.decorate('getMetaDb', () => getMetaDb(PROJECT_PATH));
|
|
100
|
+
fastify.decorate('getCurrentBranch', () => getCurrentBranch(PROJECT_PATH));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// API Routes
|
|
104
|
+
fastify.register(importRoutes, { prefix: '/api/import' });
|
|
105
|
+
|
|
106
|
+
// Project-specific routes (only in project mode)
|
|
107
|
+
if (!IMPORT_MODE) {
|
|
108
|
+
fastify.register(branchesRoutes, { prefix: '/api/branches' });
|
|
109
|
+
fastify.register(tablesRoutes, { prefix: '/api/tables' });
|
|
110
|
+
fastify.register(queryRoutes, { prefix: '/api/query' });
|
|
111
|
+
fastify.register(schemaRoutes, { prefix: '/api/schema' });
|
|
112
|
+
fastify.register(timelineRoutes, { prefix: '/api/timeline' });
|
|
113
|
+
fastify.register(migrationsRoutes, { prefix: '/api/migrations' });
|
|
114
|
+
fastify.register(snapshotsRoutes, { prefix: '/api/snapshots' });
|
|
115
|
+
fastify.register(compareRoutes, { prefix: '/api/compare' });
|
|
116
|
+
fastify.register(exportRoutes, { prefix: '/api/export' });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Project info endpoint (only in project mode)
|
|
120
|
+
if (!IMPORT_MODE) {
|
|
121
|
+
fastify.get('/api/project', async (request, reply) => {
|
|
122
|
+
const configPath = join(PROJECT_PATH, 'config.json');
|
|
123
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
124
|
+
const currentBranch = getCurrentBranch(PROJECT_PATH);
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
name: PROJECT_NAME,
|
|
128
|
+
path: PROJECT_PATH,
|
|
129
|
+
port: PORT,
|
|
130
|
+
currentBranch,
|
|
131
|
+
...config
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
// Import mode - minimal project info
|
|
136
|
+
fastify.get('/api/project', async (request, reply) => {
|
|
137
|
+
return {
|
|
138
|
+
name: 'Import Mode',
|
|
139
|
+
mode: 'import',
|
|
140
|
+
port: PORT
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Serve Studio static files - works in both layouts:
|
|
146
|
+
// monorepo: packages/server/src/ -> ../../studio/out
|
|
147
|
+
// npm: sql-kite/server/ -> ../studio-out
|
|
148
|
+
let studioPath = join(__dirname, '..', 'studio-out'); // npm layout
|
|
149
|
+
if (!existsSync(studioPath)) {
|
|
150
|
+
studioPath = join(__dirname, '..', '..', 'studio', 'out'); // monorepo layout
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log('Looking for Studio at:', studioPath);
|
|
154
|
+
console.log('Studio exists:', existsSync(studioPath));
|
|
155
|
+
|
|
156
|
+
if (!existsSync(studioPath)) {
|
|
157
|
+
console.error('\n❌ ERROR: Studio build not found!');
|
|
158
|
+
console.error('Please run: cd packages/studio && npm run build\n');
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await fastify.register(fastifyStatic, {
|
|
163
|
+
root: studioPath,
|
|
164
|
+
prefix: '/',
|
|
165
|
+
decorateReply: false,
|
|
166
|
+
index: 'index.html'
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Fallback to index.html for client-side routing
|
|
170
|
+
fastify.setNotFoundHandler((request, reply) => {
|
|
171
|
+
if (request.url.startsWith('/api')) {
|
|
172
|
+
reply.code(404).send({ error: 'API endpoint not found' });
|
|
173
|
+
} else {
|
|
174
|
+
reply.sendFile('index.html');
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Graceful shutdown
|
|
179
|
+
const closeGracefully = async (signal) => {
|
|
180
|
+
console.log(`\nReceived ${signal}, closing gracefully...`);
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
// Close database connections
|
|
184
|
+
const userDb = getUserDb(PROJECT_PATH);
|
|
185
|
+
const metaDb = getMetaDb(PROJECT_PATH);
|
|
186
|
+
userDb.close();
|
|
187
|
+
metaDb.close();
|
|
188
|
+
|
|
189
|
+
// Remove server info file
|
|
190
|
+
const serverInfoPath = join(PROJECT_PATH, '.studio', 'server.json');
|
|
191
|
+
if (existsSync(serverInfoPath)) {
|
|
192
|
+
unlinkSync(serverInfoPath);
|
|
193
|
+
}
|
|
194
|
+
} catch (e) {
|
|
195
|
+
console.error('Error during shutdown:', e);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
await fastify.close();
|
|
199
|
+
process.exit(0);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
process.on('SIGTERM', closeGracefully);
|
|
203
|
+
process.on('SIGINT', closeGracefully);
|
|
204
|
+
|
|
205
|
+
// Start server
|
|
206
|
+
try {
|
|
207
|
+
await fastify.listen({ port: PORT, host: '0.0.0.0' });
|
|
208
|
+
console.log(`\n✓ Server started for project "${PROJECT_NAME}"`);
|
|
209
|
+
console.log(` URL: http://localhost:${PORT}`);
|
|
210
|
+
console.log(` Path: ${PROJECT_PATH}\n`);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
fastify.log.error(err);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|