fix dependency
This commit is contained in:
parent
86e903ec4b
commit
427be2a293
1
.npmrc
1
.npmrc
@ -1,3 +1,2 @@
|
||||
better-sqlite3:ignore-scripts=true
|
||||
electron-deeplink:ignore-scripts=true
|
||||
sharp:ignore-scripts=true
|
@ -10,6 +10,7 @@
|
||||
"package": "npm run build:renderer && electron-forge package",
|
||||
"make": "npm run build:renderer && electron-forge make",
|
||||
"build": "npm run build:renderer && electron-builder --config electron-builder.yml --publish never",
|
||||
"build:win": "npm run build:renderer && electron-builder --win --x64 --publish never",
|
||||
"publish": "npm run build:renderer && electron-builder --config electron-builder.yml --publish always",
|
||||
"lint": "eslint --ext .ts,.tsx,.js .",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
@ -35,7 +36,7 @@
|
||||
"better-sqlite3": "^9.4.3",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.0.0",
|
||||
"electron-deeplink": "^1.0.10",
|
||||
|
||||
"electron-squirrel-startup": "^1.0.1",
|
||||
"electron-store": "^8.2.0",
|
||||
"electron-updater": "^6.6.2",
|
||||
@ -47,7 +48,6 @@
|
||||
"openai": "^4.70.0",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"sharp": "^0.34.2",
|
||||
"sqlite3": "^5.1.7",
|
||||
"validator": "^13.11.0",
|
||||
"wait-on": "^8.0.3",
|
||||
"ws": "^8.18.0"
|
||||
@ -69,8 +69,6 @@
|
||||
"esbuild": "^0.25.5"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-x64": "^0.34.2",
|
||||
"@img/sharp-libvips-darwin-x64": "^1.1.0",
|
||||
"electron-liquid-glass": "^1.0.1"
|
||||
}
|
||||
}
|
@ -2,40 +2,34 @@ const sqliteClient = require('../../services/sqliteClient');
|
||||
|
||||
function getPresets(uid) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = `
|
||||
SELECT * FROM prompt_presets
|
||||
WHERE uid = ? OR is_default = 1
|
||||
ORDER BY is_default DESC, title ASC
|
||||
`;
|
||||
db.all(query, [uid], (err, rows) => {
|
||||
if (err) {
|
||||
|
||||
try {
|
||||
return db.prepare(query).all(uid);
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to get presets:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(rows);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getPresetTemplates() {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = `
|
||||
SELECT * FROM prompt_presets
|
||||
WHERE is_default = 1
|
||||
ORDER BY title ASC
|
||||
`;
|
||||
db.all(query, [], (err, rows) => {
|
||||
if (err) {
|
||||
|
||||
try {
|
||||
return db.prepare(query).all();
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to get preset templates:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(rows);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function create({ uid, title, prompt }) {
|
||||
@ -44,38 +38,42 @@ function create({ uid, title, prompt }) {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = `INSERT INTO prompt_presets (id, uid, title, prompt, is_default, created_at, sync_state) VALUES (?, ?, ?, ?, 0, ?, 'dirty')`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(query, [presetId, uid, title, prompt, now], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve({ id: presetId });
|
||||
});
|
||||
});
|
||||
try {
|
||||
db.prepare(query).run(presetId, uid, title, prompt, now);
|
||||
return { id: presetId };
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function update(id, { title, prompt }, uid) {
|
||||
const db = sqliteClient.getDb();
|
||||
const query = `UPDATE prompt_presets SET title = ?, prompt = ?, sync_state = 'dirty' WHERE id = ? AND uid = ? AND is_default = 0`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(query, [title, prompt, id, uid], function(err) {
|
||||
if (err) reject(err);
|
||||
else if (this.changes === 0) reject(new Error("Preset not found or permission denied."));
|
||||
else resolve({ changes: this.changes });
|
||||
});
|
||||
});
|
||||
try {
|
||||
const result = db.prepare(query).run(title, prompt, id, uid);
|
||||
if (result.changes === 0) {
|
||||
throw new Error("Preset not found or permission denied.");
|
||||
}
|
||||
return { changes: result.changes };
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function del(id, uid) {
|
||||
const db = sqliteClient.getDb();
|
||||
const query = `DELETE FROM prompt_presets WHERE id = ? AND uid = ? AND is_default = 0`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(query, [id, uid], function(err) {
|
||||
if (err) reject(err);
|
||||
else if (this.changes === 0) reject(new Error("Preset not found or permission denied."));
|
||||
else resolve({ changes: this.changes });
|
||||
});
|
||||
});
|
||||
try {
|
||||
const result = db.prepare(query).run(id, uid);
|
||||
if (result.changes === 0) {
|
||||
throw new Error("Preset not found or permission denied.");
|
||||
}
|
||||
return { changes: result.changes };
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -2,124 +2,79 @@ const sqliteClient = require('../../services/sqliteClient');
|
||||
|
||||
function getById(id) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
db.get('SELECT * FROM sessions WHERE id = ?', [id], (err, row) => {
|
||||
if (err) reject(err);
|
||||
else resolve(row);
|
||||
});
|
||||
});
|
||||
return db.prepare('SELECT * FROM sessions WHERE id = ?').get(id);
|
||||
}
|
||||
|
||||
function create(uid, type = 'ask') {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const sessionId = require('crypto').randomUUID();
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = `INSERT INTO sessions (id, uid, title, session_type, started_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
db.run(query, [sessionId, uid, `Session @ ${new Date().toLocaleTimeString()}`, type, now, now], function(err) {
|
||||
if (err) {
|
||||
console.error('SQLite: Failed to create session:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
try {
|
||||
db.prepare(query).run(sessionId, uid, `Session @ ${new Date().toLocaleTimeString()}`, type, now, now);
|
||||
console.log(`SQLite: Created session ${sessionId} for user ${uid} (type: ${type})`);
|
||||
resolve(sessionId);
|
||||
return sessionId;
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to create session:', err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getAllByUserId(uid) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = "SELECT id, uid, title, session_type, started_at, ended_at, sync_state, updated_at FROM sessions WHERE uid = ? ORDER BY started_at DESC";
|
||||
db.all(query, [uid], (err, rows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows);
|
||||
});
|
||||
});
|
||||
return db.prepare(query).all(uid);
|
||||
}
|
||||
|
||||
function updateTitle(id, title) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run('UPDATE sessions SET title = ? WHERE id = ?', [title, id], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve({ changes: this.changes });
|
||||
});
|
||||
});
|
||||
const result = db.prepare('UPDATE sessions SET title = ? WHERE id = ?').run(title, id);
|
||||
return { changes: result.changes };
|
||||
}
|
||||
|
||||
function deleteWithRelatedData(id) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
db.serialize(() => {
|
||||
db.run("BEGIN TRANSACTION;");
|
||||
const queries = [
|
||||
"DELETE FROM transcripts WHERE session_id = ?",
|
||||
"DELETE FROM ai_messages WHERE session_id = ?",
|
||||
"DELETE FROM summaries WHERE session_id = ?",
|
||||
"DELETE FROM sessions WHERE id = ?"
|
||||
];
|
||||
queries.forEach(query => {
|
||||
db.run(query, [id], (err) => {
|
||||
if (err) {
|
||||
db.run("ROLLBACK;");
|
||||
return reject(err);
|
||||
const transaction = db.transaction(() => {
|
||||
db.prepare("DELETE FROM transcripts WHERE session_id = ?").run(id);
|
||||
db.prepare("DELETE FROM ai_messages WHERE session_id = ?").run(id);
|
||||
db.prepare("DELETE FROM summaries WHERE session_id = ?").run(id);
|
||||
db.prepare("DELETE FROM sessions WHERE id = ?").run(id);
|
||||
});
|
||||
|
||||
try {
|
||||
transaction();
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
db.run("COMMIT;", (err) => {
|
||||
if (err) {
|
||||
db.run("ROLLBACK;");
|
||||
return reject(err);
|
||||
}
|
||||
resolve({ success: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function end(id) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE id = ?`;
|
||||
db.run(query, [now, now, id], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve({ changes: this.changes });
|
||||
});
|
||||
});
|
||||
const result = db.prepare(query).run(now, now, id);
|
||||
return { changes: result.changes };
|
||||
}
|
||||
|
||||
function updateType(id, type) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = 'UPDATE sessions SET session_type = ?, updated_at = ? WHERE id = ?';
|
||||
db.run(query, [type, now, id], function(err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({ changes: this.changes });
|
||||
}
|
||||
});
|
||||
});
|
||||
const result = db.prepare(query).run(type, now, id);
|
||||
return { changes: result.changes };
|
||||
}
|
||||
|
||||
function touch(id) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = 'UPDATE sessions SET updated_at = ? WHERE id = ?';
|
||||
db.run(query, [now, id], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve({ changes: this.changes });
|
||||
});
|
||||
});
|
||||
const result = db.prepare(query).run(now, id);
|
||||
return { changes: result.changes };
|
||||
}
|
||||
|
||||
async function getOrCreateActive(uid, requestedType = 'ask') {
|
||||
function getOrCreateActive(uid, requestedType = 'ask') {
|
||||
const db = sqliteClient.getDb();
|
||||
|
||||
// 1. Look for ANY active session for the user (ended_at IS NULL).
|
||||
@ -131,12 +86,7 @@ async function getOrCreateActive(uid, requestedType = 'ask') {
|
||||
LIMIT 1
|
||||
`;
|
||||
|
||||
const activeSession = await new Promise((resolve, reject) => {
|
||||
db.get(findQuery, [uid], (err, row) => {
|
||||
if (err) reject(err);
|
||||
else resolve(row);
|
||||
});
|
||||
});
|
||||
const activeSession = db.prepare(findQuery).get(uid);
|
||||
|
||||
if (activeSession) {
|
||||
// An active session exists.
|
||||
@ -144,12 +94,12 @@ async function getOrCreateActive(uid, requestedType = 'ask') {
|
||||
|
||||
// 2. Promotion Logic: If it's an 'ask' session and we need 'listen', promote it.
|
||||
if (activeSession.session_type === 'ask' && requestedType === 'listen') {
|
||||
await updateType(activeSession.id, 'listen');
|
||||
updateType(activeSession.id, 'listen');
|
||||
console.log(`[Repo] Promoted session ${activeSession.id} to 'listen' type.`);
|
||||
}
|
||||
|
||||
// 3. Touch the session and return its ID.
|
||||
await touch(activeSession.id);
|
||||
touch(activeSession.id);
|
||||
return activeSession.id;
|
||||
} else {
|
||||
// 4. No active session found, create a new one.
|
||||
@ -160,19 +110,17 @@ async function getOrCreateActive(uid, requestedType = 'ask') {
|
||||
|
||||
function endAllActiveSessions() {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE ended_at IS NULL`;
|
||||
db.run(query, [now, now], function(err) {
|
||||
if (err) {
|
||||
|
||||
try {
|
||||
const result = db.prepare(query).run(now, now);
|
||||
console.log(`[Repo] Ended ${result.changes} active session(s).`);
|
||||
return { changes: result.changes };
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to end all active sessions:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
console.log(`[Repo] Ended ${this.changes} active session(s).`);
|
||||
resolve({ changes: this.changes });
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -13,66 +13,44 @@ function findOrCreate(user) {
|
||||
email=excluded.email
|
||||
`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(query, [uid, displayName, email, now], (err) => {
|
||||
if (err) {
|
||||
try {
|
||||
db.prepare(query).run(uid, displayName, email, now);
|
||||
return getById(uid);
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to find or create user:', err);
|
||||
return reject(err);
|
||||
throw err;
|
||||
}
|
||||
getById(uid).then(resolve).catch(reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getById(uid) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
db.get('SELECT * FROM users WHERE uid = ?', [uid], (err, row) => {
|
||||
if (err) reject(err);
|
||||
else resolve(row);
|
||||
});
|
||||
});
|
||||
return db.prepare('SELECT * FROM users WHERE uid = ?').get(uid);
|
||||
}
|
||||
|
||||
function saveApiKey(apiKey, uid, provider = 'openai') {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
'UPDATE users SET api_key = ?, provider = ? WHERE uid = ?',
|
||||
[apiKey, provider, uid],
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.error('SQLite: Failed to save API key:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
try {
|
||||
const result = db.prepare('UPDATE users SET api_key = ?, provider = ? WHERE uid = ?').run(apiKey, provider, uid);
|
||||
console.log(`SQLite: API key saved for user ${uid} with provider ${provider}.`);
|
||||
resolve({ changes: this.changes });
|
||||
return { changes: result.changes };
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to save API key:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function update({ uid, displayName }) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run('UPDATE users SET display_name = ? WHERE uid = ?', [displayName, uid], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve({ changes: this.changes });
|
||||
});
|
||||
});
|
||||
const result = db.prepare('UPDATE users SET display_name = ? WHERE uid = ?').run(displayName, uid);
|
||||
return { changes: result.changes };
|
||||
}
|
||||
|
||||
function deleteById(uid) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const userSessions = db.prepare('SELECT id FROM sessions WHERE uid = ?').all(uid);
|
||||
const sessionIds = userSessions.map(s => s.id);
|
||||
|
||||
db.serialize(() => {
|
||||
db.run("BEGIN TRANSACTION;");
|
||||
|
||||
try {
|
||||
const transaction = db.transaction(() => {
|
||||
if (sessionIds.length > 0) {
|
||||
const placeholders = sessionIds.map(() => '?').join(',');
|
||||
db.prepare(`DELETE FROM transcripts WHERE session_id IN (${placeholders})`).run(...sessionIds);
|
||||
@ -82,20 +60,14 @@ function deleteById(uid) {
|
||||
}
|
||||
db.prepare('DELETE FROM prompt_presets WHERE uid = ? AND is_default = 0').run(uid);
|
||||
db.prepare('DELETE FROM users WHERE uid = ?').run(uid);
|
||||
});
|
||||
|
||||
db.run("COMMIT;", (err) => {
|
||||
if (err) {
|
||||
db.run("ROLLBACK;");
|
||||
return reject(err);
|
||||
}
|
||||
resolve({ success: true });
|
||||
});
|
||||
try {
|
||||
transaction();
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
db.run("ROLLBACK;");
|
||||
reject(err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -123,7 +123,7 @@ class AuthService {
|
||||
const userState = this.getCurrentUser();
|
||||
console.log('[AuthService] Broadcasting user state change:', userState);
|
||||
BrowserWindow.getAllWindows().forEach(win => {
|
||||
if (win && !win.isDestroyed()) {
|
||||
if (win && !win.isDestroyed() && win.webContents && !win.webContents.isDestroyed()) {
|
||||
win.webContents.send('user-state-changed', userState);
|
||||
}
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
const sqlite3 = require('sqlite3').verbose();
|
||||
const Database = require('better-sqlite3');
|
||||
const path = require('path');
|
||||
const LATEST_SCHEMA = require('../config/schema');
|
||||
|
||||
@ -10,28 +10,20 @@ class SQLiteClient {
|
||||
}
|
||||
|
||||
connect(dbPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.db) {
|
||||
console.log('[SQLiteClient] Already connected.');
|
||||
return resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.dbPath = dbPath;
|
||||
this.db = new sqlite3.Database(this.dbPath, (err) => {
|
||||
if (err) {
|
||||
console.error('[SQLiteClient] Could not connect to database', err);
|
||||
return reject(err);
|
||||
}
|
||||
this.db = new Database(this.dbPath);
|
||||
this.db.pragma('journal_mode = WAL');
|
||||
console.log('[SQLiteClient] Connected successfully to:', this.dbPath);
|
||||
|
||||
this.db.run('PRAGMA journal_mode = WAL;', (err) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
} catch (err) {
|
||||
console.error('[SQLiteClient] Could not connect to database', err);
|
||||
throw err;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getDb() {
|
||||
@ -41,51 +33,39 @@ class SQLiteClient {
|
||||
return this.db;
|
||||
}
|
||||
|
||||
async synchronizeSchema() {
|
||||
synchronizeSchema() {
|
||||
console.log('[DB Sync] Starting schema synchronization...');
|
||||
const tablesInDb = await this.getTablesFromDb();
|
||||
const tablesInDb = this.getTablesFromDb();
|
||||
|
||||
for (const tableName of Object.keys(LATEST_SCHEMA)) {
|
||||
const tableSchema = LATEST_SCHEMA[tableName];
|
||||
|
||||
if (!tablesInDb.includes(tableName)) {
|
||||
// Table doesn't exist, create it
|
||||
await this.createTable(tableName, tableSchema);
|
||||
this.createTable(tableName, tableSchema);
|
||||
} else {
|
||||
// Table exists, check for missing columns
|
||||
await this.updateTable(tableName, tableSchema);
|
||||
this.updateTable(tableName, tableSchema);
|
||||
}
|
||||
}
|
||||
console.log('[DB Sync] Schema synchronization finished.');
|
||||
}
|
||||
|
||||
async getTablesFromDb() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db.all("SELECT name FROM sqlite_master WHERE type='table'", (err, tables) => {
|
||||
if (err) return reject(err);
|
||||
resolve(tables.map(t => t.name));
|
||||
});
|
||||
});
|
||||
getTablesFromDb() {
|
||||
const tables = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
||||
return tables.map(t => t.name);
|
||||
}
|
||||
|
||||
async createTable(tableName, tableSchema) {
|
||||
return new Promise((resolve, reject) => {
|
||||
createTable(tableName, tableSchema) {
|
||||
const columnDefs = tableSchema.columns.map(col => `"${col.name}" ${col.type}`).join(', ');
|
||||
const query = `CREATE TABLE IF NOT EXISTS "${tableName}" (${columnDefs})`;
|
||||
|
||||
console.log(`[DB Sync] Creating table: ${tableName}`);
|
||||
this.db.run(query, (err) => {
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
this.db.prepare(query).run();
|
||||
}
|
||||
|
||||
async updateTable(tableName, tableSchema) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db.all(`PRAGMA table_info("${tableName}")`, async (err, existingColumns) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
updateTable(tableName, tableSchema) {
|
||||
const existingColumns = this.db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
||||
const existingColumnNames = existingColumns.map(c => c.name);
|
||||
const columnsToAdd = tableSchema.columns.filter(col => !existingColumnNames.includes(col.name));
|
||||
|
||||
@ -93,28 +73,16 @@ class SQLiteClient {
|
||||
console.log(`[DB Sync] Updating table: ${tableName}. Adding columns: ${columnsToAdd.map(c=>c.name).join(', ')}`);
|
||||
for (const column of columnsToAdd) {
|
||||
const addColumnQuery = `ALTER TABLE "${tableName}" ADD COLUMN "${column.name}" ${column.type}`;
|
||||
try {
|
||||
await this.runQuery(addColumnQuery);
|
||||
} catch (alterErr) {
|
||||
return reject(alterErr);
|
||||
this.db.prepare(addColumnQuery).run();
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async runQuery(query, params = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db.run(query, params, function(err) {
|
||||
if (err) return reject(err);
|
||||
resolve(this);
|
||||
});
|
||||
});
|
||||
runQuery(query, params = []) {
|
||||
return this.db.prepare(query).run(params);
|
||||
}
|
||||
|
||||
async cleanupEmptySessions() {
|
||||
cleanupEmptySessions() {
|
||||
console.log('[DB Cleanup] Checking for empty sessions...');
|
||||
const query = `
|
||||
SELECT s.id FROM sessions s
|
||||
@ -124,16 +92,11 @@ class SQLiteClient {
|
||||
WHERE t.id IS NULL AND a.id IS NULL AND su.session_id IS NULL
|
||||
`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db.all(query, [], (err, rows) => {
|
||||
if (err) {
|
||||
console.error('[DB Cleanup] Error finding empty sessions:', err);
|
||||
return reject(err);
|
||||
}
|
||||
const rows = this.db.prepare(query).all();
|
||||
|
||||
if (rows.length === 0) {
|
||||
console.log('[DB Cleanup] No empty sessions found.');
|
||||
return resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const idsToDelete = rows.map(r => r.id);
|
||||
@ -141,36 +104,23 @@ class SQLiteClient {
|
||||
const deleteQuery = `DELETE FROM sessions WHERE id IN (${placeholders})`;
|
||||
|
||||
console.log(`[DB Cleanup] Found ${idsToDelete.length} empty sessions. Deleting...`);
|
||||
this.db.run(deleteQuery, idsToDelete, function(deleteErr) {
|
||||
if (deleteErr) {
|
||||
console.error('[DB Cleanup] Error deleting empty sessions:', deleteErr);
|
||||
return reject(deleteErr);
|
||||
}
|
||||
console.log(`[DB Cleanup] Successfully deleted ${this.changes} empty sessions.`);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
const result = this.db.prepare(deleteQuery).run(idsToDelete);
|
||||
console.log(`[DB Cleanup] Successfully deleted ${result.changes} empty sessions.`);
|
||||
}
|
||||
|
||||
async initTables() {
|
||||
await this.synchronizeSchema();
|
||||
await this.initDefaultData();
|
||||
initTables() {
|
||||
this.synchronizeSchema();
|
||||
this.initDefaultData();
|
||||
}
|
||||
|
||||
async initDefaultData() {
|
||||
return new Promise((resolve, reject) => {
|
||||
initDefaultData() {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const initUserQuery = `
|
||||
INSERT OR IGNORE INTO users (uid, display_name, email, created_at)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
this.db.run(initUserQuery, [this.defaultUserId, 'Default User', 'contact@pickle.com', now], (err) => {
|
||||
if (err) {
|
||||
console.error('Failed to initialize default user:', err);
|
||||
return reject(err);
|
||||
}
|
||||
this.db.prepare(initUserQuery).run(this.defaultUserId, 'Default User', 'contact@pickle.com', now);
|
||||
|
||||
const defaultPresets = [
|
||||
['school', 'School', 'You are a school and lecture assistant. Your goal is to help the user, a student, understand academic material and answer questions.\n\nWhenever a question appears on the user\'s screen or is asked aloud, you provide a direct, step-by-step answer, showing all necessary reasoning or calculations.\n\nIf the user is watching a lecture or working through new material, you offer concise explanations of key concepts and clarify definitions as they come up.', 1],
|
||||
@ -189,27 +139,18 @@ class SQLiteClient {
|
||||
stmt.run(preset[0], this.defaultUserId, preset[1], preset[2], preset[3], now);
|
||||
}
|
||||
|
||||
stmt.finalize((err) => {
|
||||
if (err) {
|
||||
console.error('Failed to finalize preset statement:', err);
|
||||
return reject(err);
|
||||
}
|
||||
console.log('Default data initialized.');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async markPermissionsAsCompleted() {
|
||||
markPermissionsAsCompleted() {
|
||||
return this.query(
|
||||
'INSERT OR REPLACE INTO system_settings (key, value) VALUES (?, ?)',
|
||||
['permissions_completed', 'true']
|
||||
);
|
||||
}
|
||||
|
||||
async checkPermissionsCompleted() {
|
||||
const result = await this.query(
|
||||
checkPermissionsCompleted() {
|
||||
const result = this.query(
|
||||
'SELECT value FROM system_settings WHERE key = ?',
|
||||
['permissions_completed']
|
||||
);
|
||||
@ -218,43 +159,32 @@ class SQLiteClient {
|
||||
|
||||
close() {
|
||||
if (this.db) {
|
||||
this.db.close((err) => {
|
||||
if (err) {
|
||||
console.error('SQLite connection close failed:', err);
|
||||
} else {
|
||||
try {
|
||||
this.db.close();
|
||||
console.log('SQLite connection closed.');
|
||||
} catch (err) {
|
||||
console.error('SQLite connection close failed:', err);
|
||||
}
|
||||
});
|
||||
this.db = null;
|
||||
}
|
||||
}
|
||||
|
||||
async query(sql, params = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
query(sql, params = []) {
|
||||
if (!this.db) {
|
||||
return reject(new Error('Database not connected'));
|
||||
throw new Error('Database not connected');
|
||||
}
|
||||
|
||||
try {
|
||||
if (sql.toUpperCase().startsWith('SELECT')) {
|
||||
this.db.all(sql, params, (err, rows) => {
|
||||
if (err) {
|
||||
return this.db.prepare(sql).all(params);
|
||||
} else {
|
||||
const result = this.db.prepare(sql).run(params);
|
||||
return { changes: result.changes, lastID: result.lastID };
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Query error:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(rows);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.db.run(sql, params, function(err) {
|
||||
if (err) {
|
||||
console.error('Query error:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({ changes: this.changes, lastID: this.lastID });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
const { BrowserWindow, globalShortcut, ipcMain, screen, app, shell, desktopCapturer } = require('electron');
|
||||
const WindowLayoutManager = require('./windowLayoutManager');
|
||||
const SmoothMovementManager = require('./smoothMovementManager');
|
||||
const liquidGlass = require('electron-liquid-glass');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const os = require('os');
|
||||
@ -15,6 +14,7 @@ const fetch = require('node-fetch');
|
||||
|
||||
|
||||
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
||||
let liquidGlass;
|
||||
const isLiquidGlassSupported = () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
return false;
|
||||
@ -23,7 +23,15 @@ const isLiquidGlassSupported = () => {
|
||||
// return majorVersion >= 25; // macOS 26+ (Darwin 25+)
|
||||
return majorVersion >= 26; // See you soon!
|
||||
};
|
||||
const shouldUseLiquidGlass = isLiquidGlassSupported();
|
||||
let shouldUseLiquidGlass = isLiquidGlassSupported();
|
||||
if (shouldUseLiquidGlass) {
|
||||
try {
|
||||
liquidGlass = require('electron-liquid-glass');
|
||||
} catch (e) {
|
||||
console.warn('Could not load optional dependency "electron-liquid-glass". The feature will be disabled.');
|
||||
shouldUseLiquidGlass = false;
|
||||
}
|
||||
}
|
||||
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
||||
|
||||
let isContentProtectionOn = true;
|
||||
|
@ -2,31 +2,23 @@ const sqliteClient = require('../../../common/services/sqliteClient');
|
||||
|
||||
function addAiMessage({ sessionId, role, content, model = 'gpt-4.1' }) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const messageId = require('crypto').randomUUID();
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = `INSERT INTO ai_messages (id, session_id, sent_at, role, content, model, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`;
|
||||
db.run(query, [messageId, sessionId, now, role, content, model, now], function(err) {
|
||||
if (err) {
|
||||
|
||||
try {
|
||||
db.prepare(query).run(messageId, sessionId, now, role, content, model, now);
|
||||
return { id: messageId };
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to add AI message:', err);
|
||||
reject(err);
|
||||
throw err;
|
||||
}
|
||||
else {
|
||||
resolve({ id: messageId });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getAllAiMessagesBySessionId(sessionId) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = "SELECT * FROM ai_messages WHERE session_id = ? ORDER BY sent_at ASC";
|
||||
db.all(query, [sessionId], (err, rows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows);
|
||||
});
|
||||
});
|
||||
return db.prepare(query).all(sessionId);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -2,33 +2,23 @@ const sqliteClient = require('../../../../common/services/sqliteClient');
|
||||
|
||||
function addTranscript({ sessionId, speaker, text }) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transcriptId = require('crypto').randomUUID();
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = `INSERT INTO transcripts (id, session_id, start_at, speaker, text, created_at) VALUES (?, ?, ?, ?, ?, ?)`;
|
||||
db.run(query, [transcriptId, sessionId, now, speaker, text, now], function(err) {
|
||||
if (err) {
|
||||
|
||||
try {
|
||||
db.prepare(query).run(transcriptId, sessionId, now, speaker, text, now);
|
||||
return { id: transcriptId };
|
||||
} catch (err) {
|
||||
console.error('Error adding transcript:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({ id: transcriptId });
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getAllTranscriptsBySessionId(sessionId) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = "SELECT * FROM transcripts WHERE session_id = ? ORDER BY start_at ASC";
|
||||
db.all(query, [sessionId], (err, rows) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(rows);
|
||||
}
|
||||
});
|
||||
});
|
||||
return db.prepare(query).all(sessionId);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -2,7 +2,6 @@ const sqliteClient = require('../../../../common/services/sqliteClient');
|
||||
|
||||
function saveSummary({ sessionId, tldr, text, bullet_json, action_json, model = 'gpt-4.1' }) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = `
|
||||
INSERT INTO summaries (session_id, generated_at, model, text, tldr, bullet_json, action_json, updated_at)
|
||||
@ -16,29 +15,20 @@ function saveSummary({ sessionId, tldr, text, bullet_json, action_json, model =
|
||||
action_json=excluded.action_json,
|
||||
updated_at=excluded.updated_at
|
||||
`;
|
||||
db.run(query, [sessionId, now, model, text, tldr, bullet_json, action_json, now], function(err) {
|
||||
if (err) {
|
||||
|
||||
try {
|
||||
const result = db.prepare(query).run(sessionId, now, model, text, tldr, bullet_json, action_json, now);
|
||||
return { changes: result.changes };
|
||||
} catch (err) {
|
||||
console.error('Error saving summary:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({ changes: this.changes });
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getSummaryBySessionId(sessionId) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = "SELECT * FROM summaries WHERE session_id = ?";
|
||||
db.get(query, [sessionId], (err, row) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(row || null);
|
||||
}
|
||||
});
|
||||
});
|
||||
return db.prepare(query).get(sessionId) || null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -2,102 +2,92 @@ const sqliteClient = require('../../../common/services/sqliteClient');
|
||||
|
||||
function getPresets(uid) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = `
|
||||
SELECT * FROM prompt_presets
|
||||
WHERE uid = ? OR is_default = 1
|
||||
ORDER BY is_default DESC, title ASC
|
||||
`;
|
||||
db.all(query, [uid], (err, rows) => {
|
||||
if (err) {
|
||||
|
||||
try {
|
||||
return db.prepare(query).all(uid) || [];
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to get presets:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(rows || []);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getPresetTemplates() {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = `
|
||||
SELECT * FROM prompt_presets
|
||||
WHERE is_default = 1
|
||||
ORDER BY title ASC
|
||||
`;
|
||||
db.all(query, [], (err, rows) => {
|
||||
if (err) {
|
||||
|
||||
try {
|
||||
return db.prepare(query).all() || [];
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to get preset templates:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(rows || []);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createPreset({ uid, title, prompt }) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = require('crypto').randomUUID();
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = `
|
||||
INSERT INTO prompt_presets (id, uid, title, prompt, is_default, created_at, sync_state)
|
||||
VALUES (?, ?, ?, ?, 0, ?, 'dirty')
|
||||
`;
|
||||
db.run(query, [id, uid, title, prompt, now], function(err) {
|
||||
if (err) {
|
||||
|
||||
try {
|
||||
db.prepare(query).run(id, uid, title, prompt, now);
|
||||
return { id };
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to create preset:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({ id });
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updatePreset(id, { title, prompt }, uid) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const query = `
|
||||
UPDATE prompt_presets
|
||||
SET title = ?, prompt = ?, sync_state = 'dirty', updated_at = ?
|
||||
WHERE id = ? AND uid = ? AND is_default = 0
|
||||
`;
|
||||
db.run(query, [title, prompt, now, id, uid], function(err) {
|
||||
if (err) {
|
||||
console.error('SQLite: Failed to update preset:', err);
|
||||
reject(err);
|
||||
} else if (this.changes === 0) {
|
||||
reject(new Error('Preset not found, is default, or permission denied'));
|
||||
} else {
|
||||
resolve({ changes: this.changes });
|
||||
|
||||
try {
|
||||
const result = db.prepare(query).run(title, prompt, now, id, uid);
|
||||
if (result.changes === 0) {
|
||||
throw new Error('Preset not found, is default, or permission denied');
|
||||
}
|
||||
return { changes: result.changes };
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to update preset:', err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deletePreset(id, uid) {
|
||||
const db = sqliteClient.getDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = `
|
||||
DELETE FROM prompt_presets
|
||||
WHERE id = ? AND uid = ? AND is_default = 0
|
||||
`;
|
||||
db.run(query, [id, uid], function(err) {
|
||||
if (err) {
|
||||
console.error('SQLite: Failed to delete preset:', err);
|
||||
reject(err);
|
||||
} else if (this.changes === 0) {
|
||||
reject(new Error('Preset not found, is default, or permission denied'));
|
||||
} else {
|
||||
resolve({ changes: this.changes });
|
||||
|
||||
try {
|
||||
const result = db.prepare(query).run(id, uid);
|
||||
if (result.changes === 0) {
|
||||
throw new Error('Preset not found, is default, or permission denied');
|
||||
}
|
||||
return { changes: result.changes };
|
||||
} catch (err) {
|
||||
console.error('SQLite: Failed to delete preset:', err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
195
src/index.js
195
src/index.js
@ -18,7 +18,6 @@ const { initializeFirebase } = require('./common/services/firebaseClient');
|
||||
const databaseInitializer = require('./common/services/databaseInitializer');
|
||||
const authService = require('./common/services/authService');
|
||||
const path = require('node:path');
|
||||
const { Deeplink } = require('electron-deeplink');
|
||||
const express = require('express');
|
||||
const fetch = require('node-fetch');
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
@ -33,72 +32,86 @@ let WEB_PORT = 3000;
|
||||
const listenService = new ListenService();
|
||||
// Make listenService globally accessible so other modules (e.g., windowManager, askService) can reuse the same instance
|
||||
global.listenService = listenService;
|
||||
let deeplink = null; // Initialize as null
|
||||
let pendingDeepLinkUrl = null; // Store any deep link that arrives before initialization
|
||||
|
||||
function createMainWindows() {
|
||||
createWindows();
|
||||
// Native deep link handling - cross-platform compatible
|
||||
let pendingDeepLinkUrl = null;
|
||||
|
||||
const { windowPool } = require('./electron/windowManager');
|
||||
const headerWindow = windowPool.get('header');
|
||||
function setupProtocolHandling() {
|
||||
// Protocol registration - must be done before app is ready
|
||||
if (!app.isDefaultProtocolClient('pickleglass')) {
|
||||
app.setAsDefaultProtocolClient('pickleglass');
|
||||
console.log('[Protocol] Set as default protocol client for pickleglass://');
|
||||
}
|
||||
|
||||
// Initialize deeplink after windows are created
|
||||
if (!deeplink && headerWindow) {
|
||||
try {
|
||||
deeplink = new Deeplink({
|
||||
app,
|
||||
mainWindow: headerWindow,
|
||||
protocol: 'pickleglass',
|
||||
isDev: !app.isPackaged,
|
||||
debugLogging: true
|
||||
// Handle protocol URLs on Windows/Linux
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
// Focus existing window first
|
||||
focusMainWindow();
|
||||
|
||||
// Look for protocol URL in command line arguments
|
||||
const protocolUrl = commandLine.find(arg => arg.startsWith('pickleglass://'));
|
||||
if (protocolUrl) {
|
||||
console.log('[Protocol] Received URL from second instance:', protocolUrl);
|
||||
handleCustomUrl(protocolUrl);
|
||||
}
|
||||
});
|
||||
|
||||
deeplink.on('received', (url) => {
|
||||
console.log('[deeplink] received:', url);
|
||||
// Handle protocol URLs on macOS
|
||||
app.on('open-url', (event, url) => {
|
||||
event.preventDefault();
|
||||
console.log('[Protocol] Received URL via open-url:', url);
|
||||
|
||||
if (!url || !url.startsWith('pickleglass://')) {
|
||||
console.warn('[Protocol] Invalid URL format:', url);
|
||||
return;
|
||||
}
|
||||
|
||||
if (app.isReady()) {
|
||||
handleCustomUrl(url);
|
||||
} else {
|
||||
pendingDeepLinkUrl = url;
|
||||
console.log('[Protocol] App not ready, storing URL for later');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('[deeplink] Initialized with main window');
|
||||
function focusMainWindow() {
|
||||
const { windowPool } = require('./electron/windowManager');
|
||||
if (windowPool) {
|
||||
const header = windowPool.get('header');
|
||||
if (header && !header.isDestroyed()) {
|
||||
if (header.isMinimized()) header.restore();
|
||||
header.focus();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle any pending deep link
|
||||
if (pendingDeepLinkUrl) {
|
||||
console.log('[deeplink] Processing pending deep link:', pendingDeepLinkUrl);
|
||||
handleCustomUrl(pendingDeepLinkUrl);
|
||||
pendingDeepLinkUrl = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[deeplink] Failed to initialize deep link:', error);
|
||||
deeplink = null;
|
||||
// Fallback: focus any available window
|
||||
const windows = BrowserWindow.getAllWindows();
|
||||
if (windows.length > 0) {
|
||||
const mainWindow = windows[0];
|
||||
if (!mainWindow.isDestroyed()) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||
mainWindow.focus();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup protocol handling before app.whenReady()
|
||||
setupProtocolHandling();
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
// Single instance lock - must be first
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
if (!gotTheLock) {
|
||||
app.quit();
|
||||
return;
|
||||
} else {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
const { windowPool } = require('./electron/windowManager');
|
||||
if (windowPool) {
|
||||
const header = windowPool.get('header');
|
||||
if (header) {
|
||||
if (header.isMinimized()) header.restore();
|
||||
header.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const windows = BrowserWindow.getAllWindows();
|
||||
if (windows.length > 0) {
|
||||
const mainWindow = windows[0];
|
||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||
mainWindow.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize core services
|
||||
initializeFirebase();
|
||||
|
||||
databaseInitializer.initialize()
|
||||
@ -121,9 +134,16 @@ app.whenReady().then(async () => {
|
||||
WEB_PORT = await startWebStack();
|
||||
console.log('Web front-end listening on', WEB_PORT);
|
||||
|
||||
createMainWindows();
|
||||
createWindows();
|
||||
|
||||
initAutoUpdater();
|
||||
|
||||
// Process any pending deep link after everything is initialized
|
||||
if (pendingDeepLinkUrl) {
|
||||
console.log('[Protocol] Processing pending URL:', pendingDeepLinkUrl);
|
||||
handleCustomUrl(pendingDeepLinkUrl);
|
||||
pendingDeepLinkUrl = null;
|
||||
}
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
@ -142,34 +162,17 @@ app.on('before-quit', async () => {
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createMainWindows();
|
||||
createWindows();
|
||||
}
|
||||
});
|
||||
|
||||
// Add macOS native deep link handling as fallback
|
||||
app.on('open-url', (event, url) => {
|
||||
event.preventDefault();
|
||||
console.log('[app] open-url received:', url);
|
||||
|
||||
if (!deeplink) {
|
||||
// Store the URL if deeplink isn't ready yet
|
||||
pendingDeepLinkUrl = url;
|
||||
console.log('[app] Deep link stored for later processing');
|
||||
} else {
|
||||
handleCustomUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure app can handle the protocol
|
||||
app.setAsDefaultProtocolClient('pickleglass');
|
||||
|
||||
function setupGeneralIpcHandlers() {
|
||||
const userRepository = require('./common/repositories/user');
|
||||
const presetRepository = require('./common/repositories/preset');
|
||||
|
||||
ipcMain.handle('save-api-key', async (event, apiKey) => {
|
||||
ipcMain.handle('save-api-key', (event, apiKey) => {
|
||||
try {
|
||||
await userRepository.saveApiKey(apiKey, authService.getCurrentUserId());
|
||||
userRepository.saveApiKey(apiKey, authService.getCurrentUserId());
|
||||
BrowserWindow.getAllWindows().forEach(win => {
|
||||
win.webContents.send('api-key-updated');
|
||||
});
|
||||
@ -180,12 +183,12 @@ function setupGeneralIpcHandlers() {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-user-presets', async () => {
|
||||
return await presetRepository.getPresets(authService.getCurrentUserId());
|
||||
ipcMain.handle('get-user-presets', () => {
|
||||
return presetRepository.getPresets(authService.getCurrentUserId());
|
||||
});
|
||||
|
||||
ipcMain.handle('get-preset-templates', async () => {
|
||||
return await presetRepository.getPresetTemplates();
|
||||
ipcMain.handle('get-preset-templates', () => {
|
||||
return presetRepository.getPresetTemplates();
|
||||
});
|
||||
|
||||
ipcMain.handle('start-firebase-auth', async () => {
|
||||
@ -204,7 +207,7 @@ function setupGeneralIpcHandlers() {
|
||||
return process.env.pickleglass_WEB_URL || 'http://localhost:3000';
|
||||
});
|
||||
|
||||
ipcMain.handle('get-current-user', async () => {
|
||||
ipcMain.handle('get-current-user', () => {
|
||||
return authService.getCurrentUser();
|
||||
});
|
||||
|
||||
@ -220,72 +223,72 @@ function setupWebDataHandlers() {
|
||||
const userRepository = require('./common/repositories/user');
|
||||
const presetRepository = require('./common/repositories/preset');
|
||||
|
||||
const handleRequest = async (channel, responseChannel, payload) => {
|
||||
const handleRequest = (channel, responseChannel, payload) => {
|
||||
let result;
|
||||
const currentUserId = authService.getCurrentUserId();
|
||||
try {
|
||||
switch (channel) {
|
||||
// SESSION
|
||||
case 'get-sessions':
|
||||
result = await sessionRepository.getAllByUserId(currentUserId);
|
||||
result = sessionRepository.getAllByUserId(currentUserId);
|
||||
break;
|
||||
case 'get-session-details':
|
||||
const session = await sessionRepository.getById(payload);
|
||||
const session = sessionRepository.getById(payload);
|
||||
if (!session) {
|
||||
result = null;
|
||||
break;
|
||||
}
|
||||
const transcripts = await sttRepository.getAllTranscriptsBySessionId(payload);
|
||||
const ai_messages = await askRepository.getAllAiMessagesBySessionId(payload);
|
||||
const summary = await summaryRepository.getSummaryBySessionId(payload);
|
||||
const transcripts = sttRepository.getAllTranscriptsBySessionId(payload);
|
||||
const ai_messages = askRepository.getAllAiMessagesBySessionId(payload);
|
||||
const summary = summaryRepository.getSummaryBySessionId(payload);
|
||||
result = { session, transcripts, ai_messages, summary };
|
||||
break;
|
||||
case 'delete-session':
|
||||
result = await sessionRepository.deleteWithRelatedData(payload);
|
||||
result = sessionRepository.deleteWithRelatedData(payload);
|
||||
break;
|
||||
case 'create-session':
|
||||
const id = await sessionRepository.create(currentUserId, 'ask');
|
||||
const id = sessionRepository.create(currentUserId, 'ask');
|
||||
if (payload.title) {
|
||||
await sessionRepository.updateTitle(id, payload.title);
|
||||
sessionRepository.updateTitle(id, payload.title);
|
||||
}
|
||||
result = { id };
|
||||
break;
|
||||
|
||||
// USER
|
||||
case 'get-user-profile':
|
||||
result = await userRepository.getById(currentUserId);
|
||||
result = userRepository.getById(currentUserId);
|
||||
break;
|
||||
case 'update-user-profile':
|
||||
result = await userRepository.update({ uid: currentUserId, ...payload });
|
||||
result = userRepository.update({ uid: currentUserId, ...payload });
|
||||
break;
|
||||
case 'find-or-create-user':
|
||||
result = await userRepository.findOrCreate(payload);
|
||||
result = userRepository.findOrCreate(payload);
|
||||
break;
|
||||
case 'save-api-key':
|
||||
result = await userRepository.saveApiKey(payload, currentUserId);
|
||||
result = userRepository.saveApiKey(payload, currentUserId);
|
||||
break;
|
||||
case 'check-api-key-status':
|
||||
const user = await userRepository.getById(currentUserId);
|
||||
const user = userRepository.getById(currentUserId);
|
||||
result = { hasApiKey: !!user?.api_key && user.api_key.length > 0 };
|
||||
break;
|
||||
case 'delete-account':
|
||||
result = await userRepository.deleteById(currentUserId);
|
||||
result = userRepository.deleteById(currentUserId);
|
||||
break;
|
||||
|
||||
// PRESET
|
||||
case 'get-presets':
|
||||
result = await presetRepository.getPresets(currentUserId);
|
||||
result = presetRepository.getPresets(currentUserId);
|
||||
break;
|
||||
case 'create-preset':
|
||||
result = await presetRepository.create({ ...payload, uid: currentUserId });
|
||||
result = presetRepository.create({ ...payload, uid: currentUserId });
|
||||
settingsService.notifyPresetUpdate('created', result.id, payload.title);
|
||||
break;
|
||||
case 'update-preset':
|
||||
result = await presetRepository.update(payload.id, payload.data, currentUserId);
|
||||
result = presetRepository.update(payload.id, payload.data, currentUserId);
|
||||
settingsService.notifyPresetUpdate('updated', payload.id, payload.data.title);
|
||||
break;
|
||||
case 'delete-preset':
|
||||
result = await presetRepository.delete(payload, currentUserId);
|
||||
result = presetRepository.delete(payload, currentUserId);
|
||||
settingsService.notifyPresetUpdate('deleted', payload);
|
||||
break;
|
||||
|
||||
@ -295,13 +298,13 @@ function setupWebDataHandlers() {
|
||||
const batchResult = {};
|
||||
|
||||
if (includes.includes('profile')) {
|
||||
batchResult.profile = await userRepository.getById(currentUserId);
|
||||
batchResult.profile = userRepository.getById(currentUserId);
|
||||
}
|
||||
if (includes.includes('presets')) {
|
||||
batchResult.presets = await presetRepository.getPresets(currentUserId);
|
||||
batchResult.presets = presetRepository.getPresets(currentUserId);
|
||||
}
|
||||
if (includes.includes('sessions')) {
|
||||
batchResult.sessions = await sessionRepository.getAllByUserId(currentUserId);
|
||||
batchResult.sessions = sessionRepository.getAllByUserId(currentUserId);
|
||||
}
|
||||
result = batchResult;
|
||||
break;
|
||||
@ -392,7 +395,7 @@ async function handleFirebaseAuthCallback(params) {
|
||||
};
|
||||
|
||||
// 1. Sync user data to local DB
|
||||
await userRepository.findOrCreate(firebaseUser);
|
||||
userRepository.findOrCreate(firebaseUser);
|
||||
console.log('[Auth] User data synced with local DB.');
|
||||
|
||||
// 2. Sign in using the authService in the main process
|
||||
|
Loading…
x
Reference in New Issue
Block a user