@artilingo/artiframe-cli 2.0.4 → 3.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.
- package/LICENSE +660 -660
- package/README.md +64 -118
- package/bin/artiframe.js +0 -0
- package/bin/artiframe.php +0 -0
- package/core-stubs/app/ApiAuthControl.php +76 -0
- package/core-stubs/app/ApiControl.php +81 -35
- package/core-stubs/app/AuthControl.php +39 -0
- package/core-stubs/app/Database.php +36 -35
- package/core-stubs/app/DotEnv.php +19 -1
- package/core-stubs/app/ViewControl.php +47 -1
- package/core-stubs/bin/SQLMethod.php +261 -0
- package/core-stubs/bin/SystemMethod.php +115 -74
- package/core-stubs/bin/ViewMethod.php +19 -5
- package/core-stubs/docs/de.html +4325 -4347
- package/core-stubs/docs/en.html +4325 -4347
- package/core-stubs/docs/es.html +4325 -4347
- package/core-stubs/docs/fr.html +4325 -4347
- package/core-stubs/docs/tr.html +117 -46
- package/core-stubs/public/403.php +52 -0
- package/core-stubs/public/assets/css/components/header.css +174 -0
- package/core-stubs/public/assets/css/components/mobilenav.css +198 -0
- package/core-stubs/public/assets/css/components/sidebar.css +184 -0
- package/core-stubs/public/assets/css/root/app.css +975 -38
- package/core-stubs/public/assets/css/root/root.css +64 -0
- package/core-stubs/public/assets/js/components/header.js +380 -0
- package/core-stubs/public/assets/js/components/mobilenav.js +97 -0
- package/core-stubs/public/assets/js/components/theme-modal.js +74 -0
- package/core-stubs/public/assets/js/root/app.js +1870 -0
- package/core-stubs/public/includes/footer.php +4 -5
- package/core-stubs/public/includes/head-auth.php +65 -0
- package/core-stubs/public/includes/head.php +56 -17
- package/core-stubs/public/includes/header.php +135 -1
- package/core-stubs/public/includes/mobilenav.php +75 -3
- package/core-stubs/public/includes/sidebar.php +186 -1
- package/core-stubs/public/includes/theme-modal.php +1 -0
- package/db-studio/debug.log +3 -0
- package/db-studio/index.html +1755 -0
- package/db-studio/launcher.html +102 -0
- package/db-studio/launcher.js +180 -0
- package/db-studio/logo.svg +22 -0
- package/db-studio/main.js +568 -0
- package/db-studio/package-lock.json +1034 -0
- package/db-studio/package.json +16 -0
- package/db-studio/preload.js +29 -0
- package/devops-studio/api-inspector.js +222 -0
- package/devops-studio/index.html +1892 -0
- package/devops-studio/logo.svg +22 -0
- package/devops-studio/main.js +67 -0
- package/devops-studio/make-wizard.js +635 -0
- package/devops-studio/package-lock.json +871 -0
- package/devops-studio/package.json +12 -0
- package/package.json +5 -2
- package/scripts/postinstall.js +44 -8
- package/server-studio/.json-404-guard +1 -0
- package/server-studio/index.html +283 -0
- package/server-studio/logo.svg +22 -0
- package/server-studio/main.js +133 -0
- package/server-studio/package-lock.json +1179 -0
- package/server-studio/package.json +15 -0
- package/server-studio/router.php +99 -0
- package/src/App.php +29 -0
- package/src/Commands/AddCommand.php +26 -9
- package/src/Commands/DbExportCommand.php +131 -0
- package/src/Commands/DbStartCommand.php +46 -0
- package/src/Commands/DevopsCommand.php +35 -0
- package/src/Commands/GoCommand.php +48 -48
- package/src/Commands/ListCommand.php +68 -68
- package/src/Commands/MakeApiCommand.php +6 -1
- package/src/Commands/MakeViewCommand.php +25 -2
- package/src/Commands/NewProjectCommand.php +16 -11
- package/src/Commands/OptimizeCommand.php +61 -0
- package/src/Commands/RemoveCommand.php +35 -7
- package/src/Commands/ServeCommand.php +46 -91
- package/src/Commands/ShowCommand.php +24 -24
- package/src/Commands/UpgradeCommand.php +39 -10
- package/stubs/make/api-switch-case.stub +1 -1
- package/stubs/make/auth-api-standart.stub +20 -0
- package/stubs/make/auth-api-switch-case.stub +38 -0
- package/stubs/make/auth-view.stub +26 -0
- package/stubs/make/class.stub +10 -2
- package/stubs/make/view.stub +3 -0
- package/stubs/service/image.stub +334 -334
- package/stubs/service/iyzico.stub +637 -637
- package/stubs/service/jwt.stub +312 -312
- package/stubs/service/phpmailer.stub +143 -143
- package/stubs/service/pusher.stub +232 -232
- package/stubs/service/qrcode.stub +169 -169
- package/stubs/service/redis.stub +361 -361
- package/stubs/service/s3.stub +377 -377
- package/stubs/service/stripe.stub +526 -526
- package/stubs/service/twilio.stub +361 -361
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
const { app, BrowserWindow, ipcMain } = require('electron');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const mysql = require('mysql2/promise');
|
|
5
|
+
const dotenv = require('dotenv');
|
|
6
|
+
|
|
7
|
+
let mainWindow;
|
|
8
|
+
let projectRoot = '';
|
|
9
|
+
let appMode = 'live'; // 'live' or 'offline'
|
|
10
|
+
|
|
11
|
+
// Parse arguments
|
|
12
|
+
process.argv.forEach(arg => {
|
|
13
|
+
if (arg.startsWith('--mode=')) {
|
|
14
|
+
appMode = arg.split('=')[1];
|
|
15
|
+
} else if (arg !== '.' && arg !== '..' && !arg.includes('node_modules') && !arg.includes('electron') && !arg.endsWith('.js') && !arg.startsWith('--')) {
|
|
16
|
+
// Assume the last non-flag, non-executable argument is the project root
|
|
17
|
+
projectRoot = arg;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
if (!projectRoot) projectRoot = process.cwd();
|
|
21
|
+
|
|
22
|
+
// DEBUG LOGGING
|
|
23
|
+
fs.writeFileSync(path.join(__dirname, 'debug.log'), JSON.stringify(process.argv) + '\nappMode:' + appMode + '\nprojectRoot:' + projectRoot);
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const envPath = path.join(projectRoot, '.env');
|
|
27
|
+
let schemaPath = path.join(projectRoot, 'schema.sql');
|
|
28
|
+
|
|
29
|
+
if (appMode === 'live') {
|
|
30
|
+
dotenv.config({ path: envPath });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async function createConnection() {
|
|
35
|
+
if (appMode === 'offline') {
|
|
36
|
+
throw new Error('Offline modda veritabanı bağlantısı kapalıdır.');
|
|
37
|
+
}
|
|
38
|
+
return await mysql.createConnection({
|
|
39
|
+
host: process.env.DB_HOST || 'localhost',
|
|
40
|
+
user: process.env.DB_USER || 'root',
|
|
41
|
+
password: process.env.DB_PASSWORD || process.env.DB_PASS || '',
|
|
42
|
+
database: process.env.DB_NAME || '',
|
|
43
|
+
port: process.env.DB_PORT || 3306,
|
|
44
|
+
multipleStatements: true
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function createWindow() {
|
|
49
|
+
mainWindow = new BrowserWindow({
|
|
50
|
+
width: 1400,
|
|
51
|
+
height: 900,
|
|
52
|
+
title: 'ArtiFrame DB Studio',
|
|
53
|
+
webPreferences: {
|
|
54
|
+
preload: path.join(__dirname, 'preload.js'),
|
|
55
|
+
contextIsolation: true,
|
|
56
|
+
nodeIntegration: false
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
mainWindow.removeMenu();
|
|
60
|
+
if (appMode === 'offline') {
|
|
61
|
+
mainWindow.loadFile(path.join(__dirname, 'launcher.html'));
|
|
62
|
+
} else {
|
|
63
|
+
mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
|
64
|
+
}
|
|
65
|
+
mainWindow.webContents.on('did-finish-load', () => {
|
|
66
|
+
mainWindow.webContents.send('set-app-mode', { mode: appMode, projectRoot });
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
const crypto = require('crypto');
|
|
72
|
+
const artiSecretKey = crypto.createHash('sha256').update('ArtiFrameDBStudio_TopSecret_v1').digest();
|
|
73
|
+
const artiIV = crypto.createHash('md5').update('ArtiFrameDBStudio_IV_v1').digest();
|
|
74
|
+
|
|
75
|
+
ipcMain.handle('save-artiframe-file', async (event, globalDbData) => {
|
|
76
|
+
const { dialog } = require('electron');
|
|
77
|
+
const result = await dialog.showSaveDialog(mainWindow, {
|
|
78
|
+
title: 'Dışa Aktar (.artiframe)',
|
|
79
|
+
defaultPath: 'schema.artiframe',
|
|
80
|
+
filters: [{ name: 'ArtiFrame DB', extensions: ['artiframe', 'arti'] }]
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (result.canceled || !result.filePath) return false;
|
|
84
|
+
|
|
85
|
+
const artiData = {
|
|
86
|
+
metadata: { version: "2.0", type: "ArtiFrameDBDiagram", exportedAt: new Date().toISOString() },
|
|
87
|
+
schema: globalDbData
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const jsonStr = JSON.stringify(artiData);
|
|
91
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', artiSecretKey, artiIV);
|
|
92
|
+
|
|
93
|
+
const magic = Buffer.from('ARTIFRAME\0');
|
|
94
|
+
const encrypted = Buffer.concat([cipher.update(jsonStr, 'utf8'), cipher.final()]);
|
|
95
|
+
|
|
96
|
+
fs.writeFileSync(result.filePath, Buffer.concat([magic, encrypted]));
|
|
97
|
+
return true;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
ipcMain.handle('load-artiframe-file', async () => {
|
|
101
|
+
const { dialog } = require('electron');
|
|
102
|
+
const result = await dialog.showOpenDialog(mainWindow, {
|
|
103
|
+
title: 'İçe Aktar (.artiframe)',
|
|
104
|
+
filters: [{ name: 'ArtiFrame DB', extensions: ['artiframe', 'arti'] }],
|
|
105
|
+
properties: ['openFile']
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (result.canceled || result.filePaths.length === 0) return null;
|
|
109
|
+
|
|
110
|
+
const filePath = result.filePaths[0];
|
|
111
|
+
const buffer = fs.readFileSync(filePath);
|
|
112
|
+
|
|
113
|
+
const magic = buffer.subarray(0, 10);
|
|
114
|
+
if (magic.toString() !== 'ARTIFRAME\0') {
|
|
115
|
+
return { error: 'Geçersiz veya bozuk .artiframe dosyası! Sadece ArtiFrame DB Studio tarafından üretilmiş orjinal dosyalar açılabilir.' };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const encrypted = buffer.subarray(10);
|
|
119
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', artiSecretKey, artiIV);
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
123
|
+
const json = JSON.parse(decrypted.toString('utf8'));
|
|
124
|
+
if (json.metadata?.type !== "ArtiFrameDBDiagram") {
|
|
125
|
+
return { error: 'Geçersiz dosya içeriği!' };
|
|
126
|
+
}
|
|
127
|
+
return { success: true, data: json };
|
|
128
|
+
} catch (e) {
|
|
129
|
+
return { error: 'Dosyanın şifresi çözülemedi. Dosya bozuk veya farklı bir sürümden alınmış olabilir.' };
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
app.whenReady().then(createWindow);
|
|
135
|
+
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
|
|
136
|
+
|
|
137
|
+
function parseSqlToJson(sql) {
|
|
138
|
+
const tables = [];
|
|
139
|
+
const relationships = [];
|
|
140
|
+
|
|
141
|
+
const createTableRegex = /CREATE TABLE (?:IF NOT EXISTS )?\`([^\`]+)\` \(([\s\S]*?)\).*?;/g;
|
|
142
|
+
let match;
|
|
143
|
+
while ((match = createTableRegex.exec(sql)) !== null) {
|
|
144
|
+
const rawSql = match[0];
|
|
145
|
+
const tableName = match[1];
|
|
146
|
+
const body = match[2];
|
|
147
|
+
const columns = [];
|
|
148
|
+
const pks = new Set();
|
|
149
|
+
const uks = new Set();
|
|
150
|
+
const fks = [];
|
|
151
|
+
const indexes = [];
|
|
152
|
+
|
|
153
|
+
const lines = body.split('\n').map(l => l.trim()).filter(l => l.length > 0);
|
|
154
|
+
for (const line of lines) {
|
|
155
|
+
if (line.startsWith('PRIMARY KEY')) {
|
|
156
|
+
const pkMatch = line.match(/\((.*?)\)/);
|
|
157
|
+
if (pkMatch) {
|
|
158
|
+
pkMatch[1].split(',').forEach(k => pks.add(k.replace(/`/g, '').trim()));
|
|
159
|
+
}
|
|
160
|
+
} else if (line.startsWith('CONSTRAINT') || line.includes('FOREIGN KEY')) {
|
|
161
|
+
const fkMatch = line.match(/(?:CONSTRAINT \`([^\`]+)\`\s+)?FOREIGN KEY \(\`([^\`]+)\`\) REFERENCES \`([^\`]+)\` \(\`([^\`]+)\`\)(?:\s+ON DELETE (CASCADE|RESTRICT|SET NULL|NO ACTION))?(?:\s+ON UPDATE (CASCADE|RESTRICT|SET NULL|NO ACTION))?/i);
|
|
162
|
+
if (fkMatch) {
|
|
163
|
+
const constraintName = fkMatch[1] || `fk_${tableName}_${fkMatch[2]}`;
|
|
164
|
+
fks.push({
|
|
165
|
+
name: constraintName,
|
|
166
|
+
localCol: fkMatch[2],
|
|
167
|
+
refTable: fkMatch[3],
|
|
168
|
+
refCol: fkMatch[4],
|
|
169
|
+
onDelete: (fkMatch[5] || 'RESTRICT').toUpperCase(),
|
|
170
|
+
onUpdate: (fkMatch[6] || 'RESTRICT').toUpperCase()
|
|
171
|
+
});
|
|
172
|
+
relationships.push({fromTable: tableName, fromCol: fkMatch[2], toTable: fkMatch[3], toCol: fkMatch[4]});
|
|
173
|
+
}
|
|
174
|
+
} else if (line.match(/^(UNIQUE\s+)?(?:KEY|INDEX)\s+\`([^\`]+)\`\s+\((.+)\)/i)) {
|
|
175
|
+
const idxMatch = line.match(/^(UNIQUE\s+)?(?:KEY|INDEX)\s+\`([^\`]+)\`\s+\((.+)\)/i);
|
|
176
|
+
if(idxMatch) {
|
|
177
|
+
indexes.push({
|
|
178
|
+
isUnique: !!idxMatch[1],
|
|
179
|
+
name: idxMatch[2],
|
|
180
|
+
col: idxMatch[3].replace(/[\` ]/g, '')
|
|
181
|
+
});
|
|
182
|
+
if (!!idxMatch[1]) idxMatch[3].split(',').forEach(k => uks.add(k.replace(/[\` ]/g, '')));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
for (const line of lines) {
|
|
187
|
+
if (line.startsWith('`')) {
|
|
188
|
+
const colMatch = line.match(/^\`([^\`]+)\`\s+([a-zA-Z0-9_\(\)]+)/);
|
|
189
|
+
if (colMatch) {
|
|
190
|
+
columns.push({
|
|
191
|
+
name: colMatch[1],
|
|
192
|
+
type: colMatch[2],
|
|
193
|
+
isPk: pks.has(colMatch[1]),
|
|
194
|
+
isUk: uks.has(colMatch[1]),
|
|
195
|
+
isAi: line.includes('AUTO_INCREMENT'),
|
|
196
|
+
isNotNull: line.includes('NOT NULL')
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const filteredIndexes = indexes.filter(idx => !fks.some(fk => fk.name === idx.name || (!idx.isUnique && fk.localCol === idx.col)));
|
|
202
|
+
tables.push({ name: tableName, columns, rawSql, fks, indexes: filteredIndexes });
|
|
203
|
+
}
|
|
204
|
+
return { tables, relationships };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function syncDbToFile() {
|
|
208
|
+
let conn;
|
|
209
|
+
try {
|
|
210
|
+
conn = await createConnection();
|
|
211
|
+
const [tablesRows] = await conn.query('SHOW TABLES');
|
|
212
|
+
let schemaContent = "SET SQL_MODE = \"NO_AUTO_VALUE_ON_ZERO\";\nSTART TRANSACTION;\nSET time_zone = \"+00:00\";\n\n";
|
|
213
|
+
|
|
214
|
+
for (const row of tablesRows) {
|
|
215
|
+
const tableName = Object.values(row)[0];
|
|
216
|
+
const [createRows] = await conn.query(`SHOW CREATE TABLE \`${tableName}\``);
|
|
217
|
+
let createSql = createRows[0]['Create Table'];
|
|
218
|
+
createSql = createSql.replace(/ AUTO_INCREMENT=\d+/g, '');
|
|
219
|
+
schemaContent += `-- Tablo: ${tableName}\n`;
|
|
220
|
+
schemaContent += createSql + ";\n\n";
|
|
221
|
+
}
|
|
222
|
+
schemaContent += "COMMIT;\n";
|
|
223
|
+
fs.writeFileSync(schemaPath, schemaContent);
|
|
224
|
+
return schemaContent;
|
|
225
|
+
} finally {
|
|
226
|
+
if (conn) await conn.end();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
ipcMain.handle('get-table-data', async (event, tableName, limit = 50, offset = 0) => {
|
|
234
|
+
let conn;
|
|
235
|
+
try {
|
|
236
|
+
conn = await createConnection();
|
|
237
|
+
const [rows] = await conn.query(`SELECT * FROM \`${tableName}\` LIMIT ${Number(limit)} OFFSET ${Number(offset)}`);
|
|
238
|
+
const [countRes] = await conn.query(`SELECT COUNT(*) as total FROM \`${tableName}\``);
|
|
239
|
+
return { success: true, data: rows, total: countRes[0].total };
|
|
240
|
+
} catch (error) {
|
|
241
|
+
return { success: false, error: error.message };
|
|
242
|
+
} finally {
|
|
243
|
+
if (conn) await conn.end();
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
ipcMain.handle('update-row', async (event, tableName, pkCol, pkVal, updateData) => {
|
|
248
|
+
let conn;
|
|
249
|
+
try {
|
|
250
|
+
conn = await createConnection();
|
|
251
|
+
const keys = Object.keys(updateData);
|
|
252
|
+
if(keys.length === 0) return { success: true };
|
|
253
|
+
|
|
254
|
+
const setClause = keys.map(k => `\`${k}\` = ?`).join(', ');
|
|
255
|
+
const values = Object.values(updateData);
|
|
256
|
+
values.push(pkVal);
|
|
257
|
+
|
|
258
|
+
await conn.query(`UPDATE \`${tableName}\` SET ${setClause} WHERE \`${pkCol}\` = ?`, values);
|
|
259
|
+
return { success: true };
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return { success: false, error: error.message };
|
|
262
|
+
} finally {
|
|
263
|
+
if (conn) await conn.end();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
ipcMain.handle('delete-row', async (event, tableName, pkCol, pkVal) => {
|
|
268
|
+
let conn;
|
|
269
|
+
try {
|
|
270
|
+
conn = await createConnection();
|
|
271
|
+
await conn.query(`DELETE FROM \`${tableName}\` WHERE \`${pkCol}\` = ?`, [pkVal]);
|
|
272
|
+
return { success: true };
|
|
273
|
+
} catch (error) {
|
|
274
|
+
return { success: false, error: error.message };
|
|
275
|
+
} finally {
|
|
276
|
+
if (conn) await conn.end();
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
ipcMain.handle('seed-table', async (event, tableName, columns, count) => {
|
|
281
|
+
const { faker } = await import('@faker-js/faker');
|
|
282
|
+
let conn;
|
|
283
|
+
try {
|
|
284
|
+
conn = await createConnection();
|
|
285
|
+
|
|
286
|
+
// 1. Find Foreign Keys for this table
|
|
287
|
+
const [fkRows] = await conn.query(`
|
|
288
|
+
SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
|
|
289
|
+
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
290
|
+
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND REFERENCED_TABLE_NAME IS NOT NULL
|
|
291
|
+
`, [tableName]);
|
|
292
|
+
|
|
293
|
+
// 2. Fetch valid values for each FK
|
|
294
|
+
const fkValues = {};
|
|
295
|
+
for (const fk of fkRows) {
|
|
296
|
+
const [refRows] = await conn.query(`SELECT \`${fk.REFERENCED_COLUMN_NAME}\` AS val FROM \`${fk.REFERENCED_TABLE_NAME}\``);
|
|
297
|
+
if (refRows.length === 0) {
|
|
298
|
+
return { success: false, error: `Bağlantılı tablo olan '${fk.REFERENCED_TABLE_NAME}' tablosu boş. FK hatası almamak için önce o tabloya sahte veri eklemelisiniz!` };
|
|
299
|
+
}
|
|
300
|
+
fkValues[fk.COLUMN_NAME] = refRows.map(r => r.val);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let inserted = 0;
|
|
304
|
+
for(let i = 0; i < count; i++) {
|
|
305
|
+
const insertData = {};
|
|
306
|
+
for(const col of columns) {
|
|
307
|
+
if(col.isAi) continue; // Skip auto_increment
|
|
308
|
+
|
|
309
|
+
if (fkValues[col.name]) {
|
|
310
|
+
// Pick a random valid Foreign Key value
|
|
311
|
+
const vals = fkValues[col.name];
|
|
312
|
+
insertData[col.name] = vals[Math.floor(Math.random() * vals.length)];
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const type = col.type.toLowerCase();
|
|
317
|
+
const name = col.name.toLowerCase();
|
|
318
|
+
|
|
319
|
+
let val = null;
|
|
320
|
+
if(name.includes('email')) val = faker.internet.email();
|
|
321
|
+
else if(name.includes('name') || name.includes('isim')) val = faker.person.fullName();
|
|
322
|
+
else if(name.includes('password') || name.includes('sifre')) val = faker.internet.password();
|
|
323
|
+
else if(type.includes('int')) val = faker.number.int({min:1, max:100});
|
|
324
|
+
else if(type.includes('varchar') || type.includes('text')) val = faker.lorem.words(3);
|
|
325
|
+
else if(type.includes('date') || type.includes('time') || type.includes('timestamp')) val = faker.date.recent();
|
|
326
|
+
else if(type.includes('bool') || type.includes('tinyint')) val = faker.datatype.boolean() ? 1 : 0;
|
|
327
|
+
else val = faker.string.alphanumeric(10);
|
|
328
|
+
|
|
329
|
+
insertData[col.name] = val;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const keys = Object.keys(insertData);
|
|
333
|
+
if(keys.length > 0) {
|
|
334
|
+
const cols = keys.map(k => `\`${k}\``).join(', ');
|
|
335
|
+
const placeholders = keys.map(() => '?').join(', ');
|
|
336
|
+
const values = Object.values(insertData);
|
|
337
|
+
await conn.query(`INSERT INTO \`${tableName}\` (${cols}) VALUES (${placeholders})`, values);
|
|
338
|
+
inserted++;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return { success: true, message: `${inserted} sahte veri başarıyla eklendi.` };
|
|
342
|
+
} catch (error) {
|
|
343
|
+
return { success: false, error: error.message };
|
|
344
|
+
} finally {
|
|
345
|
+
if (conn) await conn.end();
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
ipcMain.handle('get-schema', async () => {
|
|
350
|
+
if (appMode === 'preview' && global.previewSchemaData) {
|
|
351
|
+
return { schema: global.previewSchemaData };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
let schemaStr = '';
|
|
355
|
+
try {
|
|
356
|
+
// ALWAYS sync from live DB so PhpMyAdmin changes reflect instantly
|
|
357
|
+
schemaStr = await syncDbToFile();
|
|
358
|
+
} catch (e) {
|
|
359
|
+
if (!fs.existsSync(schemaPath)) return { error: 'Veritabanı bağlantısı kurulamadı. Hata: ' + e.message };
|
|
360
|
+
schemaStr = fs.readFileSync(schemaPath, 'utf8');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const parsed = parseSqlToJson(schemaStr);
|
|
364
|
+
|
|
365
|
+
// Fetch live stats
|
|
366
|
+
let conn;
|
|
367
|
+
try {
|
|
368
|
+
conn = await createConnection();
|
|
369
|
+
const [stats] = await conn.query(`
|
|
370
|
+
SELECT TABLE_NAME, (DATA_LENGTH + INDEX_LENGTH) AS SIZE_BYTES
|
|
371
|
+
FROM information_schema.TABLES
|
|
372
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
373
|
+
`);
|
|
374
|
+
const statsMap = {};
|
|
375
|
+
stats.forEach(s => statsMap[s.TABLE_NAME] = { sizeBytes: s.SIZE_BYTES || 0 });
|
|
376
|
+
|
|
377
|
+
for (const t of parsed.tables) {
|
|
378
|
+
t.stats = statsMap[t.name] || { sizeBytes: 0 };
|
|
379
|
+
try {
|
|
380
|
+
const [countRes] = await conn.query(`SELECT COUNT(*) as c FROM \`${t.name}\``);
|
|
381
|
+
t.stats.rows = countRes[0].c;
|
|
382
|
+
} catch(e) {
|
|
383
|
+
t.stats.rows = 0;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
} catch(e) {
|
|
387
|
+
// ignore if stats fail
|
|
388
|
+
} finally {
|
|
389
|
+
if(conn) await conn.end();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
let layoutData = {};
|
|
394
|
+
if (projectRoot) {
|
|
395
|
+
const layoutPath = path.join(projectRoot, '.artiframe_layout.json');
|
|
396
|
+
if (fs.existsSync(layoutPath)) {
|
|
397
|
+
try { layoutData = JSON.parse(fs.readFileSync(layoutPath, 'utf8')); } catch(e){}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
parsed.tables.forEach(t => {
|
|
401
|
+
if (layoutData[t.name]) t.ui = layoutData[t.name];
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
return { json: parsed };
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
ipcMain.handle('save-layout', (event, layoutData) => {
|
|
408
|
+
if (!projectRoot || appMode !== 'live') return { success: false, error: 'Sadece aktif projelerde dizilim kaydedilebilir.' };
|
|
409
|
+
try {
|
|
410
|
+
const layoutPath = path.join(projectRoot, '.artiframe_layout.json');
|
|
411
|
+
fs.writeFileSync(layoutPath, JSON.stringify(layoutData, null, 2), 'utf8');
|
|
412
|
+
return { success: true };
|
|
413
|
+
} catch (err) {
|
|
414
|
+
return { success: false, error: err.message };
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
ipcMain.handle('execute-sql', async (event, sqlQuery) => {
|
|
419
|
+
let conn;
|
|
420
|
+
try {
|
|
421
|
+
conn = await createConnection();
|
|
422
|
+
await conn.query('SET FOREIGN_KEY_CHECKS=0;');
|
|
423
|
+
const [result] = await conn.query(sqlQuery);
|
|
424
|
+
await conn.query('SET FOREIGN_KEY_CHECKS=1;');
|
|
425
|
+
const newSchemaStr = await syncDbToFile();
|
|
426
|
+
return { success: true, message: 'Senkronize edildi.', json: parseSqlToJson(newSchemaStr), data: Array.isArray(result) ? result : null };
|
|
427
|
+
} catch (error) {
|
|
428
|
+
return { success: false, error: error.message };
|
|
429
|
+
} finally {
|
|
430
|
+
if (conn) await conn.end();
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
ipcMain.handle('get-app-mode', () => ({ mode: appMode, projectRoot }));
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
ipcMain.handle('get-workspace', () => {
|
|
439
|
+
try {
|
|
440
|
+
if (fs.existsSync(workspaceConfigPath)) {
|
|
441
|
+
return fs.readFileSync(workspaceConfigPath, 'utf8').trim();
|
|
442
|
+
}
|
|
443
|
+
} catch(e) {}
|
|
444
|
+
return null;
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
ipcMain.handle('set-workspace', (event, dirPath) => {
|
|
448
|
+
try {
|
|
449
|
+
fs.writeFileSync(workspaceConfigPath, dirPath, 'utf8');
|
|
450
|
+
return true;
|
|
451
|
+
} catch(e) {
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
ipcMain.handle('select-directory', async () => {
|
|
457
|
+
const { dialog } = require('electron');
|
|
458
|
+
const result = await dialog.showOpenDialog(mainWindow, {
|
|
459
|
+
properties: ['openDirectory']
|
|
460
|
+
});
|
|
461
|
+
if (!result.canceled && result.filePaths.length > 0) {
|
|
462
|
+
return result.filePaths[0];
|
|
463
|
+
}
|
|
464
|
+
return null;
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
ipcMain.handle('get-projects', async (event, workspacePath) => {
|
|
468
|
+
try {
|
|
469
|
+
const dirs = fs.readdirSync(workspacePath, { withFileTypes: true })
|
|
470
|
+
.filter(dirent => dirent.isDirectory())
|
|
471
|
+
.map(dirent => dirent.name);
|
|
472
|
+
|
|
473
|
+
// Filter projects that have .env or are valid artiframe projects
|
|
474
|
+
const validProjects = [];
|
|
475
|
+
for (const dir of dirs) {
|
|
476
|
+
// Let's just return all directories for now so the user can select their project even if it's missing .env temporarily
|
|
477
|
+
validProjects.push(dir);
|
|
478
|
+
}
|
|
479
|
+
return validProjects;
|
|
480
|
+
} catch(e) {
|
|
481
|
+
return [];
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
ipcMain.handle('open-project', (event, pPath) => {
|
|
486
|
+
appMode = 'live';
|
|
487
|
+
projectRoot = pPath;
|
|
488
|
+
|
|
489
|
+
const envPath = path.join(projectRoot, '.env');
|
|
490
|
+
dotenv.config({ path: envPath });
|
|
491
|
+
|
|
492
|
+
schemaPath = path.join(projectRoot, 'schema.sql');
|
|
493
|
+
|
|
494
|
+
mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
ipcMain.handle('preview-schema-file', (event, schemaJson) => {
|
|
500
|
+
appMode = 'preview';
|
|
501
|
+
global.previewSchemaData = schemaJson; // Hacky but works for transferring
|
|
502
|
+
mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
ipcMain.handle('restart-offline', async () => {
|
|
507
|
+
app.relaunch({ args: process.argv.slice(1).filter(a => !a.includes('--mode')).concat(['--mode=offline']) });
|
|
508
|
+
app.exit(0);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
ipcMain.handle('scaffold-project', async (event, dirName, schemaJson, customWorkspace = null) => {
|
|
512
|
+
return new Promise((resolve) => {
|
|
513
|
+
const { spawn } = require('child_process');
|
|
514
|
+
const rootPath = customWorkspace || projectRoot;
|
|
515
|
+
const targetPath = path.join(rootPath, dirName);
|
|
516
|
+
|
|
517
|
+
// Find cli php script path
|
|
518
|
+
const cliPath = path.resolve(__dirname, '../bin/artiframe.php');
|
|
519
|
+
|
|
520
|
+
const child = spawn('php', [cliPath, 'new', dirName], {
|
|
521
|
+
cwd: rootPath,
|
|
522
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
let out = '';
|
|
526
|
+
child.stdout.on('data', (data) => {
|
|
527
|
+
const str = data.toString();
|
|
528
|
+
out += str;
|
|
529
|
+
// Answer prompts automatically with defaults (Enter key)
|
|
530
|
+
if (str.includes('?')) {
|
|
531
|
+
child.stdin.write('\n');
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
child.stderr.on('data', (data) => {
|
|
536
|
+
console.error(data.toString());
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
child.on('close', (code) => {
|
|
540
|
+
if (code === 0) {
|
|
541
|
+
// Generate schema.sql inside the new project
|
|
542
|
+
let sqlScript = "/* ArtiFrame DB Studio - Otomatik Üretilmiş Şema */\n\n";
|
|
543
|
+
if (schemaJson && schemaJson.tables) {
|
|
544
|
+
schemaJson.tables.forEach(t => {
|
|
545
|
+
let cols = [];
|
|
546
|
+
let pks = [];
|
|
547
|
+
t.columns.forEach(c => {
|
|
548
|
+
let def = `\`${c.name}\` ${c.type}`;
|
|
549
|
+
if(!c.nullable) def += ' NOT NULL';
|
|
550
|
+
if(c.isAi) def += ' AUTO_INCREMENT';
|
|
551
|
+
cols.push(def);
|
|
552
|
+
if(c.isPk) pks.push(`\`${c.name}\``);
|
|
553
|
+
});
|
|
554
|
+
if(pks.length > 0) cols.push(`PRIMARY KEY (${pks.join(',')})`);
|
|
555
|
+
sqlScript += `CREATE TABLE \`${t.name}\` (\n ${cols.join(',\n ')}\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;\n\n`;
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const schemaFilePath = path.join(targetPath, 'schema.sql');
|
|
560
|
+
fs.writeFileSync(schemaFilePath, sqlScript, 'utf8');
|
|
561
|
+
|
|
562
|
+
resolve({ success: true, path: targetPath });
|
|
563
|
+
} else {
|
|
564
|
+
resolve({ success: false, error: 'Proje oluşturulurken CLI tarafında bir hata oluştu.' });
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
});
|