upfynai-code 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -0
- package/client/dist/assets/{AppContent-CRld2UWX.js → AppContent-C0CyP3g5.js} +3 -3
- package/client/dist/assets/{CanvasPanel-CB4sweQq.js → CanvasPanel-0u9QR7U-.js} +5 -5
- package/client/dist/assets/DashboardPanel-Dgqw1yZk.js +1 -0
- package/client/dist/assets/{LoginModal-BwkvjfPR.js → LoginModal-CZDEzqjK.js} +1 -1
- package/client/dist/assets/Onboarding-DR6NZ4Vz.js +1 -0
- package/client/dist/assets/{SetupForm-CH5EA5W0.js → SetupForm-D49gtWY4.js} +1 -1
- package/client/dist/assets/{WorkflowsPanel-CO5g5yGG.js → WorkflowsPanel-CqlbEJA_.js} +1 -1
- package/client/dist/assets/{ar-SA-G6X2FPQ2-DoJuo98H.js → ar-SA-G6X2FPQ2-BWqa1yBH.js} +1 -1
- package/client/dist/assets/{arc-B0wBaTeh.js → arc-BegSKqEW.js} +1 -1
- package/client/dist/assets/{az-AZ-76LH7QW2-xdrt1Z13.js → az-AZ-76LH7QW2-DrVlbZDP.js} +1 -1
- package/client/dist/assets/{bg-BG-XCXSNQG7-D8NAiF6Y.js → bg-BG-XCXSNQG7-DdunjBgT.js} +1 -1
- package/client/dist/assets/{blockDiagram-38ab4fdb-DSnyKzK4.js → blockDiagram-38ab4fdb-BKMbwGHu.js} +1 -1
- package/client/dist/assets/{bn-BD-2XOGV67Q-B0qWv8_J.js → bn-BD-2XOGV67Q-_7DtmvwO.js} +1 -1
- package/client/dist/assets/{c4Diagram-3d4e48cf-DoZJ13XA.js → c4Diagram-3d4e48cf-hJuiHhSn.js} +1 -1
- package/client/dist/assets/{ca-ES-6MX7JW3Y-RgLhfbZZ.js → ca-ES-6MX7JW3Y-BFIrmojG.js} +1 -1
- package/client/dist/assets/channel-Bur-rRTp.js +1 -0
- package/client/dist/assets/{classDiagram-70f12bd4-GNyDrRCk.js → classDiagram-70f12bd4-BjiAf9cM.js} +1 -1
- package/client/dist/assets/{classDiagram-v2-f2320105-CxdGhHm2.js → classDiagram-v2-f2320105-pwBewejc.js} +1 -1
- package/client/dist/assets/clone-BtqXeoBJ.js +1 -0
- package/client/dist/assets/{createText-2e5e7dd3-DiPywQOa.js → createText-2e5e7dd3-Dq_acOWe.js} +1 -1
- package/client/dist/assets/{cs-CZ-2BRQDIVT-BAjmnuoC.js → cs-CZ-2BRQDIVT-B-x4F6TJ.js} +1 -1
- package/client/dist/assets/{da-DK-5WZEPLOC-JxKVGt8o.js → da-DK-5WZEPLOC-Btlc8Dgn.js} +1 -1
- package/client/dist/assets/{de-DE-XR44H4JA-CrnRlt4z.js → de-DE-XR44H4JA-BVu3ZIoD.js} +1 -1
- package/client/dist/assets/{edges-e0da2a9e-DDsXzXLJ.js → edges-e0da2a9e-DH0wVTXR.js} +1 -1
- package/client/dist/assets/{el-GR-BZB4AONW-DQd8iogq.js → el-GR-BZB4AONW-h2ll8_ZC.js} +1 -1
- package/client/dist/assets/{erDiagram-9861fffd-CBiCC4rl.js → erDiagram-9861fffd-BYezLIR7.js} +1 -1
- package/client/dist/assets/{es-ES-U4NZUMDT-vvUblc5i.js → es-ES-U4NZUMDT-Cveiulwt.js} +1 -1
- package/client/dist/assets/{eu-ES-A7QVB2H4-De4NNCc1.js → eu-ES-A7QVB2H4-DQluL2PY.js} +1 -1
- package/client/dist/assets/{fa-IR-HGAKTJCU-DFBXqIqq.js → fa-IR-HGAKTJCU-BJtcMBSv.js} +1 -1
- package/client/dist/assets/{fi-FI-Z5N7JZ37-DV9zESPg.js → fi-FI-Z5N7JZ37-D8NfbVXV.js} +1 -1
- package/client/dist/assets/{flowDb-956e92f1-BhdSHbdO.js → flowDb-956e92f1-scnUykhM.js} +1 -1
- package/client/dist/assets/{flowDiagram-66a62f08-M-fp1_Ie.js → flowDiagram-66a62f08-jVyWsfyU.js} +1 -1
- package/client/dist/assets/flowDiagram-v2-96b9c2cf-N6xgi25h.js +1 -0
- package/client/dist/assets/{flowchart-elk-definition-4a651766-Bp0SonQx.js → flowchart-elk-definition-4a651766-gKGX3HqR.js} +1 -1
- package/client/dist/assets/{fr-FR-RHASNOE6-CKTMXuGk.js → fr-FR-RHASNOE6-vdj42kC6.js} +1 -1
- package/client/dist/assets/{ganttDiagram-c361ad54-iA737GUS.js → ganttDiagram-c361ad54-C2CiWFUP.js} +1 -1
- package/client/dist/assets/{gitGraphDiagram-72cf32ee-BX-wj-PV.js → gitGraphDiagram-72cf32ee-C59Yz2LK.js} +1 -1
- package/client/dist/assets/{gl-ES-HMX3MZ6V-Cdiqq4jY.js → gl-ES-HMX3MZ6V-DQo0TzoP.js} +1 -1
- package/client/dist/assets/{graph-Rxkx3sEa.js → graph-Dx_H43Kv.js} +1 -1
- package/client/dist/assets/{he-IL-6SHJWFNN-gYmR5_KT.js → he-IL-6SHJWFNN-DKXK5e33.js} +1 -1
- package/client/dist/assets/{hi-IN-IWLTKZ5I-pyqK94AR.js → hi-IN-IWLTKZ5I-C2Qgqc0R.js} +1 -1
- package/client/dist/assets/{hu-HU-A5ZG7DT2-DpacJgJy.js → hu-HU-A5ZG7DT2-Ss-6vX0m.js} +1 -1
- package/client/dist/assets/{id-ID-SAP4L64H-CAvIX-mj.js → id-ID-SAP4L64H-D7Wsg1S2.js} +1 -1
- package/client/dist/assets/{index-3862675e-BX3Fpn6V.js → index-3862675e-u8Nv7hHC.js} +1 -1
- package/client/dist/assets/{index-BBlwbHq_.js → index-BVowJdZF.js} +4 -4
- package/client/dist/assets/{index-ClfzLIqY.js → index-ce18TYkg.js} +4 -4
- package/client/dist/assets/index-kQoJx-bc.css +1 -0
- package/client/dist/assets/{infoDiagram-f8f76790-Ckv8imiv.js → infoDiagram-f8f76790-LmoJYsxo.js} +1 -1
- package/client/dist/assets/{it-IT-JPQ66NNP-BtpNRSce.js → it-IT-JPQ66NNP-CAPTVl7M.js} +1 -1
- package/client/dist/assets/{ja-JP-DBVTYXUO-CwJRyY6M.js → ja-JP-DBVTYXUO-eNVPawR2.js} +1 -1
- package/client/dist/assets/{journeyDiagram-49397b02-DWWZssji.js → journeyDiagram-49397b02-BaJqehpR.js} +1 -1
- package/client/dist/assets/{kaa-6HZHGXH3-DIWQEb4A.js → kaa-6HZHGXH3-tpuNkKhS.js} +1 -1
- package/client/dist/assets/{kab-KAB-ZGHBKWFO-DjGbqhUg.js → kab-KAB-ZGHBKWFO-Dp83kx4x.js} +1 -1
- package/client/dist/assets/{kk-KZ-P5N5QNE5-B_VzJdWf.js → kk-KZ-P5N5QNE5-B9IlC6YN.js} +1 -1
- package/client/dist/assets/{km-KH-HSX4SM5Z-DUD5mi0o.js → km-KH-HSX4SM5Z-B_KMYaMj.js} +1 -1
- package/client/dist/assets/{ko-KR-MTYHY66A--sDB10db.js → ko-KR-MTYHY66A-yebnUNdb.js} +1 -1
- package/client/dist/assets/{ku-TR-6OUDTVRD-CKvKrkcX.js → ku-TR-6OUDTVRD-BR6fh6-5.js} +1 -1
- package/client/dist/assets/{layout-CkB7sSeq.js → layout-DLl5Jwcl.js} +1 -1
- package/client/dist/assets/{line-DC7MA9qY.js → line-FpB7omSK.js} +1 -1
- package/client/dist/assets/{linear-C1lBBthf.js → linear-CkXqUFJ8.js} +1 -1
- package/client/dist/assets/{lt-LT-XHIRWOB4-MSZf7xYG.js → lt-LT-XHIRWOB4-SutZSWtR.js} +1 -1
- package/client/dist/assets/{lv-LV-5QDEKY6T-C-gvvmBB.js → lv-LV-5QDEKY6T-DuAxdcZL.js} +1 -1
- package/client/dist/assets/{mindmap-definition-fc14e90a-B3O7hztq.js → mindmap-definition-fc14e90a-DyxXOExh.js} +1 -1
- package/client/dist/assets/{mr-IN-CRQNXWMA-XHtBUWQH.js → mr-IN-CRQNXWMA-DqDUWM_8.js} +1 -1
- package/client/dist/assets/{my-MM-5M5IBNSE-D9eD2edL.js → my-MM-5M5IBNSE-C40kMFMR.js} +1 -1
- package/client/dist/assets/{nb-NO-T6EIAALU-BlImC6gp.js → nb-NO-T6EIAALU-DVij32Ju.js} +1 -1
- package/client/dist/assets/{nl-NL-IS3SIHDZ-CPFhnaSP.js → nl-NL-IS3SIHDZ-rT84mDYq.js} +1 -1
- package/client/dist/assets/{nn-NO-6E72VCQL-BMvoJSKQ.js → nn-NO-6E72VCQL-BBZXBW8V.js} +1 -1
- package/client/dist/assets/{oc-FR-POXYY2M6-Buye63LS.js → oc-FR-POXYY2M6-DzjOugOf.js} +1 -1
- package/client/dist/assets/{pa-IN-N4M65BXN-D9uQ3niy.js → pa-IN-N4M65BXN-DD1iU8_F.js} +1 -1
- package/client/dist/assets/{percentages-BXMCSKIN-BzXIakGM.js → percentages-BXMCSKIN-WVlHS4wx.js} +6 -6
- package/client/dist/assets/{pieDiagram-8a3498a8-BU38mzx-.js → pieDiagram-8a3498a8-Dd_85qBH.js} +1 -1
- package/client/dist/assets/{pl-PL-T2D74RX3-BqM4xdcg.js → pl-PL-T2D74RX3-ukVXa48G.js} +1 -1
- package/client/dist/assets/{pt-BR-5N22H2LF-rAjrxGyI.js → pt-BR-5N22H2LF-BibawarT.js} +1 -1
- package/client/dist/assets/{pt-PT-UZXXM6DQ-DXsqcwLt.js → pt-PT-UZXXM6DQ-So3i9l9w.js} +1 -1
- package/client/dist/assets/{quadrantDiagram-120e2f19-HhK4H1WU.js → quadrantDiagram-120e2f19-C4dFVDEx.js} +1 -1
- package/client/dist/assets/{requirementDiagram-deff3bca-aDrcyj-A.js → requirementDiagram-deff3bca-DrTO7yFl.js} +1 -1
- package/client/dist/assets/{ro-RO-JPDTUUEW-D_F9UKer.js → ro-RO-JPDTUUEW-DY0Xq_Hd.js} +1 -1
- package/client/dist/assets/{ru-RU-B4JR7IUQ-MirqN29p.js → ru-RU-B4JR7IUQ-B7u_Zvkd.js} +1 -1
- package/client/dist/assets/{sankeyDiagram-04a897e0-C6ij7qbQ.js → sankeyDiagram-04a897e0-D24gfzuS.js} +1 -1
- package/client/dist/assets/{sequenceDiagram-704730f1-C0EKO3th.js → sequenceDiagram-704730f1-Dgji2XLQ.js} +1 -1
- package/client/dist/assets/{si-LK-N5RQ5JYF-DyZC3mkC.js → si-LK-N5RQ5JYF-OejsLzQ_.js} +1 -1
- package/client/dist/assets/{sk-SK-C5VTKIMK-D-ksz-WY.js → sk-SK-C5VTKIMK-_vy2Bt-M.js} +1 -1
- package/client/dist/assets/{sl-SI-NN7IZMDC-CknuYoQ1.js → sl-SI-NN7IZMDC-DKOl_u2M.js} +1 -1
- package/client/dist/assets/{stateDiagram-587899a1-CYoq2VjL.js → stateDiagram-587899a1-CJ8eBaiU.js} +1 -1
- package/client/dist/assets/{stateDiagram-v2-d93cdb3a-C5lbp5px.js → stateDiagram-v2-d93cdb3a-C5K3l-Nt.js} +1 -1
- package/client/dist/assets/{styles-6aaf32cf-Dkfsk8gt.js → styles-6aaf32cf-DAKE0jbx.js} +1 -1
- package/client/dist/assets/{styles-9a916d00-CMYqtcEN.js → styles-9a916d00-LFAJCgEy.js} +1 -1
- package/client/dist/assets/{styles-c10674c1-Bp-5OlRU.js → styles-c10674c1-CllKO8NG.js} +1 -1
- package/client/dist/assets/{subset-shared.chunk-kfIB1Zam.js → subset-shared.chunk-Uy-J87FQ.js} +1 -1
- package/client/dist/assets/{subset-worker.chunk-DwQBgc4z.js → subset-worker.chunk-dvgDvqt9.js} +1 -1
- package/client/dist/assets/{sv-SE-XGPEYMSR-DwN13se1.js → sv-SE-XGPEYMSR-CDCB2ZV5.js} +1 -1
- package/client/dist/assets/{svgDrawCommon-08f97a94-CEgCMqs4.js → svgDrawCommon-08f97a94-CObOzbFQ.js} +1 -1
- package/client/dist/assets/{ta-IN-2NMHFXQM-ejDfFhwa.js → ta-IN-2NMHFXQM-DHUNdO69.js} +1 -1
- package/client/dist/assets/{th-TH-HPSO5L25-Bqc90ZNn.js → th-TH-HPSO5L25-zI2hnBq3.js} +1 -1
- package/client/dist/assets/{timeline-definition-85554ec2-BmGdKqG0.js → timeline-definition-85554ec2-C2XHRmxK.js} +1 -1
- package/client/dist/assets/{tr-TR-DEFEU3FU-CJvlPbcW.js → tr-TR-DEFEU3FU-l-6Hu4-D.js} +1 -1
- package/client/dist/assets/{uk-UA-QMV73CPH-D26-cbWL.js → uk-UA-QMV73CPH-CqSOwrl7.js} +1 -1
- package/client/dist/assets/{vendor-icons-aNdOvTr_.js → vendor-icons-Lb69KSFJ.js} +136 -126
- package/client/dist/assets/{vi-VN-M7AON7JQ-MbqIIwYM.js → vi-VN-M7AON7JQ-CUL8-mBZ.js} +1 -1
- package/client/dist/assets/{xychartDiagram-e933f94c-gfcTauxU.js → xychartDiagram-e933f94c-1fmf6slj.js} +1 -1
- package/client/dist/assets/{zh-CN-LNUGB5OW-BZSmhUdL.js → zh-CN-LNUGB5OW-CB5y5VVU.js} +1 -1
- package/client/dist/assets/{zh-HK-E62DVLB3-BJqejpiX.js → zh-HK-E62DVLB3-BHcrrEeJ.js} +1 -1
- package/client/dist/assets/{zh-TW-RAJ6MFWO-BBXtV-Uz.js → zh-TW-RAJ6MFWO-DoDUdkaJ.js} +1 -1
- package/client/dist/index.html +3 -3
- package/package.json +17 -14
- package/server/cli.js +44 -0
- package/server/database/db.js +16 -2
- package/server/index.js +2738 -2621
- package/server/middleware/auth.js +10 -2
- package/server/relay-client.js +73 -20
- package/server/routes/agent.js +1226 -1266
- package/server/routes/auth.js +32 -29
- package/server/routes/commands.js +598 -601
- package/server/routes/cursor.js +806 -807
- package/server/routes/dashboard.js +154 -1
- package/server/routes/git.js +1151 -1165
- package/server/routes/mcp.js +534 -551
- package/server/routes/settings.js +261 -269
- package/server/routes/taskmaster.js +1927 -1963
- package/server/routes/vapi-chat.js +94 -0
- package/server/routes/voice.js +0 -4
- package/server/sandbox.js +120 -0
- package/client/dist/assets/DashboardPanel-BXaA-b9z.js +0 -1
- package/client/dist/assets/Onboarding-2A_5fPxy.js +0 -1
- package/client/dist/assets/channel-BmO6nY0W.js +0 -1
- package/client/dist/assets/clone-xuHMqFoD.js +0 -1
- package/client/dist/assets/flowDiagram-v2-96b9c2cf-C5eiN8Pg.js +0 -1
- package/client/dist/assets/index-Td4UdtLF.css +0 -1
|
@@ -1,269 +1,261 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import { apiKeysDb, credentialsDb } from '../database/db.js';
|
|
3
|
-
|
|
4
|
-
const router = express.Router();
|
|
5
|
-
|
|
6
|
-
// ===============================
|
|
7
|
-
// API Keys Management
|
|
8
|
-
// ===============================
|
|
9
|
-
|
|
10
|
-
// Get all API keys for the authenticated user
|
|
11
|
-
router.get('/api-keys', async (req, res) => {
|
|
12
|
-
try {
|
|
13
|
-
const apiKeys = await apiKeysDb.getApiKeys(req.user.id);
|
|
14
|
-
// Don't send the full API key in the list for security
|
|
15
|
-
const sanitizedKeys = apiKeys.map(key => ({
|
|
16
|
-
...key,
|
|
17
|
-
api_key: key.api_key.substring(0, 10) + '...'
|
|
18
|
-
}));
|
|
19
|
-
res.json({ apiKeys: sanitizedKeys });
|
|
20
|
-
} catch (error) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
res.json({
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
res.json({
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
res.status(404).json({ error: 'Provider key not found' });
|
|
263
|
-
}
|
|
264
|
-
} catch (error) {
|
|
265
|
-
res.status(500).json({ error: 'Failed to delete provider key' });
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
export default router;
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { apiKeysDb, credentialsDb } from '../database/db.js';
|
|
3
|
+
|
|
4
|
+
const router = express.Router();
|
|
5
|
+
|
|
6
|
+
// ===============================
|
|
7
|
+
// API Keys Management
|
|
8
|
+
// ===============================
|
|
9
|
+
|
|
10
|
+
// Get all API keys for the authenticated user
|
|
11
|
+
router.get('/api-keys', async (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const apiKeys = await apiKeysDb.getApiKeys(req.user.id);
|
|
14
|
+
// Don't send the full API key in the list for security
|
|
15
|
+
const sanitizedKeys = apiKeys.map(key => ({
|
|
16
|
+
...key,
|
|
17
|
+
api_key: key.api_key.substring(0, 10) + '...'
|
|
18
|
+
}));
|
|
19
|
+
res.json({ apiKeys: sanitizedKeys });
|
|
20
|
+
} catch (error) {
|
|
21
|
+
res.status(500).json({ error: 'Failed to fetch API keys' });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Create a new API key
|
|
26
|
+
router.post('/api-keys', async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const { keyName } = req.body;
|
|
29
|
+
|
|
30
|
+
if (!keyName || !keyName.trim()) {
|
|
31
|
+
return res.status(400).json({ error: 'Key name is required' });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const result = await apiKeysDb.createApiKey(req.user.id, keyName.trim());
|
|
35
|
+
res.json({
|
|
36
|
+
success: true,
|
|
37
|
+
apiKey: result
|
|
38
|
+
});
|
|
39
|
+
} catch (error) {
|
|
40
|
+
res.status(500).json({ error: 'Failed to create API key' });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Delete an API key
|
|
45
|
+
router.delete('/api-keys/:keyId', async (req, res) => {
|
|
46
|
+
try {
|
|
47
|
+
const { keyId } = req.params;
|
|
48
|
+
const success = await apiKeysDb.deleteApiKey(req.user.id, parseInt(keyId));
|
|
49
|
+
|
|
50
|
+
if (success) {
|
|
51
|
+
res.json({ success: true });
|
|
52
|
+
} else {
|
|
53
|
+
res.status(404).json({ error: 'API key not found' });
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
res.status(500).json({ error: 'Failed to delete API key' });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Toggle API key active status
|
|
61
|
+
router.patch('/api-keys/:keyId/toggle', async (req, res) => {
|
|
62
|
+
try {
|
|
63
|
+
const { keyId } = req.params;
|
|
64
|
+
const { isActive } = req.body;
|
|
65
|
+
|
|
66
|
+
if (typeof isActive !== 'boolean') {
|
|
67
|
+
return res.status(400).json({ error: 'isActive must be a boolean' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const success = await apiKeysDb.toggleApiKey(req.user.id, parseInt(keyId), isActive);
|
|
71
|
+
|
|
72
|
+
if (success) {
|
|
73
|
+
res.json({ success: true });
|
|
74
|
+
} else {
|
|
75
|
+
res.status(404).json({ error: 'API key not found' });
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
res.status(500).json({ error: 'Failed to toggle API key' });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ===============================
|
|
83
|
+
// Generic Credentials Management
|
|
84
|
+
// ===============================
|
|
85
|
+
|
|
86
|
+
// Get all credentials for the authenticated user (optionally filtered by type)
|
|
87
|
+
router.get('/credentials', async (req, res) => {
|
|
88
|
+
try {
|
|
89
|
+
const { type } = req.query;
|
|
90
|
+
const credentials = await credentialsDb.getCredentials(req.user.id, type || null);
|
|
91
|
+
// Don't send the actual credential values for security
|
|
92
|
+
res.json({ credentials });
|
|
93
|
+
} catch (error) {
|
|
94
|
+
res.status(500).json({ error: 'Failed to fetch credentials' });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Create a new credential
|
|
99
|
+
router.post('/credentials', async (req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
const { credentialName, credentialType, credentialValue, description } = req.body;
|
|
102
|
+
|
|
103
|
+
if (!credentialName || !credentialName.trim()) {
|
|
104
|
+
return res.status(400).json({ error: 'Credential name is required' });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!credentialType || !credentialType.trim()) {
|
|
108
|
+
return res.status(400).json({ error: 'Credential type is required' });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!credentialValue || !credentialValue.trim()) {
|
|
112
|
+
return res.status(400).json({ error: 'Credential value is required' });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const result = await credentialsDb.createCredential(
|
|
116
|
+
req.user.id,
|
|
117
|
+
credentialName.trim(),
|
|
118
|
+
credentialType.trim(),
|
|
119
|
+
credentialValue.trim(),
|
|
120
|
+
description?.trim() || null
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
res.json({
|
|
124
|
+
success: true,
|
|
125
|
+
credential: result
|
|
126
|
+
});
|
|
127
|
+
} catch (error) {
|
|
128
|
+
res.status(500).json({ error: 'Failed to create credential' });
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Delete a credential
|
|
133
|
+
router.delete('/credentials/:credentialId', async (req, res) => {
|
|
134
|
+
try {
|
|
135
|
+
const { credentialId } = req.params;
|
|
136
|
+
const success = await credentialsDb.deleteCredential(req.user.id, parseInt(credentialId));
|
|
137
|
+
|
|
138
|
+
if (success) {
|
|
139
|
+
res.json({ success: true });
|
|
140
|
+
} else {
|
|
141
|
+
res.status(404).json({ error: 'Credential not found' });
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
res.status(500).json({ error: 'Failed to delete credential' });
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Toggle credential active status
|
|
149
|
+
router.patch('/credentials/:credentialId/toggle', async (req, res) => {
|
|
150
|
+
try {
|
|
151
|
+
const { credentialId } = req.params;
|
|
152
|
+
const { isActive } = req.body;
|
|
153
|
+
|
|
154
|
+
if (typeof isActive !== 'boolean') {
|
|
155
|
+
return res.status(400).json({ error: 'isActive must be a boolean' });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const success = await credentialsDb.toggleCredential(req.user.id, parseInt(credentialId), isActive);
|
|
159
|
+
|
|
160
|
+
if (success) {
|
|
161
|
+
res.json({ success: true });
|
|
162
|
+
} else {
|
|
163
|
+
res.status(404).json({ error: 'Credential not found' });
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
res.status(500).json({ error: 'Failed to toggle credential' });
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// ===============================
|
|
171
|
+
// AI Provider Keys (BYOK)
|
|
172
|
+
// ===============================
|
|
173
|
+
|
|
174
|
+
const AI_PROVIDER_TYPES = [
|
|
175
|
+
'anthropic_key',
|
|
176
|
+
'openai_key',
|
|
177
|
+
'openrouter_key',
|
|
178
|
+
'google_key',
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
// Get all AI provider keys for the authenticated user (masked values)
|
|
182
|
+
router.get('/ai-providers', async (req, res) => {
|
|
183
|
+
try {
|
|
184
|
+
const allCreds = [];
|
|
185
|
+
for (const type of AI_PROVIDER_TYPES) {
|
|
186
|
+
const creds = await credentialsDb.getCredentials(req.user.id, type);
|
|
187
|
+
allCreds.push(...creds.map(c => ({
|
|
188
|
+
id: c.id,
|
|
189
|
+
credential_name: c.credential_name,
|
|
190
|
+
credential_type: c.credential_type,
|
|
191
|
+
description: c.description,
|
|
192
|
+
is_active: c.is_active,
|
|
193
|
+
created_at: c.created_at,
|
|
194
|
+
// Mask the key — show first 8 and last 4 chars
|
|
195
|
+
masked_value: c.credential_value
|
|
196
|
+
? c.credential_value.slice(0, 8) + '...' + c.credential_value.slice(-4)
|
|
197
|
+
: '***',
|
|
198
|
+
})));
|
|
199
|
+
}
|
|
200
|
+
res.json({ providers: allCreds });
|
|
201
|
+
} catch (error) {
|
|
202
|
+
res.status(500).json({ error: 'Failed to fetch AI provider keys' });
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Save an AI provider key
|
|
207
|
+
router.post('/ai-providers', async (req, res) => {
|
|
208
|
+
try {
|
|
209
|
+
const { providerType, apiKey, name } = req.body;
|
|
210
|
+
|
|
211
|
+
if (!providerType || !AI_PROVIDER_TYPES.includes(providerType)) {
|
|
212
|
+
return res.status(400).json({ error: `Invalid provider type. Supported: ${AI_PROVIDER_TYPES.join(', ')}` });
|
|
213
|
+
}
|
|
214
|
+
if (!apiKey || !apiKey.trim()) {
|
|
215
|
+
return res.status(400).json({ error: 'API key is required' });
|
|
216
|
+
}
|
|
217
|
+
if (apiKey.trim().length < 10 || apiKey.trim().length > 256) {
|
|
218
|
+
return res.status(400).json({ error: 'Invalid API key length' });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const label = providerType.replace('_key', '').replace('_', ' ');
|
|
222
|
+
const credName = name?.trim() || `${label} API key`;
|
|
223
|
+
|
|
224
|
+
// Deactivate existing keys of same type (user should only have one active per provider)
|
|
225
|
+
const existing = await credentialsDb.getCredentials(req.user.id, providerType);
|
|
226
|
+
for (const cred of existing) {
|
|
227
|
+
if (cred.is_active) {
|
|
228
|
+
await credentialsDb.toggleCredential(req.user.id, cred.id, false);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const result = await credentialsDb.createCredential(
|
|
233
|
+
req.user.id,
|
|
234
|
+
credName,
|
|
235
|
+
providerType,
|
|
236
|
+
apiKey.trim(),
|
|
237
|
+
`User-provided ${label} API key`
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
res.json({ success: true, credential: { id: result.id, credential_type: providerType, credential_name: credName } });
|
|
241
|
+
} catch (error) {
|
|
242
|
+
res.status(500).json({ error: 'Failed to save AI provider key' });
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Delete an AI provider key
|
|
247
|
+
router.delete('/ai-providers/:credentialId', async (req, res) => {
|
|
248
|
+
try {
|
|
249
|
+
const { credentialId } = req.params;
|
|
250
|
+
const success = await credentialsDb.deleteCredential(req.user.id, parseInt(credentialId));
|
|
251
|
+
if (success) {
|
|
252
|
+
res.json({ success: true });
|
|
253
|
+
} else {
|
|
254
|
+
res.status(404).json({ error: 'Provider key not found' });
|
|
255
|
+
}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
res.status(500).json({ error: 'Failed to delete provider key' });
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
export default router;
|