settings.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import express from 'express';
  2. import { apiKeysDb, credentialsDb, notificationPreferencesDb, pushSubscriptionsDb } from '../database/db.js';
  3. import { getPublicKey } from '../services/vapid-keys.js';
  4. import { createNotificationEvent, notifyUserIfEnabled } from '../services/notification-orchestrator.js';
  5. import {
  6. readPermissionSettings,
  7. writePermissionSettings,
  8. } from '../services/permissionSettings.js';
  9. const router = express.Router();
  10. // ===============================
  11. // Tool Permission Settings
  12. // ===============================
  13. router.get('/permissions', async (_req, res) => {
  14. try {
  15. res.json({ success: true, permissions: readPermissionSettings() });
  16. } catch (error) {
  17. console.error('Error fetching permission settings:', error);
  18. res.status(500).json({ error: 'Failed to fetch permission settings' });
  19. }
  20. });
  21. router.put('/permissions', async (req, res) => {
  22. try {
  23. const permissions = writePermissionSettings(req.body || {});
  24. res.json({ success: true, permissions });
  25. } catch (error) {
  26. console.error('Error saving permission settings:', error);
  27. res.status(500).json({ error: 'Failed to save permission settings' });
  28. }
  29. });
  30. // ===============================
  31. // API Keys Management
  32. // ===============================
  33. // Get all API keys for the authenticated user
  34. router.get('/api-keys', async (req, res) => {
  35. try {
  36. const apiKeys = apiKeysDb.getApiKeys(req.user.id);
  37. // Don't send the full API key in the list for security
  38. const sanitizedKeys = apiKeys.map(key => ({
  39. ...key,
  40. api_key: key.api_key.substring(0, 10) + '...'
  41. }));
  42. res.json({ apiKeys: sanitizedKeys });
  43. } catch (error) {
  44. console.error('Error fetching API keys:', error);
  45. res.status(500).json({ error: 'Failed to fetch API keys' });
  46. }
  47. });
  48. // Create a new API key
  49. router.post('/api-keys', async (req, res) => {
  50. try {
  51. const { keyName } = req.body;
  52. if (!keyName || !keyName.trim()) {
  53. return res.status(400).json({ error: 'Key name is required' });
  54. }
  55. const result = apiKeysDb.createApiKey(req.user.id, keyName.trim());
  56. res.json({
  57. success: true,
  58. apiKey: result
  59. });
  60. } catch (error) {
  61. console.error('Error creating API key:', error);
  62. res.status(500).json({ error: 'Failed to create API key' });
  63. }
  64. });
  65. // Delete an API key
  66. router.delete('/api-keys/:keyId', async (req, res) => {
  67. try {
  68. const { keyId } = req.params;
  69. const success = apiKeysDb.deleteApiKey(req.user.id, parseInt(keyId));
  70. if (success) {
  71. res.json({ success: true });
  72. } else {
  73. res.status(404).json({ error: 'API key not found' });
  74. }
  75. } catch (error) {
  76. console.error('Error deleting API key:', error);
  77. res.status(500).json({ error: 'Failed to delete API key' });
  78. }
  79. });
  80. // Toggle API key active status
  81. router.patch('/api-keys/:keyId/toggle', async (req, res) => {
  82. try {
  83. const { keyId } = req.params;
  84. const { isActive } = req.body;
  85. if (typeof isActive !== 'boolean') {
  86. return res.status(400).json({ error: 'isActive must be a boolean' });
  87. }
  88. const success = apiKeysDb.toggleApiKey(req.user.id, parseInt(keyId), isActive);
  89. if (success) {
  90. res.json({ success: true });
  91. } else {
  92. res.status(404).json({ error: 'API key not found' });
  93. }
  94. } catch (error) {
  95. console.error('Error toggling API key:', error);
  96. res.status(500).json({ error: 'Failed to toggle API key' });
  97. }
  98. });
  99. // ===============================
  100. // Generic Credentials Management
  101. // ===============================
  102. // Get all credentials for the authenticated user (optionally filtered by type)
  103. router.get('/credentials', async (req, res) => {
  104. try {
  105. const { type } = req.query;
  106. const credentials = credentialsDb.getCredentials(req.user.id, type || null);
  107. // Don't send the actual credential values for security
  108. res.json({ credentials });
  109. } catch (error) {
  110. console.error('Error fetching credentials:', error);
  111. res.status(500).json({ error: 'Failed to fetch credentials' });
  112. }
  113. });
  114. // Create a new credential
  115. router.post('/credentials', async (req, res) => {
  116. try {
  117. const { credentialName, credentialType, credentialValue, description } = req.body;
  118. if (!credentialName || !credentialName.trim()) {
  119. return res.status(400).json({ error: 'Credential name is required' });
  120. }
  121. if (!credentialType || !credentialType.trim()) {
  122. return res.status(400).json({ error: 'Credential type is required' });
  123. }
  124. if (!credentialValue || !credentialValue.trim()) {
  125. return res.status(400).json({ error: 'Credential value is required' });
  126. }
  127. const result = credentialsDb.createCredential(
  128. req.user.id,
  129. credentialName.trim(),
  130. credentialType.trim(),
  131. credentialValue.trim(),
  132. description?.trim() || null
  133. );
  134. res.json({
  135. success: true,
  136. credential: result
  137. });
  138. } catch (error) {
  139. console.error('Error creating credential:', error);
  140. res.status(500).json({ error: 'Failed to create credential' });
  141. }
  142. });
  143. // Delete a credential
  144. router.delete('/credentials/:credentialId', async (req, res) => {
  145. try {
  146. const { credentialId } = req.params;
  147. const success = credentialsDb.deleteCredential(req.user.id, parseInt(credentialId));
  148. if (success) {
  149. res.json({ success: true });
  150. } else {
  151. res.status(404).json({ error: 'Credential not found' });
  152. }
  153. } catch (error) {
  154. console.error('Error deleting credential:', error);
  155. res.status(500).json({ error: 'Failed to delete credential' });
  156. }
  157. });
  158. // Toggle credential active status
  159. router.patch('/credentials/:credentialId/toggle', async (req, res) => {
  160. try {
  161. const { credentialId } = req.params;
  162. const { isActive } = req.body;
  163. if (typeof isActive !== 'boolean') {
  164. return res.status(400).json({ error: 'isActive must be a boolean' });
  165. }
  166. const success = credentialsDb.toggleCredential(req.user.id, parseInt(credentialId), isActive);
  167. if (success) {
  168. res.json({ success: true });
  169. } else {
  170. res.status(404).json({ error: 'Credential not found' });
  171. }
  172. } catch (error) {
  173. console.error('Error toggling credential:', error);
  174. res.status(500).json({ error: 'Failed to toggle credential' });
  175. }
  176. });
  177. // ===============================
  178. // Notification Preferences
  179. // ===============================
  180. router.get('/notification-preferences', async (req, res) => {
  181. try {
  182. const preferences = notificationPreferencesDb.getPreferences(req.user.id);
  183. res.json({ success: true, preferences });
  184. } catch (error) {
  185. console.error('Error fetching notification preferences:', error);
  186. res.status(500).json({ error: 'Failed to fetch notification preferences' });
  187. }
  188. });
  189. router.put('/notification-preferences', async (req, res) => {
  190. try {
  191. const preferences = notificationPreferencesDb.updatePreferences(req.user.id, req.body || {});
  192. res.json({ success: true, preferences });
  193. } catch (error) {
  194. console.error('Error saving notification preferences:', error);
  195. res.status(500).json({ error: 'Failed to save notification preferences' });
  196. }
  197. });
  198. // ===============================
  199. // Push Subscription Management
  200. // ===============================
  201. router.get('/push/vapid-public-key', async (req, res) => {
  202. try {
  203. const publicKey = getPublicKey();
  204. res.json({ publicKey });
  205. } catch (error) {
  206. console.error('Error fetching VAPID public key:', error);
  207. res.status(500).json({ error: 'Failed to fetch VAPID public key' });
  208. }
  209. });
  210. router.post('/push/subscribe', async (req, res) => {
  211. try {
  212. const { endpoint, keys } = req.body;
  213. if (!endpoint || !keys?.p256dh || !keys?.auth) {
  214. return res.status(400).json({ error: 'Missing subscription fields' });
  215. }
  216. pushSubscriptionsDb.saveSubscription(req.user.id, endpoint, keys.p256dh, keys.auth);
  217. // Enable webPush in preferences so the confirmation goes through the full pipeline
  218. const currentPrefs = notificationPreferencesDb.getPreferences(req.user.id);
  219. if (!currentPrefs?.channels?.webPush) {
  220. notificationPreferencesDb.updatePreferences(req.user.id, {
  221. ...currentPrefs,
  222. channels: { ...currentPrefs?.channels, webPush: true },
  223. });
  224. }
  225. res.json({ success: true });
  226. // Send a confirmation push through the full notification pipeline
  227. const event = createNotificationEvent({
  228. provider: 'system',
  229. kind: 'info',
  230. code: 'push.enabled',
  231. meta: { message: 'Push notifications are now enabled!' },
  232. severity: 'info'
  233. });
  234. notifyUserIfEnabled({ userId: req.user.id, event });
  235. } catch (error) {
  236. console.error('Error saving push subscription:', error);
  237. res.status(500).json({ error: 'Failed to save push subscription' });
  238. }
  239. });
  240. router.post('/push/unsubscribe', async (req, res) => {
  241. try {
  242. const { endpoint } = req.body;
  243. if (!endpoint) {
  244. return res.status(400).json({ error: 'Missing endpoint' });
  245. }
  246. pushSubscriptionsDb.removeSubscription(endpoint);
  247. // Disable webPush in preferences to match subscription state
  248. const currentPrefs = notificationPreferencesDb.getPreferences(req.user.id);
  249. if (currentPrefs?.channels?.webPush) {
  250. notificationPreferencesDb.updatePreferences(req.user.id, {
  251. ...currentPrefs,
  252. channels: { ...currentPrefs.channels, webPush: false },
  253. });
  254. }
  255. res.json({ success: true });
  256. } catch (error) {
  257. console.error('Error removing push subscription:', error);
  258. res.status(500).json({ error: 'Failed to remove push subscription' });
  259. }
  260. });
  261. export default router;