Skip to content

Commit c6b78ff

Browse files
committed
Add API management in the settings page
1 parent 7bf3111 commit c6b78ff

8 files changed

Lines changed: 255 additions & 4 deletions

File tree

changelog.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,39 @@
11
# Changelog
22
**All dates are in YYYY/MM/DD (Year-Month-Day)**
33

4+
## [1.4.0] - 2025-06-06
5+
6+
### Added
7+
- **API Key Management in Settings**: Complete API key management functionality within the settings modal
8+
- Current API key status display showing whether an API key is configured
9+
- Change API Key button that redirects to the main API key input section
10+
- Remove API Key button with confirmation dialog for secure key removal
11+
- Real-time status updates when API key state changes
12+
- **Enhanced Settings Modal**: Expanded settings with dedicated API Key Management section
13+
- Visual status indicators (✅ configured, ❌ not configured, 🔄 checking)
14+
- Responsive button layout for API key actions
15+
- Professional styling with danger button for removal action
16+
- **Backend API Key Operations**: New IPC handlers for comprehensive API key management
17+
- `get-api-key-status`: Check if API key exists without exposing the actual key
18+
- `remove-api-key`: Securely delete API key file and clear OpenAI instance
19+
- Enhanced error handling and logging for all API key operations
20+
21+
### Enhanced
22+
- **User Experience**: Streamlined API key management workflow
23+
- Users can now manage their API key entirely from the settings without navigating away
24+
- Clear visual feedback for all API key operations
25+
- Confirmation dialogs prevent accidental key removal
26+
- **Security**: Improved API key handling
27+
- API key status checking without exposing sensitive data
28+
- Secure removal process that clears both file storage and memory
29+
- Better separation of concerns between status checking and key access
30+
31+
### Technical
32+
- Added `updateApiKeyStatus()`, `changeApiKeyFromSettings()`, and `removeApiKeyFromSettings()` methods
33+
- Extended preload.js with `getApiKeyStatus` and `removeApiKey` functions
34+
- Enhanced main.js with secure API key management IPC handlers
35+
- Improved error handling and user feedback throughout the API key lifecycle
36+
437
## [1.3.0] - 2025-06-06
538

639
### Added

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "autocaption",
3-
"version": "1.3.0",
3+
"version": "1.4.0",
44
"description": "Automatic caption generation app using OpenAI Whisper",
55
"main": "src/main.js",
66
"scripts": {

src/app.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ class AutoCaptionApp {
134134
document.getElementById('manual-check-updates-btn').addEventListener('click', () => this.checkForUpdates());
135135
document.getElementById('show-debug-logs-btn').addEventListener('click', () => this.openDebugLogs());
136136

137+
// API Key Management in Settings
138+
document.getElementById('settings-change-api-key-btn').addEventListener('click', () => this.changeApiKeyFromSettings());
139+
document.getElementById('settings-remove-api-key-btn').addEventListener('click', () => this.removeApiKeyFromSettings());
140+
137141
// Theme selection
138142
document.getElementById('theme-select').addEventListener('change', (e) => {
139143
this.setTheme(e.target.value);
@@ -454,6 +458,9 @@ class AutoCaptionApp {
454458

455459
// Update last check info
456460
this.updateLastCheckInfo();
461+
462+
// Check and update API key status
463+
await this.updateApiKeyStatus();
457464
}
458465

459466
closeSettings() {
@@ -1085,6 +1092,92 @@ class AutoCaptionApp {
10851092
console.error('Error finishing onboarding:', error);
10861093
}
10871094
}
1095+
1096+
// API Key Management in Settings
1097+
async updateApiKeyStatus() {
1098+
const statusDisplay = document.getElementById('settings-api-key-status');
1099+
const indicator = document.getElementById('settings-api-key-indicator');
1100+
1101+
try {
1102+
const result = await window.electronAPI.getApiKeyStatus();
1103+
if (result.success && result.hasApiKey) {
1104+
statusDisplay.classList.remove('not-configured');
1105+
statusDisplay.classList.add('configured');
1106+
indicator.textContent = '✅ API Key Configured';
1107+
} else {
1108+
statusDisplay.classList.remove('configured');
1109+
statusDisplay.classList.add('not-configured');
1110+
indicator.textContent = '❌ No API Key Set';
1111+
}
1112+
} catch (error) {
1113+
console.error('Error checking API key status:', error);
1114+
statusDisplay.classList.remove('configured');
1115+
statusDisplay.classList.add('not-configured');
1116+
indicator.textContent = '⚠️ Error checking status';
1117+
}
1118+
}
1119+
1120+
async changeApiKeyFromSettings() {
1121+
// Close settings modal
1122+
this.closeSettings();
1123+
1124+
// Show the main API key section
1125+
this.showApiKeySection();
1126+
1127+
// Focus on the input
1128+
document.getElementById('api-key-input').focus();
1129+
}
1130+
1131+
async removeApiKeyFromSettings() {
1132+
const button = document.getElementById('settings-remove-api-key-btn');
1133+
const originalText = button.innerHTML;
1134+
1135+
// Show confirmation dialog
1136+
const confirmed = confirm('Are you sure you want to remove your API key? You will need to enter it again to use the application.');
1137+
1138+
if (!confirmed) {
1139+
return;
1140+
}
1141+
1142+
// Show loading state
1143+
button.innerHTML = '<span>🔄 Removing...</span>';
1144+
button.disabled = true;
1145+
1146+
try {
1147+
const result = await window.electronAPI.removeApiKey();
1148+
if (result.success) {
1149+
// Update API key status
1150+
this.apiKeySet = false;
1151+
await this.updateApiKeyStatus();
1152+
1153+
// Show success message briefly
1154+
button.innerHTML = '<span>✅ Removed</span>';
1155+
1156+
// Reset UI to show API key section
1157+
setTimeout(() => {
1158+
this.closeSettings();
1159+
this.showApiKeySection();
1160+
1161+
// Show status message
1162+
const statusDiv = document.getElementById('api-key-status');
1163+
this.showStatus(statusDiv, '🗑️ API key removed. Please enter a new one to continue.', 'error');
1164+
}, 1000);
1165+
} else {
1166+
button.innerHTML = '<span>❌ Failed</span>';
1167+
setTimeout(() => {
1168+
button.innerHTML = originalText;
1169+
button.disabled = false;
1170+
}, 2000);
1171+
}
1172+
} catch (error) {
1173+
console.error('Error removing API key:', error);
1174+
button.innerHTML = '<span>❌ Error</span>';
1175+
setTimeout(() => {
1176+
button.innerHTML = originalText;
1177+
button.disabled = false;
1178+
}, 2000);
1179+
}
1180+
}
10881181
}
10891182

10901183
// Initialize the app when the DOM is loaded

src/index.html

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,26 @@ <h3>🎨 Appearance</h3>
259259
<p class="setting-description">Choose your preferred theme. System Default will follow your system's theme settings.</p>
260260
</div>
261261
</div>
262+
<div class="setting-group">
263+
<h3>🔑 API Key Management</h3>
264+
<div class="setting-item">
265+
<label class="setting-label">Current API Key Status</label>
266+
<div id="settings-api-key-status" class="api-key-status-display">
267+
<span id="settings-api-key-indicator">🔄 Checking...</span>
268+
</div>
269+
<p class="setting-description">Manage your OpenAI API key used for transcription.</p>
270+
</div>
271+
<div class="setting-item">
272+
<div class="api-key-actions">
273+
<button id="settings-change-api-key-btn" class="btn btn-secondary">
274+
<span>🔄 Change API Key</span>
275+
</button>
276+
<button id="settings-remove-api-key-btn" class="btn btn-outline btn-danger">
277+
<span>🗑️ Remove API Key</span>
278+
</button>
279+
</div>
280+
</div>
281+
</div>
262282
<div class="setting-group">
263283
<h3>🔄 Updates</h3>
264284
<div class="setting-item">
@@ -279,7 +299,7 @@ <h3>🔄 Updates</h3>
279299
<div class="setting-group">
280300
<h3>ℹ️ About</h3>
281301
<div class="setting-item">
282-
<p><strong>Version:</strong> 1.3.0</p>
302+
<p><strong>Version:</strong> 1.4.0</p>
283303
<p><strong>Made with ❤️ by Jay</strong></p>
284304
<p>Using OpenAI Whisper for AI-powered transcription</p>
285305
</div>

src/main.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ let openai;
107107
let updateCheckInterval;
108108

109109
// Current app version
110-
const CURRENT_VERSION = '1.3.0';
110+
const CURRENT_VERSION = '1.4.0';
111111
const UPDATE_CHECK_URL = 'https://raw.githubusercontent.com/jay-bman725/AutoCaption/refs/heads/main/version';
112112
const CHANGELOG_URL = 'https://raw.githubusercontent.com/jay-bman725/AutoCaption/refs/heads/main/changelog.md';
113113

@@ -476,6 +476,40 @@ ipcMain.handle('set-api-key', async (event, apiKey) => {
476476
}
477477
});
478478

479+
// Get API key status
480+
ipcMain.handle('get-api-key-status', async () => {
481+
try {
482+
const apiKey = await loadApiKey();
483+
return {
484+
success: true,
485+
hasApiKey: apiKey !== null && apiKey !== undefined && apiKey.trim().length > 0
486+
};
487+
} catch (error) {
488+
logger.error('Error checking API key status:', error);
489+
return { success: false, error: error.message };
490+
}
491+
});
492+
493+
// Remove API key
494+
ipcMain.handle('remove-api-key', async () => {
495+
try {
496+
logger.info('Removing API key');
497+
// Remove the API key file
498+
if (fs.existsSync(apiKeyFile)) {
499+
await fsPromises.unlink(apiKeyFile);
500+
logger.info('API key file removed successfully');
501+
}
502+
503+
// Clear the OpenAI instance
504+
openai = null;
505+
506+
return { success: true };
507+
} catch (error) {
508+
logger.error('Error removing API key:', error);
509+
return { success: false, error: error.message };
510+
}
511+
});
512+
479513
ipcMain.handle('select-audio-file', async () => {
480514
logger.info('Opening file selection dialog');
481515
const result = await dialog.showOpenDialog(mainWindow, {

src/preload.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ const { contextBridge, ipcRenderer } = require('electron');
22

33
contextBridge.exposeInMainWorld('electronAPI', {
44
setApiKey: (apiKey) => ipcRenderer.invoke('set-api-key', apiKey),
5+
getApiKeyStatus: () => ipcRenderer.invoke('get-api-key-status'),
6+
removeApiKey: () => ipcRenderer.invoke('remove-api-key'),
57
selectAudioFile: () => ipcRenderer.invoke('select-audio-file'),
68
transcribeAudio: (filePath) => ipcRenderer.invoke('transcribe-audio', filePath),
79
saveSrtFile: (srtContent) => ipcRenderer.invoke('save-srt-file', srtContent),

src/styles.css

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,75 @@ footer {
973973
transition: color 0.3s ease;
974974
}
975975

976+
/* API Key Management Styles */
977+
.api-key-status-display {
978+
display: flex;
979+
align-items: center;
980+
padding: 12px 16px;
981+
border-radius: 8px;
982+
background: var(--status-loading-bg);
983+
border: 2px solid var(--loading-border);
984+
margin-bottom: 8px;
985+
transition: all 0.3s ease;
986+
}
987+
988+
.api-key-status-display.configured {
989+
background: var(--status-success-bg);
990+
border-color: var(--success-border);
991+
}
992+
993+
.api-key-status-display.not-configured {
994+
background: var(--status-error-bg);
995+
border-color: var(--error-border);
996+
}
997+
998+
.api-key-status-display span {
999+
font-weight: 500;
1000+
font-size: 14px;
1001+
}
1002+
1003+
.api-key-status-display.configured span {
1004+
color: var(--status-success-text);
1005+
}
1006+
1007+
.api-key-status-display.not-configured span {
1008+
color: var(--status-error-text);
1009+
}
1010+
1011+
.api-key-status-display span {
1012+
color: var(--status-loading-text);
1013+
}
1014+
1015+
.api-key-actions {
1016+
display: flex;
1017+
gap: 12px;
1018+
flex-wrap: wrap;
1019+
}
1020+
1021+
.btn-danger {
1022+
background: transparent;
1023+
color: #ef4444;
1024+
border: 2px solid #ef4444;
1025+
box-shadow: 0 2px 4px rgba(239, 68, 68, 0.1);
1026+
}
1027+
1028+
.btn-danger:hover {
1029+
background: #ef4444;
1030+
color: white;
1031+
transform: translateY(-2px);
1032+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
1033+
}
1034+
1035+
@media (max-width: 600px) {
1036+
.api-key-actions {
1037+
flex-direction: column;
1038+
}
1039+
1040+
.api-key-actions .btn {
1041+
width: 100%;
1042+
}
1043+
}
1044+
9761045
/* Update Modal Styles */
9771046
.update-modal-content {
9781047
max-width: 700px;

version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.3.0
1+
1.4.0

0 commit comments

Comments
 (0)