diff --git a/electron-builder.yml b/electron-builder.yml index 8f7d820..153f3b5 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -13,6 +13,12 @@ publish: repo: glass releaseType: draft +# Protocols configuration for deep linking +protocols: + name: PickleGlass Protocol + schemes: + - pickleglass + # List of files to be included in the app package files: - src/**/* @@ -29,6 +35,27 @@ extraResources: asarUnpack: - "src/assets/SystemAudioDump" +# Windows configuration +win: + icon: src/assets/logo.ico + target: + - target: nsis + arch: x64 + - target: portable + arch: x64 + publisherName: Pickle Team + requestedExecutionLevel: asInvoker + +# NSIS installer configuration for Windows +nsis: + oneClick: false + perMachine: false + allowToChangeInstallationDirectory: true + deleteAppDataOnUninstall: true + createDesktopShortcut: always + createStartMenuShortcut: true + shortcutName: Glass + # macOS specific configuration mac: # The application category type diff --git a/pickleglass_web/backend_node/ipcBridge.js b/pickleglass_web/backend_node/ipcBridge.js index 46d1e04..805403c 100644 --- a/pickleglass_web/backend_node/ipcBridge.js +++ b/pickleglass_web/backend_node/ipcBridge.js @@ -2,9 +2,20 @@ const crypto = require('crypto'); function ipcRequest(req, channel, payload) { return new Promise((resolve, reject) => { + // 즉시 브리지 상태 확인 - 문제있으면 바로 실패 + if (!req.bridge || typeof req.bridge.emit !== 'function') { + reject(new Error('IPC bridge is not available')); + return; + } + const responseChannel = `${channel}-${crypto.randomUUID()}`; req.bridge.once(responseChannel, (response) => { + if (!response) { + reject(new Error(`No response received from ${channel}`)); + return; + } + if (response.success) { resolve(response.data); } else { @@ -12,7 +23,12 @@ function ipcRequest(req, channel, payload) { } }); - req.bridge.emit('web-data-request', channel, responseChannel, payload); + try { + req.bridge.emit('web-data-request', channel, responseChannel, payload); + } catch (error) { + req.bridge.removeAllListeners(responseChannel); + reject(new Error(`Failed to emit IPC request: ${error.message}`)); + } }); } diff --git a/pickleglass_web/backend_node/routes/user.js b/pickleglass_web/backend_node/routes/user.js index e0fe054..d31a9a8 100644 --- a/pickleglass_web/backend_node/routes/user.js +++ b/pickleglass_web/backend_node/routes/user.js @@ -25,11 +25,22 @@ router.get('/profile', async (req, res) => { router.post('/find-or-create', async (req, res) => { try { + console.log('[API] find-or-create request received:', req.body); + + if (!req.body || !req.body.uid) { + return res.status(400).json({ error: 'User data with uid is required' }); + } + const user = await ipcRequest(req, 'find-or-create-user', req.body); + console.log('[API] find-or-create response:', user); res.status(200).json(user); } catch (error) { console.error('Failed to find or create user via IPC:', error); - res.status(500).json({ error: 'Failed to find or create user' }); + console.error('Request body:', req.body); + res.status(500).json({ + error: 'Failed to find or create user', + details: error.message + }); } }); diff --git a/src/common/repositories/user/sqlite.repository.js b/src/common/repositories/user/sqlite.repository.js index 2bb0ad7..7185c81 100644 --- a/src/common/repositories/user/sqlite.repository.js +++ b/src/common/repositories/user/sqlite.repository.js @@ -2,8 +2,17 @@ const sqliteClient = require('../../services/sqliteClient'); function findOrCreate(user) { const db = sqliteClient.getDb(); + + if (!user || !user.uid) { + throw new Error('User object and uid are required'); + } + const { uid, displayName, email } = user; const now = Math.floor(Date.now() / 1000); + + // Validate inputs + const safeDisplayName = displayName || 'User'; + const safeEmail = email || 'no-email@example.com'; const query = ` INSERT INTO users (uid, display_name, email, created_at) @@ -14,11 +23,15 @@ function findOrCreate(user) { `; try { - db.prepare(query).run(uid, displayName, email, now); - return getById(uid); + console.log('[SQLite] Creating/updating user:', { uid, displayName: safeDisplayName, email: safeEmail }); + db.prepare(query).run(uid, safeDisplayName, safeEmail, now); + const result = getById(uid); + console.log('[SQLite] User operation successful:', result); + return result; } catch (err) { console.error('SQLite: Failed to find or create user:', err); - throw err; + console.error('SQLite: User data:', { uid, displayName: safeDisplayName, email: safeEmail }); + throw new Error(`Failed to create user in database: ${err.message}`); } } diff --git a/src/index.js b/src/index.js index e943a55..e0fcde4 100644 --- a/src/index.js +++ b/src/index.js @@ -61,28 +61,28 @@ function setupProtocolHandling() { let protocolUrl = null; - if (process.platform === 'win32') { - // Windows - const lastArg = commandLine.length > 0 ? commandLine[commandLine.length - 1] : null; - if (lastArg && - typeof lastArg === 'string' && - lastArg.startsWith('pickleglass://') && - !lastArg.includes('\\') && - !lastArg.includes('₩')) { - protocolUrl = lastArg; - } - } else { - // Linux or etc - const lastArg = commandLine.length > 0 ? commandLine[commandLine.length - 1] : null; - if (lastArg && - typeof lastArg === 'string' && - lastArg.startsWith('pickleglass://')) { - protocolUrl = lastArg; + // Search through all command line arguments for a valid protocol URL + for (const arg of commandLine) { + if (arg && typeof arg === 'string' && arg.startsWith('pickleglass://')) { + // Clean up the URL by removing problematic characters + const cleanUrl = arg.replace(/[\\₩]/g, ''); + + // Additional validation for Windows + if (process.platform === 'win32') { + // On Windows, ensure the URL doesn't contain file path indicators + if (!cleanUrl.includes(':') || cleanUrl.indexOf('://') === cleanUrl.lastIndexOf(':')) { + protocolUrl = cleanUrl; + break; + } + } else { + protocolUrl = cleanUrl; + break; + } } } if (protocolUrl) { - console.log('[Protocol] Valid URL found from second instance (last arg):', protocolUrl); + console.log('[Protocol] Valid URL found from second instance:', protocolUrl); handleCustomUrl(protocolUrl); } else { console.log('[Protocol] No valid protocol URL found in command line arguments'); @@ -135,16 +135,17 @@ function focusMainWindow() { } if (process.platform === 'win32') { - const lastArg = process.argv.length > 0 ? process.argv[process.argv.length - 1] : null; - - if (lastArg && - typeof lastArg === 'string' && - lastArg.startsWith('pickleglass://') && - !lastArg.includes('\\') && - !lastArg.includes('₩')) { - - console.log('[Protocol] Found protocol URL in initial arguments (last arg):', lastArg); - pendingDeepLinkUrl = lastArg; + for (const arg of process.argv) { + if (arg && typeof arg === 'string' && arg.startsWith('pickleglass://')) { + // Clean up the URL by removing problematic characters (korean characters issue...) + const cleanUrl = arg.replace(/[\\₩]/g, ''); + + if (!cleanUrl.includes(':') || cleanUrl.indexOf('://') === cleanUrl.lastIndexOf(':')) { + console.log('[Protocol] Found protocol URL in initial arguments:', cleanUrl); + pendingDeepLinkUrl = cleanUrl; + break; + } + } } console.log('[Protocol] Initial process.argv:', process.argv); @@ -376,16 +377,19 @@ async function handleCustomUrl(url) { try { console.log('[Custom URL] Processing URL:', url); - // val url + // Validate and clean URL if (!url || typeof url !== 'string' || !url.startsWith('pickleglass://')) { console.error('[Custom URL] Invalid URL format:', url); return; } - // val url - if (url.includes('\\') || url.includes('₩')) { - console.error('[Custom URL] URL contains invalid path characters:', url); - return; + // Clean up URL by removing problematic characters + const cleanUrl = url.replace(/[\\₩]/g, ''); + + // Additional validation + if (cleanUrl !== url) { + console.log('[Custom URL] Cleaned URL from:', url, 'to:', cleanUrl); + url = cleanUrl; } const urlObj = new URL(url);