fix dependency
This commit is contained in:
parent
86e903ec4b
commit
427be2a293
1
.npmrc
1
.npmrc
@ -1,3 +1,2 @@
|
|||||||
better-sqlite3:ignore-scripts=true
|
better-sqlite3:ignore-scripts=true
|
||||||
electron-deeplink:ignore-scripts=true
|
|
||||||
sharp:ignore-scripts=true
|
sharp:ignore-scripts=true
|
@ -10,6 +10,7 @@
|
|||||||
"package": "npm run build:renderer && electron-forge package",
|
"package": "npm run build:renderer && electron-forge package",
|
||||||
"make": "npm run build:renderer && electron-forge make",
|
"make": "npm run build:renderer && electron-forge make",
|
||||||
"build": "npm run build:renderer && electron-builder --config electron-builder.yml --publish never",
|
"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",
|
"publish": "npm run build:renderer && electron-builder --config electron-builder.yml --publish always",
|
||||||
"lint": "eslint --ext .ts,.tsx,.js .",
|
"lint": "eslint --ext .ts,.tsx,.js .",
|
||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
@ -35,7 +36,7 @@
|
|||||||
"better-sqlite3": "^9.4.3",
|
"better-sqlite3": "^9.4.3",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.0.0",
|
"dotenv": "^17.0.0",
|
||||||
"electron-deeplink": "^1.0.10",
|
|
||||||
"electron-squirrel-startup": "^1.0.1",
|
"electron-squirrel-startup": "^1.0.1",
|
||||||
"electron-store": "^8.2.0",
|
"electron-store": "^8.2.0",
|
||||||
"electron-updater": "^6.6.2",
|
"electron-updater": "^6.6.2",
|
||||||
@ -47,7 +48,6 @@
|
|||||||
"openai": "^4.70.0",
|
"openai": "^4.70.0",
|
||||||
"react-hot-toast": "^2.5.2",
|
"react-hot-toast": "^2.5.2",
|
||||||
"sharp": "^0.34.2",
|
"sharp": "^0.34.2",
|
||||||
"sqlite3": "^5.1.7",
|
|
||||||
"validator": "^13.11.0",
|
"validator": "^13.11.0",
|
||||||
"wait-on": "^8.0.3",
|
"wait-on": "^8.0.3",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
@ -69,8 +69,6 @@
|
|||||||
"esbuild": "^0.25.5"
|
"esbuild": "^0.25.5"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-darwin-x64": "^0.34.2",
|
|
||||||
"@img/sharp-libvips-darwin-x64": "^1.1.0",
|
|
||||||
"electron-liquid-glass": "^1.0.1"
|
"electron-liquid-glass": "^1.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,40 +2,34 @@ const sqliteClient = require('../../services/sqliteClient');
|
|||||||
|
|
||||||
function getPresets(uid) {
|
function getPresets(uid) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const query = `
|
||||||
const query = `
|
SELECT * FROM prompt_presets
|
||||||
SELECT * FROM prompt_presets
|
WHERE uid = ? OR is_default = 1
|
||||||
WHERE uid = ? OR is_default = 1
|
ORDER BY is_default DESC, title ASC
|
||||||
ORDER BY is_default DESC, title ASC
|
`;
|
||||||
`;
|
|
||||||
db.all(query, [uid], (err, rows) => {
|
try {
|
||||||
if (err) {
|
return db.prepare(query).all(uid);
|
||||||
console.error('SQLite: Failed to get presets:', err);
|
} catch (err) {
|
||||||
reject(err);
|
console.error('SQLite: Failed to get presets:', err);
|
||||||
} else {
|
throw err;
|
||||||
resolve(rows);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPresetTemplates() {
|
function getPresetTemplates() {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const query = `
|
||||||
const query = `
|
SELECT * FROM prompt_presets
|
||||||
SELECT * FROM prompt_presets
|
WHERE is_default = 1
|
||||||
WHERE is_default = 1
|
ORDER BY title ASC
|
||||||
ORDER BY title ASC
|
`;
|
||||||
`;
|
|
||||||
db.all(query, [], (err, rows) => {
|
try {
|
||||||
if (err) {
|
return db.prepare(query).all();
|
||||||
console.error('SQLite: Failed to get preset templates:', err);
|
} catch (err) {
|
||||||
reject(err);
|
console.error('SQLite: Failed to get preset templates:', err);
|
||||||
} else {
|
throw err;
|
||||||
resolve(rows);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function create({ uid, title, prompt }) {
|
function create({ uid, title, prompt }) {
|
||||||
@ -44,38 +38,42 @@ function create({ uid, title, prompt }) {
|
|||||||
const now = Math.floor(Date.now() / 1000);
|
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')`;
|
const query = `INSERT INTO prompt_presets (id, uid, title, prompt, is_default, created_at, sync_state) VALUES (?, ?, ?, ?, 0, ?, 'dirty')`;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
try {
|
||||||
db.run(query, [presetId, uid, title, prompt, now], function(err) {
|
db.prepare(query).run(presetId, uid, title, prompt, now);
|
||||||
if (err) reject(err);
|
return { id: presetId };
|
||||||
else resolve({ id: presetId });
|
} catch (err) {
|
||||||
});
|
throw err;
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function update(id, { title, prompt }, uid) {
|
function update(id, { title, prompt }, uid) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
const query = `UPDATE prompt_presets SET title = ?, prompt = ?, sync_state = 'dirty' WHERE id = ? AND uid = ? AND is_default = 0`;
|
const query = `UPDATE prompt_presets SET title = ?, prompt = ?, sync_state = 'dirty' WHERE id = ? AND uid = ? AND is_default = 0`;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
try {
|
||||||
db.run(query, [title, prompt, id, uid], function(err) {
|
const result = db.prepare(query).run(title, prompt, id, uid);
|
||||||
if (err) reject(err);
|
if (result.changes === 0) {
|
||||||
else if (this.changes === 0) reject(new Error("Preset not found or permission denied."));
|
throw new Error("Preset not found or permission denied.");
|
||||||
else resolve({ changes: this.changes });
|
}
|
||||||
});
|
return { changes: result.changes };
|
||||||
});
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(id, uid) {
|
function del(id, uid) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
const query = `DELETE FROM prompt_presets WHERE id = ? AND uid = ? AND is_default = 0`;
|
const query = `DELETE FROM prompt_presets WHERE id = ? AND uid = ? AND is_default = 0`;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
try {
|
||||||
db.run(query, [id, uid], function(err) {
|
const result = db.prepare(query).run(id, uid);
|
||||||
if (err) reject(err);
|
if (result.changes === 0) {
|
||||||
else if (this.changes === 0) reject(new Error("Preset not found or permission denied."));
|
throw new Error("Preset not found or permission denied.");
|
||||||
else resolve({ changes: this.changes });
|
}
|
||||||
});
|
return { changes: result.changes };
|
||||||
});
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -2,124 +2,79 @@ const sqliteClient = require('../../services/sqliteClient');
|
|||||||
|
|
||||||
function getById(id) {
|
function getById(id) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
return db.prepare('SELECT * FROM sessions WHERE id = ?').get(id);
|
||||||
db.get('SELECT * FROM sessions WHERE id = ?', [id], (err, row) => {
|
|
||||||
if (err) reject(err);
|
|
||||||
else resolve(row);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function create(uid, type = 'ask') {
|
function create(uid, type = 'ask') {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const sessionId = require('crypto').randomUUID();
|
||||||
const sessionId = require('crypto').randomUUID();
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = `INSERT INTO sessions (id, uid, title, session_type, started_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`;
|
||||||
const query = `INSERT INTO sessions (id, uid, title, session_type, started_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`;
|
|
||||||
|
try {
|
||||||
db.run(query, [sessionId, uid, `Session @ ${new Date().toLocaleTimeString()}`, type, now, now], function(err) {
|
db.prepare(query).run(sessionId, uid, `Session @ ${new Date().toLocaleTimeString()}`, type, now, now);
|
||||||
if (err) {
|
console.log(`SQLite: Created session ${sessionId} for user ${uid} (type: ${type})`);
|
||||||
console.error('SQLite: Failed to create session:', err);
|
return sessionId;
|
||||||
reject(err);
|
} catch (err) {
|
||||||
} else {
|
console.error('SQLite: Failed to create session:', err);
|
||||||
console.log(`SQLite: Created session ${sessionId} for user ${uid} (type: ${type})`);
|
throw err;
|
||||||
resolve(sessionId);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllByUserId(uid) {
|
function getAllByUserId(uid) {
|
||||||
const db = sqliteClient.getDb();
|
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";
|
||||||
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";
|
return db.prepare(query).all(uid);
|
||||||
db.all(query, [uid], (err, rows) => {
|
|
||||||
if (err) reject(err);
|
|
||||||
else resolve(rows);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTitle(id, title) {
|
function updateTitle(id, title) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const result = db.prepare('UPDATE sessions SET title = ? WHERE id = ?').run(title, id);
|
||||||
db.run('UPDATE sessions SET title = ? WHERE id = ?', [title, id], function(err) {
|
return { changes: result.changes };
|
||||||
if (err) reject(err);
|
|
||||||
else resolve({ changes: this.changes });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteWithRelatedData(id) {
|
function deleteWithRelatedData(id) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const transaction = db.transaction(() => {
|
||||||
db.serialize(() => {
|
db.prepare("DELETE FROM transcripts WHERE session_id = ?").run(id);
|
||||||
db.run("BEGIN TRANSACTION;");
|
db.prepare("DELETE FROM ai_messages WHERE session_id = ?").run(id);
|
||||||
const queries = [
|
db.prepare("DELETE FROM summaries WHERE session_id = ?").run(id);
|
||||||
"DELETE FROM transcripts WHERE session_id = ?",
|
db.prepare("DELETE FROM sessions WHERE id = ?").run(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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
db.run("COMMIT;", (err) => {
|
|
||||||
if (err) {
|
|
||||||
db.run("ROLLBACK;");
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
resolve({ success: true });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
transaction();
|
||||||
|
return { success: true };
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function end(id) {
|
function end(id) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE id = ?`;
|
||||||
const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE id = ?`;
|
const result = db.prepare(query).run(now, now, id);
|
||||||
db.run(query, [now, now, id], function(err) {
|
return { changes: result.changes };
|
||||||
if (err) reject(err);
|
|
||||||
else resolve({ changes: this.changes });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateType(id, type) {
|
function updateType(id, type) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = 'UPDATE sessions SET session_type = ?, updated_at = ? WHERE id = ?';
|
||||||
const query = 'UPDATE sessions SET session_type = ?, updated_at = ? WHERE id = ?';
|
const result = db.prepare(query).run(type, now, id);
|
||||||
db.run(query, [type, now, id], function(err) {
|
return { changes: result.changes };
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve({ changes: this.changes });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function touch(id) {
|
function touch(id) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = 'UPDATE sessions SET updated_at = ? WHERE id = ?';
|
||||||
const query = 'UPDATE sessions SET updated_at = ? WHERE id = ?';
|
const result = db.prepare(query).run(now, id);
|
||||||
db.run(query, [now, id], function(err) {
|
return { changes: result.changes };
|
||||||
if (err) reject(err);
|
|
||||||
else resolve({ changes: this.changes });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getOrCreateActive(uid, requestedType = 'ask') {
|
function getOrCreateActive(uid, requestedType = 'ask') {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
|
|
||||||
// 1. Look for ANY active session for the user (ended_at IS NULL).
|
// 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
|
LIMIT 1
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const activeSession = await new Promise((resolve, reject) => {
|
const activeSession = db.prepare(findQuery).get(uid);
|
||||||
db.get(findQuery, [uid], (err, row) => {
|
|
||||||
if (err) reject(err);
|
|
||||||
else resolve(row);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (activeSession) {
|
if (activeSession) {
|
||||||
// An active session exists.
|
// 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.
|
// 2. Promotion Logic: If it's an 'ask' session and we need 'listen', promote it.
|
||||||
if (activeSession.session_type === 'ask' && requestedType === 'listen') {
|
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.`);
|
console.log(`[Repo] Promoted session ${activeSession.id} to 'listen' type.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Touch the session and return its ID.
|
// 3. Touch the session and return its ID.
|
||||||
await touch(activeSession.id);
|
touch(activeSession.id);
|
||||||
return activeSession.id;
|
return activeSession.id;
|
||||||
} else {
|
} else {
|
||||||
// 4. No active session found, create a new one.
|
// 4. No active session found, create a new one.
|
||||||
@ -160,19 +110,17 @@ async function getOrCreateActive(uid, requestedType = 'ask') {
|
|||||||
|
|
||||||
function endAllActiveSessions() {
|
function endAllActiveSessions() {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE ended_at IS NULL`;
|
||||||
const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE ended_at IS NULL`;
|
|
||||||
db.run(query, [now, now], function(err) {
|
try {
|
||||||
if (err) {
|
const result = db.prepare(query).run(now, now);
|
||||||
console.error('SQLite: Failed to end all active sessions:', err);
|
console.log(`[Repo] Ended ${result.changes} active session(s).`);
|
||||||
reject(err);
|
return { changes: result.changes };
|
||||||
} else {
|
} catch (err) {
|
||||||
console.log(`[Repo] Ended ${this.changes} active session(s).`);
|
console.error('SQLite: Failed to end all active sessions:', err);
|
||||||
resolve({ changes: this.changes });
|
throw err;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -13,89 +13,61 @@ function findOrCreate(user) {
|
|||||||
email=excluded.email
|
email=excluded.email
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
try {
|
||||||
db.run(query, [uid, displayName, email, now], (err) => {
|
db.prepare(query).run(uid, displayName, email, now);
|
||||||
if (err) {
|
return getById(uid);
|
||||||
console.error('SQLite: Failed to find or create user:', err);
|
} catch (err) {
|
||||||
return reject(err);
|
console.error('SQLite: Failed to find or create user:', err);
|
||||||
}
|
throw err;
|
||||||
getById(uid).then(resolve).catch(reject);
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getById(uid) {
|
function getById(uid) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
return db.prepare('SELECT * FROM users WHERE uid = ?').get(uid);
|
||||||
db.get('SELECT * FROM users WHERE uid = ?', [uid], (err, row) => {
|
|
||||||
if (err) reject(err);
|
|
||||||
else resolve(row);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveApiKey(apiKey, uid, provider = 'openai') {
|
function saveApiKey(apiKey, uid, provider = 'openai') {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
try {
|
||||||
db.run(
|
const result = db.prepare('UPDATE users SET api_key = ?, provider = ? WHERE uid = ?').run(apiKey, provider, uid);
|
||||||
'UPDATE users SET api_key = ?, provider = ? WHERE uid = ?',
|
console.log(`SQLite: API key saved for user ${uid} with provider ${provider}.`);
|
||||||
[apiKey, provider, uid],
|
return { changes: result.changes };
|
||||||
function(err) {
|
} catch (err) {
|
||||||
if (err) {
|
console.error('SQLite: Failed to save API key:', err);
|
||||||
console.error('SQLite: Failed to save API key:', err);
|
throw err;
|
||||||
reject(err);
|
}
|
||||||
} else {
|
|
||||||
console.log(`SQLite: API key saved for user ${uid} with provider ${provider}.`);
|
|
||||||
resolve({ changes: this.changes });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function update({ uid, displayName }) {
|
function update({ uid, displayName }) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const result = db.prepare('UPDATE users SET display_name = ? WHERE uid = ?').run(displayName, uid);
|
||||||
db.run('UPDATE users SET display_name = ? WHERE uid = ?', [displayName, uid], function(err) {
|
return { changes: result.changes };
|
||||||
if (err) reject(err);
|
|
||||||
else resolve({ changes: this.changes });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteById(uid) {
|
function deleteById(uid) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const userSessions = db.prepare('SELECT id FROM sessions WHERE uid = ?').all(uid);
|
||||||
const userSessions = db.prepare('SELECT id FROM sessions WHERE uid = ?').all(uid);
|
const sessionIds = userSessions.map(s => s.id);
|
||||||
const sessionIds = userSessions.map(s => s.id);
|
|
||||||
|
|
||||||
db.serialize(() => {
|
const transaction = db.transaction(() => {
|
||||||
db.run("BEGIN TRANSACTION;");
|
if (sessionIds.length > 0) {
|
||||||
|
const placeholders = sessionIds.map(() => '?').join(',');
|
||||||
try {
|
db.prepare(`DELETE FROM transcripts WHERE session_id IN (${placeholders})`).run(...sessionIds);
|
||||||
if (sessionIds.length > 0) {
|
db.prepare(`DELETE FROM ai_messages WHERE session_id IN (${placeholders})`).run(...sessionIds);
|
||||||
const placeholders = sessionIds.map(() => '?').join(',');
|
db.prepare(`DELETE FROM summaries WHERE session_id IN (${placeholders})`).run(...sessionIds);
|
||||||
db.prepare(`DELETE FROM transcripts WHERE session_id IN (${placeholders})`).run(...sessionIds);
|
db.prepare(`DELETE FROM sessions WHERE uid = ?`).run(uid);
|
||||||
db.prepare(`DELETE FROM ai_messages WHERE session_id IN (${placeholders})`).run(...sessionIds);
|
}
|
||||||
db.prepare(`DELETE FROM summaries WHERE session_id IN (${placeholders})`).run(...sessionIds);
|
db.prepare('DELETE FROM prompt_presets WHERE uid = ? AND is_default = 0').run(uid);
|
||||||
db.prepare(`DELETE FROM sessions WHERE uid = ?`).run(uid);
|
db.prepare('DELETE FROM users WHERE uid = ?').run(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 });
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
db.run("ROLLBACK;");
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
transaction();
|
||||||
|
return { success: true };
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -123,7 +123,7 @@ class AuthService {
|
|||||||
const userState = this.getCurrentUser();
|
const userState = this.getCurrentUser();
|
||||||
console.log('[AuthService] Broadcasting user state change:', userState);
|
console.log('[AuthService] Broadcasting user state change:', userState);
|
||||||
BrowserWindow.getAllWindows().forEach(win => {
|
BrowserWindow.getAllWindows().forEach(win => {
|
||||||
if (win && !win.isDestroyed()) {
|
if (win && !win.isDestroyed() && win.webContents && !win.webContents.isDestroyed()) {
|
||||||
win.webContents.send('user-state-changed', userState);
|
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 path = require('path');
|
||||||
const LATEST_SCHEMA = require('../config/schema');
|
const LATEST_SCHEMA = require('../config/schema');
|
||||||
|
|
||||||
@ -10,28 +10,20 @@ class SQLiteClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connect(dbPath) {
|
connect(dbPath) {
|
||||||
return new Promise((resolve, reject) => {
|
if (this.db) {
|
||||||
if (this.db) {
|
console.log('[SQLiteClient] Already connected.');
|
||||||
console.log('[SQLiteClient] Already connected.');
|
return;
|
||||||
return resolve();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
try {
|
||||||
this.dbPath = dbPath;
|
this.dbPath = dbPath;
|
||||||
this.db = new sqlite3.Database(this.dbPath, (err) => {
|
this.db = new Database(this.dbPath);
|
||||||
if (err) {
|
this.db.pragma('journal_mode = WAL');
|
||||||
console.error('[SQLiteClient] Could not connect to database', err);
|
console.log('[SQLiteClient] Connected successfully to:', this.dbPath);
|
||||||
return reject(err);
|
} catch (err) {
|
||||||
}
|
console.error('[SQLiteClient] Could not connect to database', err);
|
||||||
console.log('[SQLiteClient] Connected successfully to:', this.dbPath);
|
throw err;
|
||||||
|
}
|
||||||
this.db.run('PRAGMA journal_mode = WAL;', (err) => {
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDb() {
|
getDb() {
|
||||||
@ -41,80 +33,56 @@ class SQLiteClient {
|
|||||||
return this.db;
|
return this.db;
|
||||||
}
|
}
|
||||||
|
|
||||||
async synchronizeSchema() {
|
synchronizeSchema() {
|
||||||
console.log('[DB Sync] Starting schema synchronization...');
|
console.log('[DB Sync] Starting schema synchronization...');
|
||||||
const tablesInDb = await this.getTablesFromDb();
|
const tablesInDb = this.getTablesFromDb();
|
||||||
|
|
||||||
for (const tableName of Object.keys(LATEST_SCHEMA)) {
|
for (const tableName of Object.keys(LATEST_SCHEMA)) {
|
||||||
const tableSchema = LATEST_SCHEMA[tableName];
|
const tableSchema = LATEST_SCHEMA[tableName];
|
||||||
|
|
||||||
if (!tablesInDb.includes(tableName)) {
|
if (!tablesInDb.includes(tableName)) {
|
||||||
// Table doesn't exist, create it
|
// Table doesn't exist, create it
|
||||||
await this.createTable(tableName, tableSchema);
|
this.createTable(tableName, tableSchema);
|
||||||
} else {
|
} else {
|
||||||
// Table exists, check for missing columns
|
// Table exists, check for missing columns
|
||||||
await this.updateTable(tableName, tableSchema);
|
this.updateTable(tableName, tableSchema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log('[DB Sync] Schema synchronization finished.');
|
console.log('[DB Sync] Schema synchronization finished.');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTablesFromDb() {
|
getTablesFromDb() {
|
||||||
return new Promise((resolve, reject) => {
|
const tables = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
||||||
this.db.all("SELECT name FROM sqlite_master WHERE type='table'", (err, tables) => {
|
return tables.map(t => t.name);
|
||||||
if (err) return reject(err);
|
|
||||||
resolve(tables.map(t => t.name));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createTable(tableName, tableSchema) {
|
createTable(tableName, tableSchema) {
|
||||||
return new Promise((resolve, reject) => {
|
const columnDefs = tableSchema.columns.map(col => `"${col.name}" ${col.type}`).join(', ');
|
||||||
const columnDefs = tableSchema.columns.map(col => `"${col.name}" ${col.type}`).join(', ');
|
const query = `CREATE TABLE IF NOT EXISTS "${tableName}" (${columnDefs})`;
|
||||||
const query = `CREATE TABLE IF NOT EXISTS "${tableName}" (${columnDefs})`;
|
|
||||||
|
|
||||||
console.log(`[DB Sync] Creating table: ${tableName}`);
|
console.log(`[DB Sync] Creating table: ${tableName}`);
|
||||||
this.db.run(query, (err) => {
|
this.db.prepare(query).run();
|
||||||
if (err) return reject(err);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateTable(tableName, tableSchema) {
|
updateTable(tableName, tableSchema) {
|
||||||
return new Promise((resolve, reject) => {
|
const existingColumns = this.db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
||||||
this.db.all(`PRAGMA table_info("${tableName}")`, async (err, existingColumns) => {
|
const existingColumnNames = existingColumns.map(c => c.name);
|
||||||
if (err) return reject(err);
|
const columnsToAdd = tableSchema.columns.filter(col => !existingColumnNames.includes(col.name));
|
||||||
|
|
||||||
const existingColumnNames = existingColumns.map(c => c.name);
|
if (columnsToAdd.length > 0) {
|
||||||
const columnsToAdd = tableSchema.columns.filter(col => !existingColumnNames.includes(col.name));
|
console.log(`[DB Sync] Updating table: ${tableName}. Adding columns: ${columnsToAdd.map(c=>c.name).join(', ')}`);
|
||||||
|
for (const column of columnsToAdd) {
|
||||||
if (columnsToAdd.length > 0) {
|
const addColumnQuery = `ALTER TABLE "${tableName}" ADD COLUMN "${column.name}" ${column.type}`;
|
||||||
console.log(`[DB Sync] Updating table: ${tableName}. Adding columns: ${columnsToAdd.map(c=>c.name).join(', ')}`);
|
this.db.prepare(addColumnQuery).run();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async runQuery(query, params = []) {
|
runQuery(query, params = []) {
|
||||||
return new Promise((resolve, reject) => {
|
return this.db.prepare(query).run(params);
|
||||||
this.db.run(query, params, function(err) {
|
|
||||||
if (err) return reject(err);
|
|
||||||
resolve(this);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async cleanupEmptySessions() {
|
cleanupEmptySessions() {
|
||||||
console.log('[DB Cleanup] Checking for empty sessions...');
|
console.log('[DB Cleanup] Checking for empty sessions...');
|
||||||
const query = `
|
const query = `
|
||||||
SELECT s.id FROM sessions s
|
SELECT s.id FROM sessions s
|
||||||
@ -124,92 +92,65 @@ class SQLiteClient {
|
|||||||
WHERE t.id IS NULL AND a.id IS NULL AND su.session_id IS NULL
|
WHERE t.id IS NULL AND a.id IS NULL AND su.session_id IS NULL
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
const rows = this.db.prepare(query).all();
|
||||||
this.db.all(query, [], (err, rows) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('[DB Cleanup] Error finding empty sessions:', err);
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows.length === 0) {
|
if (rows.length === 0) {
|
||||||
console.log('[DB Cleanup] No empty sessions found.');
|
console.log('[DB Cleanup] No empty sessions found.');
|
||||||
return resolve();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const idsToDelete = rows.map(r => r.id);
|
const idsToDelete = rows.map(r => r.id);
|
||||||
const placeholders = idsToDelete.map(() => '?').join(',');
|
const placeholders = idsToDelete.map(() => '?').join(',');
|
||||||
const deleteQuery = `DELETE FROM sessions WHERE id IN (${placeholders})`;
|
const deleteQuery = `DELETE FROM sessions WHERE id IN (${placeholders})`;
|
||||||
|
|
||||||
console.log(`[DB Cleanup] Found ${idsToDelete.length} empty sessions. Deleting...`);
|
console.log(`[DB Cleanup] Found ${idsToDelete.length} empty sessions. Deleting...`);
|
||||||
this.db.run(deleteQuery, idsToDelete, function(deleteErr) {
|
const result = this.db.prepare(deleteQuery).run(idsToDelete);
|
||||||
if (deleteErr) {
|
console.log(`[DB Cleanup] Successfully deleted ${result.changes} empty sessions.`);
|
||||||
console.error('[DB Cleanup] Error deleting empty sessions:', deleteErr);
|
|
||||||
return reject(deleteErr);
|
|
||||||
}
|
|
||||||
console.log(`[DB Cleanup] Successfully deleted ${this.changes} empty sessions.`);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async initTables() {
|
initTables() {
|
||||||
await this.synchronizeSchema();
|
this.synchronizeSchema();
|
||||||
await this.initDefaultData();
|
this.initDefaultData();
|
||||||
}
|
}
|
||||||
|
|
||||||
async initDefaultData() {
|
initDefaultData() {
|
||||||
return new Promise((resolve, reject) => {
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const initUserQuery = `
|
||||||
const initUserQuery = `
|
INSERT OR IGNORE INTO users (uid, display_name, email, created_at)
|
||||||
INSERT OR IGNORE INTO users (uid, display_name, email, created_at)
|
VALUES (?, ?, ?, ?)
|
||||||
VALUES (?, ?, ?, ?)
|
`;
|
||||||
`;
|
|
||||||
|
|
||||||
this.db.run(initUserQuery, [this.defaultUserId, 'Default User', 'contact@pickle.com', now], (err) => {
|
this.db.prepare(initUserQuery).run(this.defaultUserId, 'Default User', 'contact@pickle.com', now);
|
||||||
if (err) {
|
|
||||||
console.error('Failed to initialize default user:', err);
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultPresets = [
|
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],
|
['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],
|
||||||
['meetings', 'Meetings', 'You are a meeting assistant. Your goal is to help the user capture key information during meetings and follow up effectively.\n\nYou help capture meeting notes, track action items, identify key decisions, and summarize important points discussed during meetings.', 1],
|
['meetings', 'Meetings', 'You are a meeting assistant. Your goal is to help the user capture key information during meetings and follow up effectively.\n\nYou help capture meeting notes, track action items, identify key decisions, and summarize important points discussed during meetings.', 1],
|
||||||
['sales', 'Sales', 'You are a real-time AI sales assistant, and your goal is to help the user close deals during sales interactions.\n\nYou provide real-time sales support, suggest responses to objections, help identify customer needs, and recommend strategies to advance deals.', 1],
|
['sales', 'Sales', 'You are a real-time AI sales assistant, and your goal is to help the user close deals during sales interactions.\n\nYou provide real-time sales support, suggest responses to objections, help identify customer needs, and recommend strategies to advance deals.', 1],
|
||||||
['recruiting', 'Recruiting', 'You are a recruiting assistant. Your goal is to help the user interview candidates and evaluate talent effectively.\n\nYou help evaluate candidates, suggest interview questions, analyze responses, and provide insights about candidate fit for positions.', 1],
|
['recruiting', 'Recruiting', 'You are a recruiting assistant. Your goal is to help the user interview candidates and evaluate talent effectively.\n\nYou help evaluate candidates, suggest interview questions, analyze responses, and provide insights about candidate fit for positions.', 1],
|
||||||
['customer-support', 'Customer Support', 'You are a customer support assistant. Your goal is to help resolve customer issues efficiently and thoroughly.\n\nYou help diagnose customer problems, suggest solutions, provide step-by-step troubleshooting guidance, and ensure customer satisfaction.', 1],
|
['customer-support', 'Customer Support', 'You are a customer support assistant. Your goal is to help resolve customer issues efficiently and thoroughly.\n\nYou help diagnose customer problems, suggest solutions, provide step-by-step troubleshooting guidance, and ensure customer satisfaction.', 1],
|
||||||
];
|
];
|
||||||
|
|
||||||
const stmt = this.db.prepare(`
|
const stmt = this.db.prepare(`
|
||||||
INSERT OR IGNORE INTO prompt_presets (id, uid, title, prompt, is_default, created_at)
|
INSERT OR IGNORE INTO prompt_presets (id, uid, title, prompt, is_default, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
for (const preset of defaultPresets) {
|
for (const preset of defaultPresets) {
|
||||||
stmt.run(preset[0], this.defaultUserId, preset[1], preset[2], preset[3], now);
|
stmt.run(preset[0], this.defaultUserId, preset[1], preset[2], preset[3], now);
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt.finalize((err) => {
|
console.log('Default data initialized.');
|
||||||
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(
|
return this.query(
|
||||||
'INSERT OR REPLACE INTO system_settings (key, value) VALUES (?, ?)',
|
'INSERT OR REPLACE INTO system_settings (key, value) VALUES (?, ?)',
|
||||||
['permissions_completed', 'true']
|
['permissions_completed', 'true']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkPermissionsCompleted() {
|
checkPermissionsCompleted() {
|
||||||
const result = await this.query(
|
const result = this.query(
|
||||||
'SELECT value FROM system_settings WHERE key = ?',
|
'SELECT value FROM system_settings WHERE key = ?',
|
||||||
['permissions_completed']
|
['permissions_completed']
|
||||||
);
|
);
|
||||||
@ -218,43 +159,32 @@ class SQLiteClient {
|
|||||||
|
|
||||||
close() {
|
close() {
|
||||||
if (this.db) {
|
if (this.db) {
|
||||||
this.db.close((err) => {
|
try {
|
||||||
if (err) {
|
this.db.close();
|
||||||
console.error('SQLite connection close failed:', err);
|
console.log('SQLite connection closed.');
|
||||||
} else {
|
} catch (err) {
|
||||||
console.log('SQLite connection closed.');
|
console.error('SQLite connection close failed:', err);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
this.db = null;
|
this.db = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async query(sql, params = []) {
|
query(sql, params = []) {
|
||||||
return new Promise((resolve, reject) => {
|
if (!this.db) {
|
||||||
if (!this.db) {
|
throw new Error('Database not connected');
|
||||||
return reject(new Error('Database not connected'));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
try {
|
||||||
if (sql.toUpperCase().startsWith('SELECT')) {
|
if (sql.toUpperCase().startsWith('SELECT')) {
|
||||||
this.db.all(sql, params, (err, rows) => {
|
return this.db.prepare(sql).all(params);
|
||||||
if (err) {
|
|
||||||
console.error('Query error:', err);
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve(rows);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this.db.run(sql, params, function(err) {
|
const result = this.db.prepare(sql).run(params);
|
||||||
if (err) {
|
return { changes: result.changes, lastID: result.lastID };
|
||||||
console.error('Query error:', err);
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve({ changes: this.changes, lastID: this.lastID });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
} catch (err) {
|
||||||
|
console.error('Query error:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const { BrowserWindow, globalShortcut, ipcMain, screen, app, shell, desktopCapturer } = require('electron');
|
const { BrowserWindow, globalShortcut, ipcMain, screen, app, shell, desktopCapturer } = require('electron');
|
||||||
const WindowLayoutManager = require('./windowLayoutManager');
|
const WindowLayoutManager = require('./windowLayoutManager');
|
||||||
const SmoothMovementManager = require('./smoothMovementManager');
|
const SmoothMovementManager = require('./smoothMovementManager');
|
||||||
const liquidGlass = require('electron-liquid-glass');
|
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
@ -15,6 +14,7 @@ const fetch = require('node-fetch');
|
|||||||
|
|
||||||
|
|
||||||
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
||||||
|
let liquidGlass;
|
||||||
const isLiquidGlassSupported = () => {
|
const isLiquidGlassSupported = () => {
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
return false;
|
return false;
|
||||||
@ -23,7 +23,15 @@ const isLiquidGlassSupported = () => {
|
|||||||
// return majorVersion >= 25; // macOS 26+ (Darwin 25+)
|
// return majorVersion >= 25; // macOS 26+ (Darwin 25+)
|
||||||
return majorVersion >= 26; // See you soon!
|
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 ]─────────────── */
|
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
||||||
|
|
||||||
let isContentProtectionOn = true;
|
let isContentProtectionOn = true;
|
||||||
|
@ -2,31 +2,23 @@ const sqliteClient = require('../../../common/services/sqliteClient');
|
|||||||
|
|
||||||
function addAiMessage({ sessionId, role, content, model = 'gpt-4.1' }) {
|
function addAiMessage({ sessionId, role, content, model = 'gpt-4.1' }) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const messageId = require('crypto').randomUUID();
|
||||||
const messageId = require('crypto').randomUUID();
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = `INSERT INTO ai_messages (id, session_id, sent_at, role, content, model, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`;
|
||||||
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) {
|
try {
|
||||||
if (err) {
|
db.prepare(query).run(messageId, sessionId, now, role, content, model, now);
|
||||||
console.error('SQLite: Failed to add AI message:', err);
|
return { id: messageId };
|
||||||
reject(err);
|
} catch (err) {
|
||||||
}
|
console.error('SQLite: Failed to add AI message:', err);
|
||||||
else {
|
throw err;
|
||||||
resolve({ id: messageId });
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllAiMessagesBySessionId(sessionId) {
|
function getAllAiMessagesBySessionId(sessionId) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const query = "SELECT * FROM ai_messages WHERE session_id = ? ORDER BY sent_at ASC";
|
||||||
const query = "SELECT * FROM ai_messages WHERE session_id = ? ORDER BY sent_at ASC";
|
return db.prepare(query).all(sessionId);
|
||||||
db.all(query, [sessionId], (err, rows) => {
|
|
||||||
if (err) reject(err);
|
|
||||||
else resolve(rows);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -2,33 +2,23 @@ const sqliteClient = require('../../../../common/services/sqliteClient');
|
|||||||
|
|
||||||
function addTranscript({ sessionId, speaker, text }) {
|
function addTranscript({ sessionId, speaker, text }) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const transcriptId = require('crypto').randomUUID();
|
||||||
const transcriptId = require('crypto').randomUUID();
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = `INSERT INTO transcripts (id, session_id, start_at, speaker, text, created_at) VALUES (?, ?, ?, ?, ?, ?)`;
|
||||||
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) {
|
try {
|
||||||
if (err) {
|
db.prepare(query).run(transcriptId, sessionId, now, speaker, text, now);
|
||||||
console.error('Error adding transcript:', err);
|
return { id: transcriptId };
|
||||||
reject(err);
|
} catch (err) {
|
||||||
} else {
|
console.error('Error adding transcript:', err);
|
||||||
resolve({ id: transcriptId });
|
throw err;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllTranscriptsBySessionId(sessionId) {
|
function getAllTranscriptsBySessionId(sessionId) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const query = "SELECT * FROM transcripts WHERE session_id = ? ORDER BY start_at ASC";
|
||||||
const query = "SELECT * FROM transcripts WHERE session_id = ? ORDER BY start_at ASC";
|
return db.prepare(query).all(sessionId);
|
||||||
db.all(query, [sessionId], (err, rows) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve(rows);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -2,43 +2,33 @@ const sqliteClient = require('../../../../common/services/sqliteClient');
|
|||||||
|
|
||||||
function saveSummary({ sessionId, tldr, text, bullet_json, action_json, model = 'gpt-4.1' }) {
|
function saveSummary({ sessionId, tldr, text, bullet_json, action_json, model = 'gpt-4.1' }) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = `
|
||||||
const query = `
|
INSERT INTO summaries (session_id, generated_at, model, text, tldr, bullet_json, action_json, updated_at)
|
||||||
INSERT INTO summaries (session_id, generated_at, model, text, tldr, bullet_json, action_json, updated_at)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
ON CONFLICT(session_id) DO UPDATE SET
|
||||||
ON CONFLICT(session_id) DO UPDATE SET
|
generated_at=excluded.generated_at,
|
||||||
generated_at=excluded.generated_at,
|
model=excluded.model,
|
||||||
model=excluded.model,
|
text=excluded.text,
|
||||||
text=excluded.text,
|
tldr=excluded.tldr,
|
||||||
tldr=excluded.tldr,
|
bullet_json=excluded.bullet_json,
|
||||||
bullet_json=excluded.bullet_json,
|
action_json=excluded.action_json,
|
||||||
action_json=excluded.action_json,
|
updated_at=excluded.updated_at
|
||||||
updated_at=excluded.updated_at
|
`;
|
||||||
`;
|
|
||||||
db.run(query, [sessionId, now, model, text, tldr, bullet_json, action_json, now], function(err) {
|
try {
|
||||||
if (err) {
|
const result = db.prepare(query).run(sessionId, now, model, text, tldr, bullet_json, action_json, now);
|
||||||
console.error('Error saving summary:', err);
|
return { changes: result.changes };
|
||||||
reject(err);
|
} catch (err) {
|
||||||
} else {
|
console.error('Error saving summary:', err);
|
||||||
resolve({ changes: this.changes });
|
throw err;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSummaryBySessionId(sessionId) {
|
function getSummaryBySessionId(sessionId) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const query = "SELECT * FROM summaries WHERE session_id = ?";
|
||||||
const query = "SELECT * FROM summaries WHERE session_id = ?";
|
return db.prepare(query).get(sessionId) || null;
|
||||||
db.get(query, [sessionId], (err, row) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve(row || null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -2,102 +2,92 @@ const sqliteClient = require('../../../common/services/sqliteClient');
|
|||||||
|
|
||||||
function getPresets(uid) {
|
function getPresets(uid) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const query = `
|
||||||
const query = `
|
SELECT * FROM prompt_presets
|
||||||
SELECT * FROM prompt_presets
|
WHERE uid = ? OR is_default = 1
|
||||||
WHERE uid = ? OR is_default = 1
|
ORDER BY is_default DESC, title ASC
|
||||||
ORDER BY is_default DESC, title ASC
|
`;
|
||||||
`;
|
|
||||||
db.all(query, [uid], (err, rows) => {
|
try {
|
||||||
if (err) {
|
return db.prepare(query).all(uid) || [];
|
||||||
console.error('SQLite: Failed to get presets:', err);
|
} catch (err) {
|
||||||
reject(err);
|
console.error('SQLite: Failed to get presets:', err);
|
||||||
} else {
|
throw err;
|
||||||
resolve(rows || []);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPresetTemplates() {
|
function getPresetTemplates() {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const query = `
|
||||||
const query = `
|
SELECT * FROM prompt_presets
|
||||||
SELECT * FROM prompt_presets
|
WHERE is_default = 1
|
||||||
WHERE is_default = 1
|
ORDER BY title ASC
|
||||||
ORDER BY title ASC
|
`;
|
||||||
`;
|
|
||||||
db.all(query, [], (err, rows) => {
|
try {
|
||||||
if (err) {
|
return db.prepare(query).all() || [];
|
||||||
console.error('SQLite: Failed to get preset templates:', err);
|
} catch (err) {
|
||||||
reject(err);
|
console.error('SQLite: Failed to get preset templates:', err);
|
||||||
} else {
|
throw err;
|
||||||
resolve(rows || []);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPreset({ uid, title, prompt }) {
|
function createPreset({ uid, title, prompt }) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const id = require('crypto').randomUUID();
|
||||||
const id = require('crypto').randomUUID();
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = `
|
||||||
const query = `
|
INSERT INTO prompt_presets (id, uid, title, prompt, is_default, created_at, sync_state)
|
||||||
INSERT INTO prompt_presets (id, uid, title, prompt, is_default, created_at, sync_state)
|
VALUES (?, ?, ?, ?, 0, ?, 'dirty')
|
||||||
VALUES (?, ?, ?, ?, 0, ?, 'dirty')
|
`;
|
||||||
`;
|
|
||||||
db.run(query, [id, uid, title, prompt, now], function(err) {
|
try {
|
||||||
if (err) {
|
db.prepare(query).run(id, uid, title, prompt, now);
|
||||||
console.error('SQLite: Failed to create preset:', err);
|
return { id };
|
||||||
reject(err);
|
} catch (err) {
|
||||||
} else {
|
console.error('SQLite: Failed to create preset:', err);
|
||||||
resolve({ id });
|
throw err;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePreset(id, { title, prompt }, uid) {
|
function updatePreset(id, { title, prompt }, uid) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const query = `
|
||||||
const query = `
|
UPDATE prompt_presets
|
||||||
UPDATE prompt_presets
|
SET title = ?, prompt = ?, sync_state = 'dirty', updated_at = ?
|
||||||
SET title = ?, prompt = ?, sync_state = 'dirty', updated_at = ?
|
WHERE id = ? AND uid = ? AND is_default = 0
|
||||||
WHERE id = ? AND uid = ? AND is_default = 0
|
`;
|
||||||
`;
|
|
||||||
db.run(query, [title, prompt, now, id, uid], function(err) {
|
try {
|
||||||
if (err) {
|
const result = db.prepare(query).run(title, prompt, now, id, uid);
|
||||||
console.error('SQLite: Failed to update preset:', err);
|
if (result.changes === 0) {
|
||||||
reject(err);
|
throw new Error('Preset not found, is default, or permission denied');
|
||||||
} else if (this.changes === 0) {
|
}
|
||||||
reject(new Error('Preset not found, is default, or permission denied'));
|
return { changes: result.changes };
|
||||||
} else {
|
} catch (err) {
|
||||||
resolve({ changes: this.changes });
|
console.error('SQLite: Failed to update preset:', err);
|
||||||
}
|
throw err;
|
||||||
});
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletePreset(id, uid) {
|
function deletePreset(id, uid) {
|
||||||
const db = sqliteClient.getDb();
|
const db = sqliteClient.getDb();
|
||||||
return new Promise((resolve, reject) => {
|
const query = `
|
||||||
const query = `
|
DELETE FROM prompt_presets
|
||||||
DELETE FROM prompt_presets
|
WHERE id = ? AND uid = ? AND is_default = 0
|
||||||
WHERE id = ? AND uid = ? AND is_default = 0
|
`;
|
||||||
`;
|
|
||||||
db.run(query, [id, uid], function(err) {
|
try {
|
||||||
if (err) {
|
const result = db.prepare(query).run(id, uid);
|
||||||
console.error('SQLite: Failed to delete preset:', err);
|
if (result.changes === 0) {
|
||||||
reject(err);
|
throw new Error('Preset not found, is default, or permission denied');
|
||||||
} else if (this.changes === 0) {
|
}
|
||||||
reject(new Error('Preset not found, is default, or permission denied'));
|
return { changes: result.changes };
|
||||||
} else {
|
} catch (err) {
|
||||||
resolve({ changes: this.changes });
|
console.error('SQLite: Failed to delete preset:', err);
|
||||||
}
|
throw err;
|
||||||
});
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
211
src/index.js
211
src/index.js
@ -18,7 +18,6 @@ const { initializeFirebase } = require('./common/services/firebaseClient');
|
|||||||
const databaseInitializer = require('./common/services/databaseInitializer');
|
const databaseInitializer = require('./common/services/databaseInitializer');
|
||||||
const authService = require('./common/services/authService');
|
const authService = require('./common/services/authService');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const { Deeplink } = require('electron-deeplink');
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const fetch = require('node-fetch');
|
const fetch = require('node-fetch');
|
||||||
const { autoUpdater } = require('electron-updater');
|
const { autoUpdater } = require('electron-updater');
|
||||||
@ -33,72 +32,86 @@ let WEB_PORT = 3000;
|
|||||||
const listenService = new ListenService();
|
const listenService = new ListenService();
|
||||||
// Make listenService globally accessible so other modules (e.g., windowManager, askService) can reuse the same instance
|
// Make listenService globally accessible so other modules (e.g., windowManager, askService) can reuse the same instance
|
||||||
global.listenService = listenService;
|
global.listenService = listenService;
|
||||||
let deeplink = null; // Initialize as null
|
|
||||||
let pendingDeepLinkUrl = null; // Store any deep link that arrives before initialization
|
|
||||||
|
|
||||||
function createMainWindows() {
|
// Native deep link handling - cross-platform compatible
|
||||||
createWindows();
|
let pendingDeepLinkUrl = null;
|
||||||
|
|
||||||
const { windowPool } = require('./electron/windowManager');
|
function setupProtocolHandling() {
|
||||||
const headerWindow = windowPool.get('header');
|
// Protocol registration - must be done before app is ready
|
||||||
|
if (!app.isDefaultProtocolClient('pickleglass')) {
|
||||||
// Initialize deeplink after windows are created
|
app.setAsDefaultProtocolClient('pickleglass');
|
||||||
if (!deeplink && headerWindow) {
|
console.log('[Protocol] Set as default protocol client for pickleglass://');
|
||||||
try {
|
|
||||||
deeplink = new Deeplink({
|
|
||||||
app,
|
|
||||||
mainWindow: headerWindow,
|
|
||||||
protocol: 'pickleglass',
|
|
||||||
isDev: !app.isPackaged,
|
|
||||||
debugLogging: true
|
|
||||||
});
|
|
||||||
|
|
||||||
deeplink.on('received', (url) => {
|
|
||||||
console.log('[deeplink] received:', url);
|
|
||||||
handleCustomUrl(url);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('[deeplink] Initialized with main window');
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 () => {
|
app.whenReady().then(async () => {
|
||||||
|
// Single instance lock - must be first
|
||||||
const gotTheLock = app.requestSingleInstanceLock();
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
if (!gotTheLock) {
|
if (!gotTheLock) {
|
||||||
app.quit();
|
app.quit();
|
||||||
return;
|
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();
|
initializeFirebase();
|
||||||
|
|
||||||
databaseInitializer.initialize()
|
databaseInitializer.initialize()
|
||||||
@ -121,9 +134,16 @@ app.whenReady().then(async () => {
|
|||||||
WEB_PORT = await startWebStack();
|
WEB_PORT = await startWebStack();
|
||||||
console.log('Web front-end listening on', WEB_PORT);
|
console.log('Web front-end listening on', WEB_PORT);
|
||||||
|
|
||||||
createMainWindows();
|
createWindows();
|
||||||
|
|
||||||
initAutoUpdater();
|
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', () => {
|
app.on('window-all-closed', () => {
|
||||||
@ -142,34 +162,17 @@ app.on('before-quit', async () => {
|
|||||||
|
|
||||||
app.on('activate', () => {
|
app.on('activate', () => {
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
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() {
|
function setupGeneralIpcHandlers() {
|
||||||
const userRepository = require('./common/repositories/user');
|
const userRepository = require('./common/repositories/user');
|
||||||
const presetRepository = require('./common/repositories/preset');
|
const presetRepository = require('./common/repositories/preset');
|
||||||
|
|
||||||
ipcMain.handle('save-api-key', async (event, apiKey) => {
|
ipcMain.handle('save-api-key', (event, apiKey) => {
|
||||||
try {
|
try {
|
||||||
await userRepository.saveApiKey(apiKey, authService.getCurrentUserId());
|
userRepository.saveApiKey(apiKey, authService.getCurrentUserId());
|
||||||
BrowserWindow.getAllWindows().forEach(win => {
|
BrowserWindow.getAllWindows().forEach(win => {
|
||||||
win.webContents.send('api-key-updated');
|
win.webContents.send('api-key-updated');
|
||||||
});
|
});
|
||||||
@ -180,12 +183,12 @@ function setupGeneralIpcHandlers() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-user-presets', async () => {
|
ipcMain.handle('get-user-presets', () => {
|
||||||
return await presetRepository.getPresets(authService.getCurrentUserId());
|
return presetRepository.getPresets(authService.getCurrentUserId());
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-preset-templates', async () => {
|
ipcMain.handle('get-preset-templates', () => {
|
||||||
return await presetRepository.getPresetTemplates();
|
return presetRepository.getPresetTemplates();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('start-firebase-auth', async () => {
|
ipcMain.handle('start-firebase-auth', async () => {
|
||||||
@ -204,7 +207,7 @@ function setupGeneralIpcHandlers() {
|
|||||||
return process.env.pickleglass_WEB_URL || 'http://localhost:3000';
|
return process.env.pickleglass_WEB_URL || 'http://localhost:3000';
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-current-user', async () => {
|
ipcMain.handle('get-current-user', () => {
|
||||||
return authService.getCurrentUser();
|
return authService.getCurrentUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -220,72 +223,72 @@ function setupWebDataHandlers() {
|
|||||||
const userRepository = require('./common/repositories/user');
|
const userRepository = require('./common/repositories/user');
|
||||||
const presetRepository = require('./common/repositories/preset');
|
const presetRepository = require('./common/repositories/preset');
|
||||||
|
|
||||||
const handleRequest = async (channel, responseChannel, payload) => {
|
const handleRequest = (channel, responseChannel, payload) => {
|
||||||
let result;
|
let result;
|
||||||
const currentUserId = authService.getCurrentUserId();
|
const currentUserId = authService.getCurrentUserId();
|
||||||
try {
|
try {
|
||||||
switch (channel) {
|
switch (channel) {
|
||||||
// SESSION
|
// SESSION
|
||||||
case 'get-sessions':
|
case 'get-sessions':
|
||||||
result = await sessionRepository.getAllByUserId(currentUserId);
|
result = sessionRepository.getAllByUserId(currentUserId);
|
||||||
break;
|
break;
|
||||||
case 'get-session-details':
|
case 'get-session-details':
|
||||||
const session = await sessionRepository.getById(payload);
|
const session = sessionRepository.getById(payload);
|
||||||
if (!session) {
|
if (!session) {
|
||||||
result = null;
|
result = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const transcripts = await sttRepository.getAllTranscriptsBySessionId(payload);
|
const transcripts = sttRepository.getAllTranscriptsBySessionId(payload);
|
||||||
const ai_messages = await askRepository.getAllAiMessagesBySessionId(payload);
|
const ai_messages = askRepository.getAllAiMessagesBySessionId(payload);
|
||||||
const summary = await summaryRepository.getSummaryBySessionId(payload);
|
const summary = summaryRepository.getSummaryBySessionId(payload);
|
||||||
result = { session, transcripts, ai_messages, summary };
|
result = { session, transcripts, ai_messages, summary };
|
||||||
break;
|
break;
|
||||||
case 'delete-session':
|
case 'delete-session':
|
||||||
result = await sessionRepository.deleteWithRelatedData(payload);
|
result = sessionRepository.deleteWithRelatedData(payload);
|
||||||
break;
|
break;
|
||||||
case 'create-session':
|
case 'create-session':
|
||||||
const id = await sessionRepository.create(currentUserId, 'ask');
|
const id = sessionRepository.create(currentUserId, 'ask');
|
||||||
if (payload.title) {
|
if (payload.title) {
|
||||||
await sessionRepository.updateTitle(id, payload.title);
|
sessionRepository.updateTitle(id, payload.title);
|
||||||
}
|
}
|
||||||
result = { id };
|
result = { id };
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// USER
|
// USER
|
||||||
case 'get-user-profile':
|
case 'get-user-profile':
|
||||||
result = await userRepository.getById(currentUserId);
|
result = userRepository.getById(currentUserId);
|
||||||
break;
|
break;
|
||||||
case 'update-user-profile':
|
case 'update-user-profile':
|
||||||
result = await userRepository.update({ uid: currentUserId, ...payload });
|
result = userRepository.update({ uid: currentUserId, ...payload });
|
||||||
break;
|
break;
|
||||||
case 'find-or-create-user':
|
case 'find-or-create-user':
|
||||||
result = await userRepository.findOrCreate(payload);
|
result = userRepository.findOrCreate(payload);
|
||||||
break;
|
break;
|
||||||
case 'save-api-key':
|
case 'save-api-key':
|
||||||
result = await userRepository.saveApiKey(payload, currentUserId);
|
result = userRepository.saveApiKey(payload, currentUserId);
|
||||||
break;
|
break;
|
||||||
case 'check-api-key-status':
|
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 };
|
result = { hasApiKey: !!user?.api_key && user.api_key.length > 0 };
|
||||||
break;
|
break;
|
||||||
case 'delete-account':
|
case 'delete-account':
|
||||||
result = await userRepository.deleteById(currentUserId);
|
result = userRepository.deleteById(currentUserId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// PRESET
|
// PRESET
|
||||||
case 'get-presets':
|
case 'get-presets':
|
||||||
result = await presetRepository.getPresets(currentUserId);
|
result = presetRepository.getPresets(currentUserId);
|
||||||
break;
|
break;
|
||||||
case 'create-preset':
|
case 'create-preset':
|
||||||
result = await presetRepository.create({ ...payload, uid: currentUserId });
|
result = presetRepository.create({ ...payload, uid: currentUserId });
|
||||||
settingsService.notifyPresetUpdate('created', result.id, payload.title);
|
settingsService.notifyPresetUpdate('created', result.id, payload.title);
|
||||||
break;
|
break;
|
||||||
case 'update-preset':
|
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);
|
settingsService.notifyPresetUpdate('updated', payload.id, payload.data.title);
|
||||||
break;
|
break;
|
||||||
case 'delete-preset':
|
case 'delete-preset':
|
||||||
result = await presetRepository.delete(payload, currentUserId);
|
result = presetRepository.delete(payload, currentUserId);
|
||||||
settingsService.notifyPresetUpdate('deleted', payload);
|
settingsService.notifyPresetUpdate('deleted', payload);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -295,13 +298,13 @@ function setupWebDataHandlers() {
|
|||||||
const batchResult = {};
|
const batchResult = {};
|
||||||
|
|
||||||
if (includes.includes('profile')) {
|
if (includes.includes('profile')) {
|
||||||
batchResult.profile = await userRepository.getById(currentUserId);
|
batchResult.profile = userRepository.getById(currentUserId);
|
||||||
}
|
}
|
||||||
if (includes.includes('presets')) {
|
if (includes.includes('presets')) {
|
||||||
batchResult.presets = await presetRepository.getPresets(currentUserId);
|
batchResult.presets = presetRepository.getPresets(currentUserId);
|
||||||
}
|
}
|
||||||
if (includes.includes('sessions')) {
|
if (includes.includes('sessions')) {
|
||||||
batchResult.sessions = await sessionRepository.getAllByUserId(currentUserId);
|
batchResult.sessions = sessionRepository.getAllByUserId(currentUserId);
|
||||||
}
|
}
|
||||||
result = batchResult;
|
result = batchResult;
|
||||||
break;
|
break;
|
||||||
@ -392,7 +395,7 @@ async function handleFirebaseAuthCallback(params) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 1. Sync user data to local DB
|
// 1. Sync user data to local DB
|
||||||
await userRepository.findOrCreate(firebaseUser);
|
userRepository.findOrCreate(firebaseUser);
|
||||||
console.log('[Auth] User data synced with local DB.');
|
console.log('[Auth] User data synced with local DB.');
|
||||||
|
|
||||||
// 2. Sign in using the authService in the main process
|
// 2. Sign in using the authService in the main process
|
||||||
|
Loading…
x
Reference in New Issue
Block a user