feat: 111125/logger

This commit is contained in:
NguyenAnhQuan 2025-11-11 17:16:43 +07:00
parent 80babfd455
commit cb67afb1a5
10 changed files with 133 additions and 68 deletions

View File

@ -2,6 +2,7 @@ import { ipcMain, dialog, app } from 'electron'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import { encrypt, fromEncryptedJson } from './utils/keystore' import { encrypt, fromEncryptedJson } from './utils/keystore'
import logger from './logger'
const neptuneNative = require('@neptune/native') const neptuneNative = require('@neptune/native')
@ -13,15 +14,14 @@ ipcMain.handle('wallet:createKeystore', async (_event, seed, password) => {
const savePath = path.join(process.cwd(), 'wallets') const savePath = path.join(process.cwd(), 'wallets')
fs.mkdirSync(savePath, { recursive: true }) fs.mkdirSync(savePath, { recursive: true })
// Use timestamp for filename
const timestamp = Date.now() const timestamp = Date.now()
const fileName = `neptune-wallet-${timestamp}.json` const fileName = `neptune-wallet-${timestamp}.json`
const filePath = path.join(savePath, fileName) const filePath = path.join(savePath, fileName)
fs.writeFileSync(filePath, keystore) fs.writeFileSync(filePath, keystore)
return { filePath } return { filePath }
} catch (error) { } catch (error: any) {
console.error('Error creating keystore:', error) logger.error('Error creating keystore:', error.message)
throw error throw error
} }
}) })
@ -46,8 +46,8 @@ ipcMain.handle('wallet:saveKeystoreAs', async (_event, seed: string, password: s
fs.writeFileSync(filePath, keystore) fs.writeFileSync(filePath, keystore)
return { filePath } return { filePath }
} catch (error) { } catch (error: any) {
console.error('Error saving keystore (Save As):', error) logger.error('Error saving keystore (Save As):', error.message)
throw error throw error
} }
}) })
@ -58,8 +58,8 @@ ipcMain.handle('wallet:decryptKeystore', async (_event, filePath, password) => {
const phrase = await fromEncryptedJson(json, password) const phrase = await fromEncryptedJson(json, password)
return { phrase } return { phrase }
} catch (error) { } catch (error: any) {
console.error('Error decrypting keystore ipc:', error) logger.error('Error decrypting keystore ipc:', error.message)
throw error throw error
} }
}) })
@ -91,14 +91,14 @@ ipcMain.handle('wallet:checkKeystore', async () => {
if (typeof height === 'number' && Number.isFinite(height)) { if (typeof height === 'number' && Number.isFinite(height)) {
minBlockHeight = height minBlockHeight = height
} }
} catch (error) { } catch (error: any) {
console.warn('Unable to read minBlockHeight from keystore:', error) logger.warn('Unable to read minBlockHeight from keystore:', error.message)
} }
return { exists: true, filePath: resolvedPath, minBlockHeight } return { exists: true, filePath: resolvedPath, minBlockHeight }
} catch (error) { } catch (error: any) {
console.error('Error checking keystore ipc:', error) logger.error('Error checking keystore ipc:', error.message)
return { exists: false, filePath: null, minBlockHeight: null, error: String(error) } return { exists: false, filePath: null, minBlockHeight: null, error: error.message }
} }
}) })
@ -130,9 +130,9 @@ ipcMain.handle(
fs.writeFileSync(normalizedPath, JSON.stringify(walletJson, null, 2), 'utf-8') fs.writeFileSync(normalizedPath, JSON.stringify(walletJson, null, 2), 'utf-8')
return { success: true, minBlockHeight } return { success: true, minBlockHeight }
} catch (error) { } catch (error: any) {
console.error('Error updating min block height:', error) logger.error('Error updating min block height:', error.message)
return { success: false, error: String(error) } return { success: false, error: error.message }
} }
} }
) )
@ -162,9 +162,9 @@ ipcMain.handle(
} }
return { success: true, minBlockHeight: null } return { success: true, minBlockHeight: null }
} catch (error) { } catch (error: any) {
console.error('Error reading min block height:', error) logger.error('Error reading min block height:', error.message)
return { success: false, error: String(error), minBlockHeight: null } return { success: false, error: error.message, minBlockHeight: null }
} }
} }
) )
@ -173,8 +173,8 @@ ipcMain.handle('wallet:generateKeysFromSeed', async (_event, seedPhrase: string[
try { try {
const wallet = new neptuneNative.WalletManager() const wallet = new neptuneNative.WalletManager()
return wallet.generateKeysFromSeed(seedPhrase) return wallet.generateKeysFromSeed(seedPhrase)
} catch (error) { } catch (error: any) {
console.error('Error generating keys from seed ipc:', error) logger.error('Error generating keys from seed ipc:', error.message)
throw error throw error
} }
}) })
@ -188,7 +188,6 @@ ipcMain.handle('wallet:buildTransaction', async (_event, args) => {
import.meta.env.VITE_APP_API, import.meta.env.VITE_APP_API,
spendingKeyHex, spendingKeyHex,
inputAdditionRecords, inputAdditionRecords,
// pass minBlockHeight from args if provided, default 0
typeof args?.minBlockHeight === 'number' && Number.isFinite(args.minBlockHeight) typeof args?.minBlockHeight === 'number' && Number.isFinite(args.minBlockHeight)
? args.minBlockHeight ? args.minBlockHeight
: 0, : 0,
@ -197,8 +196,12 @@ ipcMain.handle('wallet:buildTransaction', async (_event, args) => {
fee fee
) )
return JSON.parse(result) return JSON.parse(result)
} catch (error) { } catch (error: any) {
console.error('Error building transaction with primitive proof ipc:', error) logger.error('Error building transaction with primitive proof ipc:', error.message)
throw error throw error
} }
}) })
ipcMain.on('log:info', (_, msg: string) => logger.info(msg))
ipcMain.on('log:warn', (_, msg: string) => logger.warn(msg))
ipcMain.on('log:error', (_, msg: string) => logger.error(msg))

33
electron/logger.ts Normal file
View File

@ -0,0 +1,33 @@
import { app } from 'electron'
import log from 'electron-log'
const isDev = app.isPackaged
console.log('isDev :>> ', isDev);
if (!isDev) {
log.transports.file.level = 'info'
log.transports.console.level = false
log.transports.file.format = '{y}-{m}-{d} {h}:{i}:{s} [{level}] {text}'
}
export const logger = {
info: (...args: any[]) => {
if (isDev) console.log('[INFO]', ...args)
else log.info(...args)
},
warn: (...args: any[]) => {
if (isDev) console.warn('[WARN]', ...args)
else log.warn(...args)
},
error: (...args: any[]) => {
if (isDev) console.error('[ERROR]', ...args)
else log.error(...args)
},
debug: (...args: any[]) => {
if (isDev) console.debug('[DEBUG]', ...args)
else log.debug(...args)
},
}
export default logger

View File

@ -2,12 +2,14 @@ import { app, BrowserWindow } from 'electron'
import path from 'node:path' import path from 'node:path'
import started from 'electron-squirrel-startup' import started from 'electron-squirrel-startup'
import './ipcHandlers' import './ipcHandlers'
import logger from './logger'
if (started) { if (started) {
app.quit() app.quit()
} }
const createWindow = () => { const createWindow = () => {
logger.info('App started')
const mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 800, height: 800,
@ -38,6 +40,7 @@ app.on('ready', createWindow)
// for applications and their menu bar to stay active until the user quits // for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q. // explicitly with Cmd + Q.
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
logger.info('App closed')
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }

View File

@ -18,3 +18,9 @@ contextBridge.exposeInMainWorld('walletApi', {
getMinBlockHeight: (filePath: string | null) => getMinBlockHeight: (filePath: string | null) =>
ipcRenderer.invoke('wallet:getMinBlockHeight', filePath), ipcRenderer.invoke('wallet:getMinBlockHeight', filePath),
}) })
contextBridge.exposeInMainWorld('logger', {
info: (msg: string) => ipcRenderer.send('log:info', msg),
warn: (msg: string) => ipcRenderer.send('log:warn', msg),
error: (msg: string) => ipcRenderer.send('log:error', msg),
})

10
package-lock.json generated
View File

@ -16,6 +16,7 @@
"ant-design-vue": "^4.2.6", "ant-design-vue": "^4.2.6",
"axios": "^1.6.8", "axios": "^1.6.8",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"electron-log": "^5.4.3",
"electron-squirrel-startup": "^1.0.1", "electron-squirrel-startup": "^1.0.1",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vue": "^3.4.21", "vue": "^3.4.21",
@ -5807,6 +5808,15 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/electron-log": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.4.3.tgz",
"integrity": "sha512-sOUsM3LjZdugatazSQ/XTyNcw8dfvH1SYhXWiJyfYodAAKOZdHs0txPiLDXFzOZbhXgAgshQkshH2ccq0feyLQ==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/electron-squirrel-startup": { "node_modules/electron-squirrel-startup": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz", "resolved": "https://registry.npmjs.org/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz",

View File

@ -26,6 +26,7 @@
"ant-design-vue": "^4.2.6", "ant-design-vue": "^4.2.6",
"axios": "^1.6.8", "axios": "^1.6.8",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"electron-log": "^5.4.3",
"electron-squirrel-startup": "^1.0.1", "electron-squirrel-startup": "^1.0.1",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vue": "^3.4.21", "vue": "^3.4.21",

View File

@ -30,11 +30,10 @@ export function useNeptuneWallet() {
await initWasm() await initWasm()
wasmInitialized = true wasmInitialized = true
} catch (err) { } catch (err: any) {
wasmInitialized = false wasmInitialized = false
const errorMsg = 'Failed to initialize Neptune WASM' await (window as any).logger.error('WASM init error:', err.message)
console.error('WASM init error:', err) throw new Error('Failed to initialize Neptune WASM')
throw new Error(errorMsg)
} }
})() })()
@ -59,8 +58,8 @@ export function useNeptuneWallet() {
store.setAddress(addressResult) store.setAddress(addressResult)
return result return result
} catch (err) { } catch (err: any) {
console.error('Error generating wallet:', err) await (window as any).logger.error('Error generating wallet:', err.message)
throw err throw err
} }
} }
@ -93,8 +92,8 @@ export function useNeptuneWallet() {
spendingKey: result.spending_key_hex, spendingKey: result.spending_key_hex,
address: addressResult, address: addressResult,
} }
} catch (err) { } catch (err: any) {
console.error('Error recovering wallet from seed:', err) await (window as any).logger.error('Error recovering wallet from seed:', err.message)
throw err throw err
} }
} }
@ -128,14 +127,14 @@ export function useNeptuneWallet() {
store.setAddress(addressResult) store.setAddress(addressResult)
await loadMinBlockHeightFromKeystore() await loadMinBlockHeightFromKeystore()
} catch (err) { } catch (err: any) {
if ( if (
err instanceof Error && err instanceof Error &&
(err.message.includes('Unsupported state') || (err.message.includes('Unsupported state') ||
err.message.includes('unable to authenticate')) err.message.includes('unable to authenticate'))
) { ) {
console.error('Invalid password') await (window as any).logger.error('Invalid password')
} else console.error('Error decrypting keystore:', err) } else await (window as any).logger.error('Error decrypting keystore:', err.message)
throw err throw err
} }
@ -147,8 +146,8 @@ export function useNeptuneWallet() {
store.setKeystorePath(result.filePath) store.setKeystorePath(result.filePath)
store.setMinBlockHeight(null) store.setMinBlockHeight(null)
return result.filePath return result.filePath
} catch (err) { } catch (err: any) {
console.error('Error creating keystore:', err) await (window as any).logger.error('Error creating keystore:', err.message)
throw err throw err
} }
} }
@ -158,8 +157,8 @@ export function useNeptuneWallet() {
const result = await (window as any).walletApi.saveKeystoreAs(seed, password) const result = await (window as any).walletApi.saveKeystoreAs(seed, password)
if (!result.filePath) throw new Error('User canceled') if (!result.filePath) throw new Error('User canceled')
return result.filePath return result.filePath
} catch (err) { } catch (err: any) {
console.error('Error saving keystore:', err) await (window as any).logger.error('Error saving keystore:', err.message)
throw err throw err
} }
} }
@ -175,8 +174,8 @@ export function useNeptuneWallet() {
store.setMinBlockHeight(toFiniteNumber(height)) store.setMinBlockHeight(toFiniteNumber(height))
} }
return true return true
} catch (err) { } catch (err: any) {
console.error('Error checking keystore:', err) await (window as any).logger.error('Error checking keystore:', err.message)
throw err throw err
} }
} }
@ -202,8 +201,8 @@ export function useNeptuneWallet() {
) )
if (!response.success) throw new Error('Failed to update min block height') if (!response.success) throw new Error('Failed to update min block height')
store.setMinBlockHeight(minBlockHeight) store.setMinBlockHeight(minBlockHeight)
} catch (err) { } catch (err: any) {
console.error('Error saving min block height:', err) await (window as any).logger.error('Error saving min block height:', err.message)
throw err throw err
} }
} }
@ -220,8 +219,8 @@ export function useNeptuneWallet() {
store.setMinBlockHeight(minBlockHeight) store.setMinBlockHeight(minBlockHeight)
return minBlockHeight return minBlockHeight
} catch (err) { } catch (err: any) {
console.error('Error loading min block height:', err) await (window as any).logger.error('Error loading min block height:', err.message)
throw err throw err
} }
} }
@ -249,8 +248,8 @@ export function useNeptuneWallet() {
await persistMinBlockHeight(utxoList) await persistMinBlockHeight(utxoList)
return result return result
} catch (err) { } catch (err: any) {
console.error('Error getting UTXOs:', err) await (window as any).logger.error('Error getting UTXOs:', err.message)
throw err throw err
} }
} }
@ -275,8 +274,8 @@ export function useNeptuneWallet() {
balance: result?.balance || result, balance: result?.balance || result,
pendingBalance: result?.pendingBalance || result, pendingBalance: result?.pendingBalance || result,
} }
} catch (err) { } catch (err: any) {
console.error('Error getting balance:', err) await (window as any).logger.error('Error getting balance:', err.message)
throw err throw err
} }
} }
@ -286,8 +285,8 @@ export function useNeptuneWallet() {
const response = await API.getBlockHeight() const response = await API.getBlockHeight()
const result = response?.result || response const result = response?.result || response
return result?.height || result return result?.height || result
} catch (err) { } catch (err: any) {
console.error('Error getting block height:', err) await (window as any).logger.error('Error getting block height:', err.message)
throw err throw err
} }
} }
@ -299,8 +298,8 @@ export function useNeptuneWallet() {
store.setNetwork((result.network + 'net') as 'mainnet' | 'testnet') store.setNetwork((result.network + 'net') as 'mainnet' | 'testnet')
return result return result
} catch (err) { } catch (err: any) {
console.error('Error getting network info:', err) await (window as any).logger.error('Error getting network info:', err.message)
throw err throw err
} }
} }
@ -327,8 +326,8 @@ export function useNeptuneWallet() {
const response = await API.broadcastSignedTransaction(transactionHex) const response = await API.broadcastSignedTransaction(transactionHex)
const result = response?.result || response const result = response?.result || response
return result return result
} catch (err) { } catch (err: any) {
console.error('Error sending transaction:', err) await (window as any).logger.error('Error sending transaction:', err.message)
throw err throw err
} }
} }
@ -345,8 +344,8 @@ export function useNeptuneWallet() {
const addressResult = await getAddressFromSeed(store.getSeedPhrase) const addressResult = await getAddressFromSeed(store.getSeedPhrase)
store.setAddress(addressResult) store.setAddress(addressResult)
} }
} catch (err) { } catch (err: any) {
console.error('Error setting network:', err) await (window as any).logger.error('Error setting network:', err.message)
throw err throw err
} }
} }

View File

@ -32,7 +32,6 @@ const loadUtxos = async () => {
loading.value = true loading.value = true
const result = await getUtxos() const result = await getUtxos()
if (result.error) { if (result.error) {
console.error(result.error.message)
message.error('Failed to load UTXOs') message.error('Failed to load UTXOs')
loading.value = false loading.value = false
return return
@ -40,7 +39,6 @@ const loadUtxos = async () => {
loading.value = false loading.value = false
} catch (err) { } catch (err) {
message.error('Failed to load UTXOs') message.error('Failed to load UTXOs')
console.error('Error loading UTXOs:', err)
} finally { } finally {
loading.value = false loading.value = false
} }
@ -63,8 +61,8 @@ watch(
<div class="debug-header"> <div class="debug-header">
<h3 class="debug-title">IN USE UTXOS</h3> <h3 class="debug-title">IN USE UTXOS</h3>
<div class="debug-info"> <div class="debug-info">
<p><span>COUNT</span> {{ inUseUtxosCount }}</p> <p><span>COUNT</span> <strong>{{ inUseUtxosCount }}</strong></p>
<p><span>AMOUNT</span> {{ inUseUtxosAmount }} <strong>NPT</strong></p> <p><span>AMOUNT</span> <strong>{{ inUseUtxosAmount }}</strong> <strong>NPT</strong></p>
</div> </div>
</div> </div>
@ -116,6 +114,10 @@ watch(
font-weight: var(--font-bold); font-weight: var(--font-bold);
margin-right: var(--spacing-sm); margin-right: var(--spacing-sm);
} }
strong {
color: var(--primary-color);
}
} }
} }
} }

View File

@ -158,9 +158,11 @@ const displayAddress = computed(() => {
<div class="amount-row"> <div class="amount-row">
<div class="amount-field"> <div class="amount-field">
<div class="form-group"> <div class="form-group">
<label class="form-label"> <label class="form-label-extra">
Amount <span class="required">*</span> <span class="form-label"> Amount <span class="required">*</span> </span>
<span class="balance-info">Available: {{ availableBalance }} NPT</span> <span class="balance-info"
>Available: <strong>{{ availableBalance }}</strong> NPT</span
>
</label> </label>
<input <input
v-model="outputAmounts" v-model="outputAmounts"
@ -285,12 +287,17 @@ const displayAddress = computed(() => {
gap: var(--spacing-xs); gap: var(--spacing-xs);
} }
.form-label-extra {
display: flex;
justify-content: space-between;
align-items: center;
}
.form-label { .form-label {
font-size: var(--font-sm); font-size: var(--font-sm);
font-weight: var(--font-medium); font-weight: var(--font-medium);
color: var(--text-primary); color: var(--text-primary);
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
.required { .required {

View File

@ -21,6 +21,7 @@ const activeTabKey = inject<ComputedRef<string>>('activeTabKey')
const availableBalance = ref<string>('0.00000000') const availableBalance = ref<string>('0.00000000')
const pendingBalance = ref<string>('0.00000000') const pendingBalance = ref<string>('0.00000000')
const loading = ref(false) const loading = ref(false)
const sendingLoading = ref(false)
const error = ref<string | null>(null) const error = ref<string | null>(null)
const showSeedModal = ref(false) const showSeedModal = ref(false)
const isSendingMode = ref(false) const isSendingMode = ref(false)
@ -63,7 +64,7 @@ const handleSendTransaction = async (data: {
fee: string fee: string
}) => { }) => {
try { try {
loading.value = true sendingLoading.value = true
const payload = { const payload = {
spendingKeyHex: neptuneStore.getSpendingKey || '', spendingKeyHex: neptuneStore.getSpendingKey || '',
inputAdditionRecords: neptuneStore.getUtxos?.map((utxo: Utxo) => utxo.additionRecord), inputAdditionRecords: neptuneStore.getUtxos?.map((utxo: Utxo) => utxo.additionRecord),
@ -89,7 +90,7 @@ const handleSendTransaction = async (data: {
} catch (error) { } catch (error) {
message.error('Failed to send transaction') message.error('Failed to send transaction')
} finally { } finally {
loading.value = false sendingLoading.value = false
} }
} }
@ -235,14 +236,14 @@ watch(
<!-- Send Transaction View --> <!-- Send Transaction View -->
<div v-else-if="isSendingMode" class="send-transaction-wrapper"> <div v-else-if="isSendingMode" class="send-transaction-wrapper">
<SendTransactionComponent <SendTransactionComponent
:is-loading="loading" :is-loading="sendingLoading"
:available-balance="availableBalance" :available-balance="availableBalance"
@cancel="handleCancelSend" @cancel="handleCancelSend"
@send="handleSendTransaction" @send="handleSendTransaction"
/> />
<!-- Loading Overlay --> <!-- Loading Overlay -->
<div v-if="loading" class="sending-overlay"> <div v-if="sendingLoading" class="sending-overlay">
<div class="sending-content"> <div class="sending-content">
<SpinnerCommon size="large" /> <SpinnerCommon size="large" />
<p class="sending-text">Sending...</p> <p class="sending-text">Sending...</p>