cognova 0.2.7 → 0.2.9
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{Bb9LNSu_.js → -JdScH3W.js} +1 -1
- package/.output/public/_nuxt/{A5lrd5_j.js → 0EY9Msdx.js} +1 -1
- package/.output/public/_nuxt/{BsXPllgv.js → 1lOC4Si0.js} +1 -1
- package/.output/public/_nuxt/{CeDxLTxX.js → 1zIQX9mh.js} +1 -1
- package/.output/public/_nuxt/{D3V98LVR.js → 4Qy4OdWt.js} +1 -1
- package/.output/public/_nuxt/{MdebRWqA.js → 4nN41Tyd.js} +1 -1
- package/.output/public/_nuxt/{BrICHPil.js → 5MZBbx-H.js} +1 -1
- package/.output/public/_nuxt/{Brqrl-cH.js → 6lIxuL6n.js} +1 -1
- package/.output/public/_nuxt/AUdD1uDD.js +1 -0
- package/.output/public/_nuxt/{B9Kd_0HK.js → B-513wJW.js} +1 -1
- package/.output/public/_nuxt/{CNwGp6eP.js → B1vDCixB.js} +1 -1
- package/.output/public/_nuxt/{deSeNTOh.js → B2MVZlI-.js} +1 -1
- package/.output/public/_nuxt/{CVr40yKH.js → B5JDxHxC.js} +1 -1
- package/.output/public/_nuxt/{CvMqUdkl.js → B63LIwsW.js} +3 -3
- package/.output/public/_nuxt/{C5nfaH9w.js → B6bnHNWY.js} +1 -1
- package/.output/public/_nuxt/{BaJEGr8G.js → B6wYZD_K.js} +1 -1
- package/.output/public/_nuxt/{_16_b82K.js → B8Ou5yh2.js} +1 -1
- package/.output/public/_nuxt/{BaVaTug8.js → BE4Ndhrq.js} +1 -1
- package/.output/public/_nuxt/{CphXSvBo.js → BES6FzF5.js} +1 -1
- package/.output/public/_nuxt/{BmTCDcNm.js → BE_OHTBv.js} +1 -1
- package/.output/public/_nuxt/{CBrXB4hk.js → BJ8jbKuq.js} +1 -1
- package/.output/public/_nuxt/{vM-wdhkZ.js → BNBGEa-U.js} +1 -1
- package/.output/public/_nuxt/{XzRnkkwG.js → BOGXUgGg.js} +1 -1
- package/.output/public/_nuxt/BQ1T6zWA.js +1 -0
- package/.output/public/_nuxt/{Bb1B0ntt.js → BVc_fOF1.js} +1 -1
- package/.output/public/_nuxt/{B-5roYpk.js → BX6vcvkp.js} +1 -1
- package/.output/public/_nuxt/{CLZS246x.js → BYIq3eP8.js} +1 -1
- package/.output/public/_nuxt/{B_28CKij.js → BZqdzrSw.js} +2 -2
- package/.output/public/_nuxt/{D-wVwRIx.js → BaYtugUf.js} +1 -1
- package/.output/public/_nuxt/{EgYyfo1g.js → Be5L93GM.js} +3 -3
- package/.output/public/_nuxt/{BLszW9T3.js → BfECQayu.js} +1 -1
- package/.output/public/_nuxt/{Ci-MVdCd.js → BgG2k-zy.js} +1 -1
- package/.output/public/_nuxt/BhnONkIm.js +1 -0
- package/.output/public/_nuxt/{BjFs8Ujb.js → BjI5wb_q.js} +1 -1
- package/.output/public/_nuxt/{CK5Ef4YR.js → Bk9-HxOg.js} +1 -1
- package/.output/public/_nuxt/{MzEmU9ER.js → BltasKce.js} +1 -1
- package/.output/public/_nuxt/{BvuV9uCn.js → Bp9kHdw6.js} +1 -1
- package/.output/public/_nuxt/{CnW8zyuA.js → Bsdcw5gr.js} +1 -1
- package/.output/public/_nuxt/{Zjvb5YJR.js → BsnhHCOa.js} +1 -1
- package/.output/public/_nuxt/{DMsHLHmB.js → BwJVN9k0.js} +1 -1
- package/.output/public/_nuxt/{CgQKyBSS.js → BwVt3BzR.js} +1 -1
- package/.output/public/_nuxt/{Bc0NIKN7.js → BwdxQyyo.js} +1 -1
- package/.output/public/_nuxt/{CfWDee_q.js → BzTjmQCD.js} +1 -1
- package/.output/public/_nuxt/{RRoc4m9d.js → C1FhgWcI.js} +1 -1
- package/.output/public/_nuxt/{DvgLoqaC.js → C2DZeD9G.js} +1 -1
- package/.output/public/_nuxt/{B43p0So7.js → C3JOLqW1.js} +1 -1
- package/.output/public/_nuxt/{atVSHCxP.js → C4ScCUjy.js} +1 -1
- package/.output/public/_nuxt/{Bn8loaeQ.js → C6HEirAm.js} +1 -1
- package/.output/public/_nuxt/{BLL1JLwq.js → C6UTTaCP.js} +1 -1
- package/.output/public/_nuxt/{DNpsjOxN.js → C8gtQaKB.js} +1 -1
- package/.output/public/_nuxt/{BhOIdu3Z.js → C8tXHRIo.js} +1 -1
- package/.output/public/_nuxt/{DaiWSWdm.js → C8wa4Wh4.js} +1 -1
- package/.output/public/_nuxt/{DZdh7_lh.js → CDradKPE.js} +1 -1
- package/.output/public/_nuxt/{DB3y_21M.js → CEJh-OAl.js} +1 -1
- package/.output/public/_nuxt/CGfVTQUF.js +1 -0
- package/.output/public/_nuxt/{Dx2Tgm6S.js → CGgZuPnb.js} +1 -1
- package/.output/public/_nuxt/{CwaMSU1O.js → CH8kNq6W.js} +1 -1
- package/.output/public/_nuxt/{NlScC3GW.js → CIJ2MJdc.js} +1 -1
- package/.output/public/_nuxt/{CzTTKfet.js → CIUZAp6g.js} +1 -1
- package/.output/public/_nuxt/{CwCEQ4Dw.js → CXBOHJ2W.js} +1 -1
- package/.output/public/_nuxt/{BJ_GgKV0.js → CY3x8iy3.js} +1 -1
- package/.output/public/_nuxt/{ByfSJZV6.js → CaLO8sre.js} +1 -1
- package/.output/public/_nuxt/{jeu3Xx8e.js → CbTu13wM.js} +1 -1
- package/.output/public/_nuxt/{CjYUi9c8.js → CfO_d5gC.js} +1 -1
- package/.output/public/_nuxt/{DpYns7cT.js → Clr3MHUr.js} +1 -1
- package/.output/public/_nuxt/{1Hz98MKF.js → Cmdr5BNz.js} +1 -1
- package/.output/public/_nuxt/{DZ_azKLI.js → Cmj-BR0q.js} +1 -1
- package/.output/public/_nuxt/{CSQ-nblB.js → Cn83lLUb.js} +1 -1
- package/.output/public/_nuxt/{BdCB2PoJ.js → CqMgz85Z.js} +1 -1
- package/.output/public/_nuxt/{Dkzqkzki.js → CrW6y2-s.js} +1 -1
- package/.output/public/_nuxt/{DAs574wU.js → CrjNndEO.js} +1 -1
- package/.output/public/_nuxt/{CB8_A8N4.js → Cs_udj6A.js} +1 -1
- package/.output/public/_nuxt/{qW7F87d8.js → CsbHYpNi.js} +3 -3
- package/.output/public/_nuxt/{DuOsLoVR.js → CwKe2KkK.js} +1 -1
- package/.output/public/_nuxt/{Db5XIfWS.js → CwOUYOe5.js} +1 -1
- package/.output/public/_nuxt/{0J8pJb0Z.js → CybU4a95.js} +1 -1
- package/.output/public/_nuxt/{WWNeb45u.js → CziltI1u.js} +1 -1
- package/.output/public/_nuxt/{DgPiIWA2.js → CzurfTnt.js} +1 -1
- package/.output/public/_nuxt/{DAFasgHH.js → D-O8wAju.js} +1 -1
- package/.output/public/_nuxt/{CJ85o8FK.js → D1t0272g.js} +1 -1
- package/.output/public/_nuxt/D3RROe4s.js +1 -0
- package/.output/public/_nuxt/{Cedv1M65.js → D5G5UL_g.js} +1 -1
- package/.output/public/_nuxt/{1IuG3yiQ.js → D76Dw8rw.js} +1 -1
- package/.output/public/_nuxt/{w3t8WD8v.js → D7DTZ0iS.js} +1 -1
- package/.output/public/_nuxt/{C6Kc4XvS.js → D7WbsJsa.js} +1 -1
- package/.output/public/_nuxt/D7iYVmH6.js +1 -0
- package/.output/public/_nuxt/{CXuKrnTA.js → D8gh62A0.js} +1 -1
- package/.output/public/_nuxt/{CW8xtqwN.js → DA2RowFe.js} +1 -1
- package/.output/public/_nuxt/{4bC1z31I.js → DBKxzQ3U.js} +1 -1
- package/.output/public/_nuxt/DBuW01Fx.js +1 -0
- package/.output/public/_nuxt/{B7-0ADn3.js → DCL9gWFw.js} +1 -1
- package/.output/public/_nuxt/{NBE83ZQn.js → DGJKfutb.js} +1 -1
- package/.output/public/_nuxt/{B5OWqWCh.js → DHScQdvh.js} +1 -1
- package/.output/public/_nuxt/{BTk5mgdf.js → DI1FIvaM.js} +1 -1
- package/.output/public/_nuxt/{0LyRrTRM.js → DJ4pqxg-.js} +1 -1
- package/.output/public/_nuxt/{DImdNj-F.js → DJoRBCVm.js} +1 -1
- package/.output/public/_nuxt/{DfpSaqgV.js → DLuRYg2p.js} +1 -1
- package/.output/public/_nuxt/DMy2sxuC.js +1 -0
- package/.output/public/_nuxt/{BJi2ZHtT.js → DQd-GXKJ.js} +1 -1
- package/.output/public/_nuxt/{CpC3C9gj.js → DSgubznn.js} +1 -1
- package/.output/public/_nuxt/{BZFXnEwe.js → DSynhNOO.js} +1 -1
- package/.output/public/_nuxt/{BsPj3ifk.js → DTrFhdRO.js} +1 -1
- package/.output/public/_nuxt/{BNlugvM3.js → DTu0K_pX.js} +1 -1
- package/.output/public/_nuxt/{DV0JMebZ.js → DUDbJONU.js} +1 -1
- package/.output/public/_nuxt/{C14DGwaT.js → DYu_NYj3.js} +1 -1
- package/.output/public/_nuxt/{Bf34lExB.js → D_tCgCFX.js} +1 -1
- package/.output/public/_nuxt/{pkbwpgSD.js → DdXVAzIg.js} +1 -1
- package/.output/public/_nuxt/DfIqFkyp.js +1 -0
- package/.output/public/_nuxt/{BoPAYize.js → Dj2opPDu.js} +1 -1
- package/.output/public/_nuxt/{Drr-RSSP.js → DjmFlCHK.js} +1 -1
- package/.output/public/_nuxt/{DgKeJf5C.js → DkhxRAb3.js} +1 -1
- package/.output/public/_nuxt/DlSJ4TF_.js +1 -0
- package/.output/public/_nuxt/{S4a_N6rD.js → DmmQUhEb.js} +1 -1
- package/.output/public/_nuxt/{CG0fS-BQ.js → DvATBMPl.js} +1 -1
- package/.output/public/_nuxt/{CxJr4mB0.js → DwAJIRGg.js} +1 -1
- package/.output/public/_nuxt/{CjRmY2OQ.js → FYOLNuMV.js} +1 -1
- package/.output/public/_nuxt/Gy6_ehml.js +1 -0
- package/.output/public/_nuxt/{zBoPDS4z.js → HPOXd4gt.js} +1 -1
- package/.output/public/_nuxt/{HCCuOAgZ.js → HlbU_z9a.js} +1 -1
- package/.output/public/_nuxt/{DsKy-gee.js → JQV21WEV.js} +1 -1
- package/.output/public/_nuxt/{JJIcQEna.js → NusIKwmA.js} +1 -1
- package/.output/public/_nuxt/{HC3657Xq.js → OrcbFmB1.js} +1 -1
- package/.output/public/_nuxt/{fTEmwfo4.js → Oy4_RPEZ.js} +1 -1
- package/.output/public/_nuxt/{Dpp5cOwJ.js → Pa936BQP.js} +1 -1
- package/.output/public/_nuxt/PzQHm02e.js +1 -0
- package/.output/public/_nuxt/{Dysa6np_.js → REP2-rzf.js} +1 -1
- package/.output/public/_nuxt/{-2wkydbj.js → RRbnMIVe.js} +1 -1
- package/.output/public/_nuxt/{CzqLfT8k.js → TGVM5w2K.js} +1 -1
- package/.output/public/_nuxt/UbZP7wjm.js +1 -0
- package/.output/public/_nuxt/{BW-E-Sar.js → XJYoLoTi.js} +1 -1
- package/.output/public/_nuxt/{CrjKigv8.js → a9_LvIZ4.js} +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/613faa5d-8ace-45e0-8274-b611cc4fd1ad.json +1 -0
- package/.output/public/_nuxt/entry.DkvuF_CR.css +1 -0
- package/.output/public/_nuxt/{CwJR22-0.js → fVkHtgGF.js} +1 -1
- package/.output/public/_nuxt/{sdvIKLwS.js → hm3ShyaF.js} +1 -1
- package/.output/public/_nuxt/{CqiOV9n1.js → k16puyWi.js} +1 -1
- package/.output/public/_nuxt/{B8Kve-DH.js → myNwwtfj.js} +1 -1
- package/.output/public/_nuxt/{DxR8g9So.js → nTCVPCKg.js} +1 -1
- package/.output/public/_nuxt/{DytaiikM.js → pHQcHl8n.js} +1 -1
- package/.output/public/_nuxt/{Cebw7Q7c.js → pNqWPbXW.js} +1 -1
- package/.output/public/_nuxt/{Cow1TrPj.js → qWWnsfRV.js} +1 -1
- package/.output/public/_nuxt/{DCco_rlV.js → rRYEvKFu.js} +1 -1
- package/.output/public/_nuxt/{hqpqUuXS.js → svWfwR0T.js} +1 -1
- package/.output/public/_nuxt/{BMLwzSDu.js → wgX1ldgZ.js} +1 -1
- package/.output/public/_nuxt/{CEfkp9Rl.js → xrh9hzzH.js} +2 -2
- package/.output/server/chunks/build/client.precomputed.mjs +1 -1
- package/.output/server/chunks/build/server.mjs +1 -1
- package/.output/server/chunks/build/{settings-DuT6LGJZ.mjs → settings-B2KXoGcz.mjs} +1679 -203
- package/.output/server/chunks/build/settings-B2KXoGcz.mjs.map +1 -0
- package/.output/server/chunks/build/styles.mjs +4 -4
- package/.output/server/chunks/nitro/nitro.mjs +922 -866
- package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
- package/.output/server/package.json +1 -1
- package/app/pages/settings.vue +581 -6
- package/package.json +1 -1
- package/server/api/skills/import.post.ts +1 -1
- package/server/bridge/adapters/telegram.ts +84 -9
- package/.output/public/_nuxt/B-YQztaH.js +0 -1
- package/.output/public/_nuxt/B1gFtqXK.js +0 -1
- package/.output/public/_nuxt/BTfZ7nek.js +0 -1
- package/.output/public/_nuxt/BbPnqQJ0.js +0 -1
- package/.output/public/_nuxt/BhSWIRpp.js +0 -1
- package/.output/public/_nuxt/By6kmkhJ.js +0 -1
- package/.output/public/_nuxt/C4XVJnNJ.js +0 -1
- package/.output/public/_nuxt/CkvyAKbu.js +0 -1
- package/.output/public/_nuxt/D2f7BlK2.js +0 -1
- package/.output/public/_nuxt/D2lmhTjQ.js +0 -1
- package/.output/public/_nuxt/D64-rCSQ.js +0 -1
- package/.output/public/_nuxt/DGP2JJt8.js +0 -1
- package/.output/public/_nuxt/builds/meta/9863fe43-d78c-4b29-a1e8-e6de61119507.json +0 -1
- package/.output/public/_nuxt/entry._7ZkP07A.css +0 -1
- package/.output/public/_nuxt/f7zDjk6q.js +0 -1
- package/.output/server/chunks/build/settings-DuT6LGJZ.mjs.map +0 -1
package/app/pages/settings.vue
CHANGED
|
@@ -195,6 +195,50 @@ function formatDate(dateStr: string) {
|
|
|
195
195
|
})
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// === App URL ===
|
|
199
|
+
const appUrl = ref('')
|
|
200
|
+
const appUrlSaving = ref(false)
|
|
201
|
+
|
|
202
|
+
async function loadAppUrl() {
|
|
203
|
+
try {
|
|
204
|
+
const { data } = await $fetch<{ data: { key: string, value: string } }>('/api/secrets/APP_URL')
|
|
205
|
+
appUrl.value = data.value || ''
|
|
206
|
+
} catch {
|
|
207
|
+
appUrl.value = ''
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function saveAppUrl() {
|
|
212
|
+
appUrlSaving.value = true
|
|
213
|
+
try {
|
|
214
|
+
if (appUrl.value) {
|
|
215
|
+
// Upsert the APP_URL secret
|
|
216
|
+
const exists = secretsData.value.some(s => s.key === 'APP_URL')
|
|
217
|
+
if (exists) {
|
|
218
|
+
await $fetch('/api/secrets/APP_URL', {
|
|
219
|
+
method: 'PUT',
|
|
220
|
+
body: { value: appUrl.value, description: 'Public URL for webhooks and auth callbacks' }
|
|
221
|
+
})
|
|
222
|
+
} else {
|
|
223
|
+
await $fetch('/api/secrets', {
|
|
224
|
+
method: 'POST',
|
|
225
|
+
body: { key: 'APP_URL', value: appUrl.value, description: 'Public URL for webhooks and auth callbacks' }
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
// Remove the secret if cleared
|
|
230
|
+
const exists = secretsData.value.some(s => s.key === 'APP_URL')
|
|
231
|
+
if (exists)
|
|
232
|
+
await $fetch('/api/secrets/APP_URL', { method: 'DELETE' })
|
|
233
|
+
}
|
|
234
|
+
toast.add({ title: 'Public URL saved', description: 'Restart running integrations for changes to take effect.', color: 'success' })
|
|
235
|
+
await fetchSecrets()
|
|
236
|
+
} catch {
|
|
237
|
+
toast.add({ title: 'Failed to save URL', color: 'error' })
|
|
238
|
+
}
|
|
239
|
+
appUrlSaving.value = false
|
|
240
|
+
}
|
|
241
|
+
|
|
198
242
|
// === Notification Preferences ===
|
|
199
243
|
const notifPrefs = ref<NotificationPreferences>({ ...defaultNotificationPreferences })
|
|
200
244
|
const notifLoading = ref(false)
|
|
@@ -272,6 +316,71 @@ const bridgeForm = reactive({
|
|
|
272
316
|
name: ''
|
|
273
317
|
})
|
|
274
318
|
|
|
319
|
+
// Bridge config modal state
|
|
320
|
+
const bridgeConfigModal = ref(false)
|
|
321
|
+
const editingBridge = ref<Bridge | null>(null)
|
|
322
|
+
const bridgeConfigSaving = ref(false)
|
|
323
|
+
|
|
324
|
+
const bridgeConfigForm = reactive({
|
|
325
|
+
name: '',
|
|
326
|
+
// Telegram
|
|
327
|
+
botUsername: '',
|
|
328
|
+
allowedChatIds: '',
|
|
329
|
+
// Discord
|
|
330
|
+
listenMode: 'mentions' as 'mentions' | 'dm' | 'all',
|
|
331
|
+
guildId: '',
|
|
332
|
+
channelId: '',
|
|
333
|
+
// iMessage
|
|
334
|
+
strategy: 'imsg' as 'imsg' | 'bluebubbles',
|
|
335
|
+
allowedNumbers: '',
|
|
336
|
+
blueBubblesUrl: '',
|
|
337
|
+
// Google
|
|
338
|
+
enabledServices: [] as string[],
|
|
339
|
+
account: '',
|
|
340
|
+
// Email
|
|
341
|
+
imapHost: '',
|
|
342
|
+
imapPort: '',
|
|
343
|
+
smtpHost: '',
|
|
344
|
+
smtpPort: '',
|
|
345
|
+
emailAddress: ''
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
const discordListenModeOptions = [
|
|
349
|
+
{ value: 'mentions', label: 'Mentions & DMs' },
|
|
350
|
+
{ value: 'dm', label: 'DMs only' },
|
|
351
|
+
{ value: 'all', label: 'All messages' }
|
|
352
|
+
]
|
|
353
|
+
|
|
354
|
+
const imessageStrategyOptions = [
|
|
355
|
+
{ value: 'imsg', label: 'Local (imsg CLI)' },
|
|
356
|
+
{ value: 'bluebubbles', label: 'BlueBubbles' }
|
|
357
|
+
]
|
|
358
|
+
|
|
359
|
+
const googleServiceOptions = [
|
|
360
|
+
{ value: 'gmail', label: 'Gmail' },
|
|
361
|
+
{ value: 'calendar', label: 'Calendar' },
|
|
362
|
+
{ value: 'drive', label: 'Drive' },
|
|
363
|
+
{ value: 'contacts', label: 'Contacts' },
|
|
364
|
+
{ value: 'tasks', label: 'Tasks' }
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
const platformNamePlaceholders: Record<BridgePlatform, string> = {
|
|
368
|
+
telegram: 'My Telegram Bot',
|
|
369
|
+
discord: 'My Discord Bot',
|
|
370
|
+
imessage: 'iMessage Bridge',
|
|
371
|
+
google: 'Google Suite',
|
|
372
|
+
email: 'Work Email'
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Secrets required to create a bridge (blocks creation if missing)
|
|
376
|
+
const platformRequiredSecrets: Record<BridgePlatform, string[]> = {
|
|
377
|
+
telegram: ['TELEGRAM_BOT_TOKEN'],
|
|
378
|
+
discord: ['DISCORD_BOT_TOKEN'],
|
|
379
|
+
imessage: [],
|
|
380
|
+
google: [],
|
|
381
|
+
email: []
|
|
382
|
+
}
|
|
383
|
+
|
|
275
384
|
const platformOptions: { value: BridgePlatform, label: string, icon: string }[] = [
|
|
276
385
|
{ value: 'telegram', label: 'Telegram', icon: 'i-lucide-send' },
|
|
277
386
|
{ value: 'discord', label: 'Discord', icon: 'i-lucide-message-circle' },
|
|
@@ -280,6 +389,13 @@ const platformOptions: { value: BridgePlatform, label: string, icon: string }[]
|
|
|
280
389
|
{ value: 'email', label: 'Email (IMAP)', icon: 'i-lucide-at-sign' }
|
|
281
390
|
]
|
|
282
391
|
|
|
392
|
+
function getMissingSecrets(platform: BridgePlatform): string[] {
|
|
393
|
+
const required = platformRequiredSecrets[platform]
|
|
394
|
+
if (!required.length) return []
|
|
395
|
+
const existing = new Set(secretsData.value.map(s => s.key))
|
|
396
|
+
return required.filter(k => !existing.has(k))
|
|
397
|
+
}
|
|
398
|
+
|
|
283
399
|
const healthColors: Record<string, string> = {
|
|
284
400
|
connected: 'text-success',
|
|
285
401
|
disconnected: 'text-dimmed',
|
|
@@ -298,6 +414,119 @@ async function fetchBridges() {
|
|
|
298
414
|
bridgesLoading.value = false
|
|
299
415
|
}
|
|
300
416
|
|
|
417
|
+
function openBridgeConfig(bridge: Bridge) {
|
|
418
|
+
editingBridge.value = bridge
|
|
419
|
+
bridgeConfigForm.name = bridge.name
|
|
420
|
+
|
|
421
|
+
const config = bridge.config ? JSON.parse(bridge.config) : {}
|
|
422
|
+
|
|
423
|
+
// Reset all fields
|
|
424
|
+
bridgeConfigForm.botUsername = ''
|
|
425
|
+
bridgeConfigForm.allowedChatIds = ''
|
|
426
|
+
bridgeConfigForm.listenMode = 'mentions'
|
|
427
|
+
bridgeConfigForm.guildId = ''
|
|
428
|
+
bridgeConfigForm.channelId = ''
|
|
429
|
+
bridgeConfigForm.strategy = 'imsg'
|
|
430
|
+
bridgeConfigForm.allowedNumbers = ''
|
|
431
|
+
bridgeConfigForm.blueBubblesUrl = ''
|
|
432
|
+
bridgeConfigForm.enabledServices = []
|
|
433
|
+
bridgeConfigForm.account = ''
|
|
434
|
+
bridgeConfigForm.imapHost = ''
|
|
435
|
+
bridgeConfigForm.imapPort = ''
|
|
436
|
+
bridgeConfigForm.smtpHost = ''
|
|
437
|
+
bridgeConfigForm.smtpPort = ''
|
|
438
|
+
bridgeConfigForm.emailAddress = ''
|
|
439
|
+
|
|
440
|
+
// Populate platform-specific fields from config
|
|
441
|
+
switch (bridge.platform) {
|
|
442
|
+
case 'telegram':
|
|
443
|
+
bridgeConfigForm.botUsername = config.botUsername || ''
|
|
444
|
+
bridgeConfigForm.allowedChatIds = (config.allowedChatIds || []).join(', ')
|
|
445
|
+
break
|
|
446
|
+
case 'discord':
|
|
447
|
+
bridgeConfigForm.listenMode = config.listenMode || 'mentions'
|
|
448
|
+
bridgeConfigForm.guildId = config.guildId || ''
|
|
449
|
+
bridgeConfigForm.channelId = config.channelId || ''
|
|
450
|
+
break
|
|
451
|
+
case 'imessage':
|
|
452
|
+
bridgeConfigForm.strategy = config.strategy || 'imsg'
|
|
453
|
+
bridgeConfigForm.allowedNumbers = (config.allowedNumbers || []).join(', ')
|
|
454
|
+
bridgeConfigForm.blueBubblesUrl = config.blueBubblesUrl || ''
|
|
455
|
+
break
|
|
456
|
+
case 'google':
|
|
457
|
+
bridgeConfigForm.enabledServices = config.enabledServices || []
|
|
458
|
+
bridgeConfigForm.account = config.account || ''
|
|
459
|
+
break
|
|
460
|
+
case 'email':
|
|
461
|
+
bridgeConfigForm.imapHost = config.imapHost || ''
|
|
462
|
+
bridgeConfigForm.imapPort = config.imapPort?.toString() || ''
|
|
463
|
+
bridgeConfigForm.smtpHost = config.smtpHost || ''
|
|
464
|
+
bridgeConfigForm.smtpPort = config.smtpPort?.toString() || ''
|
|
465
|
+
bridgeConfigForm.emailAddress = config.emailAddress || ''
|
|
466
|
+
break
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
bridgeConfigModal.value = true
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function handleBridgeConfigSave() {
|
|
473
|
+
if (!editingBridge.value) return
|
|
474
|
+
|
|
475
|
+
const platform = editingBridge.value.platform
|
|
476
|
+
let config: Record<string, unknown> = {}
|
|
477
|
+
|
|
478
|
+
// Preserve existing config (like webhookSecret) and merge new fields
|
|
479
|
+
if (editingBridge.value.config)
|
|
480
|
+
config = JSON.parse(editingBridge.value.config)
|
|
481
|
+
|
|
482
|
+
switch (platform) {
|
|
483
|
+
case 'telegram':
|
|
484
|
+
config.botUsername = bridgeConfigForm.botUsername || undefined
|
|
485
|
+
config.allowedChatIds = bridgeConfigForm.allowedChatIds
|
|
486
|
+
? bridgeConfigForm.allowedChatIds.split(',').map(s => s.trim()).filter(Boolean)
|
|
487
|
+
: undefined
|
|
488
|
+
break
|
|
489
|
+
case 'discord':
|
|
490
|
+
config.listenMode = bridgeConfigForm.listenMode
|
|
491
|
+
config.guildId = bridgeConfigForm.guildId || undefined
|
|
492
|
+
config.channelId = bridgeConfigForm.channelId || undefined
|
|
493
|
+
break
|
|
494
|
+
case 'imessage':
|
|
495
|
+
config.strategy = bridgeConfigForm.strategy
|
|
496
|
+
config.allowedNumbers = bridgeConfigForm.allowedNumbers
|
|
497
|
+
? bridgeConfigForm.allowedNumbers.split(',').map(s => s.trim()).filter(Boolean)
|
|
498
|
+
: undefined
|
|
499
|
+
config.blueBubblesUrl = bridgeConfigForm.blueBubblesUrl || undefined
|
|
500
|
+
break
|
|
501
|
+
case 'google':
|
|
502
|
+
config.enabledServices = bridgeConfigForm.enabledServices
|
|
503
|
+
config.account = bridgeConfigForm.account || undefined
|
|
504
|
+
break
|
|
505
|
+
case 'email':
|
|
506
|
+
config.imapHost = bridgeConfigForm.imapHost || undefined
|
|
507
|
+
config.imapPort = bridgeConfigForm.imapPort ? Number(bridgeConfigForm.imapPort) : undefined
|
|
508
|
+
config.smtpHost = bridgeConfigForm.smtpHost || undefined
|
|
509
|
+
config.smtpPort = bridgeConfigForm.smtpPort ? Number(bridgeConfigForm.smtpPort) : undefined
|
|
510
|
+
config.emailAddress = bridgeConfigForm.emailAddress || undefined
|
|
511
|
+
break
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
bridgeConfigSaving.value = true
|
|
515
|
+
try {
|
|
516
|
+
await $fetch(`/api/bridges/${editingBridge.value.id}` as string, {
|
|
517
|
+
method: 'PUT',
|
|
518
|
+
body: { name: bridgeConfigForm.name, config }
|
|
519
|
+
})
|
|
520
|
+
toast.add({ title: 'Integration updated', color: 'success' })
|
|
521
|
+
bridgeConfigModal.value = false
|
|
522
|
+
await fetchBridges()
|
|
523
|
+
} catch (err: unknown) {
|
|
524
|
+
const error = err as { data?: { message?: string } }
|
|
525
|
+
toast.add({ title: 'Failed to update integration', description: error.data?.message, color: 'error' })
|
|
526
|
+
}
|
|
527
|
+
bridgeConfigSaving.value = false
|
|
528
|
+
}
|
|
529
|
+
|
|
301
530
|
function openCreateBridge() {
|
|
302
531
|
bridgeForm.platform = 'telegram'
|
|
303
532
|
bridgeForm.name = ''
|
|
@@ -309,6 +538,15 @@ async function handleBridgeCreate() {
|
|
|
309
538
|
toast.add({ title: 'Name is required', color: 'error' })
|
|
310
539
|
return
|
|
311
540
|
}
|
|
541
|
+
const missing = getMissingSecrets(bridgeForm.platform)
|
|
542
|
+
if (missing.length) {
|
|
543
|
+
toast.add({
|
|
544
|
+
title: 'Missing required secrets',
|
|
545
|
+
description: `Add ${missing.join(', ')} in the Secrets tab first.`,
|
|
546
|
+
color: 'error'
|
|
547
|
+
})
|
|
548
|
+
return
|
|
549
|
+
}
|
|
312
550
|
bridgeSaving.value = true
|
|
313
551
|
try {
|
|
314
552
|
await $fetch('/api/bridges', {
|
|
@@ -327,7 +565,7 @@ async function handleBridgeCreate() {
|
|
|
327
565
|
|
|
328
566
|
async function toggleBridge(bridge: Bridge) {
|
|
329
567
|
try {
|
|
330
|
-
await $fetch(`/api/bridges/${bridge.id}
|
|
568
|
+
await $fetch(`/api/bridges/${bridge.id}` as string, {
|
|
331
569
|
method: 'PUT',
|
|
332
570
|
body: { enabled: !bridge.enabled }
|
|
333
571
|
})
|
|
@@ -345,7 +583,7 @@ function confirmDeleteBridge(bridge: Bridge) {
|
|
|
345
583
|
async function handleDeleteBridge() {
|
|
346
584
|
if (!bridgeToDelete.value) return
|
|
347
585
|
try {
|
|
348
|
-
await $fetch(`/api/bridges/${bridgeToDelete.value.id}
|
|
586
|
+
await $fetch(`/api/bridges/${bridgeToDelete.value.id}` as string, { method: 'DELETE' })
|
|
349
587
|
toast.add({ title: 'Integration deleted', color: 'success' })
|
|
350
588
|
bridgeDeleteConfirm.value = false
|
|
351
589
|
bridgeToDelete.value = null
|
|
@@ -355,11 +593,12 @@ async function handleDeleteBridge() {
|
|
|
355
593
|
}
|
|
356
594
|
}
|
|
357
595
|
|
|
358
|
-
// Load secrets + notification prefs + bridges when component mounts
|
|
596
|
+
// Load secrets + notification prefs + bridges + app url when component mounts
|
|
359
597
|
onMounted(() => {
|
|
360
598
|
fetchSecrets()
|
|
361
599
|
loadNotificationPrefs()
|
|
362
600
|
fetchBridges()
|
|
601
|
+
loadAppUrl()
|
|
363
602
|
})
|
|
364
603
|
|
|
365
604
|
// Form handlers
|
|
@@ -717,7 +956,8 @@ async function handlePasswordSubmit() {
|
|
|
717
956
|
<div
|
|
718
957
|
v-for="bridge in bridgesData"
|
|
719
958
|
:key="bridge.id"
|
|
720
|
-
class="border border-default rounded-lg px-4 py-3"
|
|
959
|
+
class="border border-default rounded-lg px-4 py-3 cursor-pointer hover:bg-elevated/50 transition-colors"
|
|
960
|
+
@click="openBridgeConfig(bridge)"
|
|
721
961
|
>
|
|
722
962
|
<div class="flex items-center justify-between">
|
|
723
963
|
<div class="flex items-center gap-3">
|
|
@@ -739,6 +979,7 @@ async function handlePasswordSubmit() {
|
|
|
739
979
|
</span>
|
|
740
980
|
<USwitch
|
|
741
981
|
:model-value="bridge.enabled"
|
|
982
|
+
@click.stop
|
|
742
983
|
@update:model-value="toggleBridge(bridge)"
|
|
743
984
|
/>
|
|
744
985
|
<UButton
|
|
@@ -746,7 +987,7 @@ async function handlePasswordSubmit() {
|
|
|
746
987
|
color="error"
|
|
747
988
|
icon="i-lucide-trash-2"
|
|
748
989
|
size="xs"
|
|
749
|
-
@click="confirmDeleteBridge(bridge)"
|
|
990
|
+
@click.stop="confirmDeleteBridge(bridge)"
|
|
750
991
|
/>
|
|
751
992
|
</div>
|
|
752
993
|
</div>
|
|
@@ -764,6 +1005,35 @@ async function handlePasswordSubmit() {
|
|
|
764
1005
|
<!-- App Tab -->
|
|
765
1006
|
<template #app>
|
|
766
1007
|
<div class="max-w-2xl mx-auto py-6">
|
|
1008
|
+
<!-- Public URL -->
|
|
1009
|
+
<div class="mb-8">
|
|
1010
|
+
<h3 class="text-lg font-semibold mb-1">
|
|
1011
|
+
Public URL
|
|
1012
|
+
</h3>
|
|
1013
|
+
<p class="text-sm text-dimmed mb-4">
|
|
1014
|
+
The publicly accessible URL for this instance. Used for Telegram webhooks, auth callbacks, and other integrations that need to reach your server.
|
|
1015
|
+
</p>
|
|
1016
|
+
<div class="flex gap-2">
|
|
1017
|
+
<UInput
|
|
1018
|
+
v-model="appUrl"
|
|
1019
|
+
placeholder="https://example.com"
|
|
1020
|
+
class="flex-1"
|
|
1021
|
+
/>
|
|
1022
|
+
<UButton
|
|
1023
|
+
:loading="appUrlSaving"
|
|
1024
|
+
@click="saveAppUrl"
|
|
1025
|
+
>
|
|
1026
|
+
Save
|
|
1027
|
+
</UButton>
|
|
1028
|
+
</div>
|
|
1029
|
+
<p class="text-xs text-dimmed mt-2">
|
|
1030
|
+
Leave empty to use long-polling for Telegram instead of webhooks. Changes require restarting running integrations.
|
|
1031
|
+
</p>
|
|
1032
|
+
</div>
|
|
1033
|
+
|
|
1034
|
+
<USeparator class="mb-8" />
|
|
1035
|
+
|
|
1036
|
+
<!-- Notification Preferences -->
|
|
767
1037
|
<div class="mb-6">
|
|
768
1038
|
<h3 class="text-lg font-semibold mb-1">
|
|
769
1039
|
Notification Preferences
|
|
@@ -974,13 +1244,41 @@ async function handlePasswordSubmit() {
|
|
|
974
1244
|
class="w-full"
|
|
975
1245
|
/>
|
|
976
1246
|
</UFormField>
|
|
1247
|
+
|
|
1248
|
+
<!-- Missing secrets warning -->
|
|
1249
|
+
<div
|
|
1250
|
+
v-if="getMissingSecrets(bridgeForm.platform).length"
|
|
1251
|
+
class="flex items-start gap-2 rounded-lg bg-error/10 border border-error/20 p-3 text-sm"
|
|
1252
|
+
>
|
|
1253
|
+
<UIcon
|
|
1254
|
+
name="i-lucide-alert-triangle"
|
|
1255
|
+
class="size-4 mt-0.5 text-error shrink-0"
|
|
1256
|
+
/>
|
|
1257
|
+
<div>
|
|
1258
|
+
<p class="font-medium text-error">
|
|
1259
|
+
Missing required secrets
|
|
1260
|
+
</p>
|
|
1261
|
+
<p class="text-dimmed mt-1">
|
|
1262
|
+
Add the following in Settings → Secrets before creating:
|
|
1263
|
+
</p>
|
|
1264
|
+
<ul class="mt-1 space-y-0.5">
|
|
1265
|
+
<li
|
|
1266
|
+
v-for="key in getMissingSecrets(bridgeForm.platform)"
|
|
1267
|
+
:key="key"
|
|
1268
|
+
>
|
|
1269
|
+
<code class="text-xs bg-elevated px-1.5 py-0.5 rounded">{{ key }}</code>
|
|
1270
|
+
</li>
|
|
1271
|
+
</ul>
|
|
1272
|
+
</div>
|
|
1273
|
+
</div>
|
|
1274
|
+
|
|
977
1275
|
<UFormField
|
|
978
1276
|
label="Name"
|
|
979
1277
|
name="name"
|
|
980
1278
|
>
|
|
981
1279
|
<UInput
|
|
982
1280
|
v-model="bridgeForm.name"
|
|
983
|
-
placeholder="
|
|
1281
|
+
:placeholder="platformNamePlaceholders[bridgeForm.platform]"
|
|
984
1282
|
class="w-full"
|
|
985
1283
|
/>
|
|
986
1284
|
</UFormField>
|
|
@@ -993,6 +1291,7 @@ async function handlePasswordSubmit() {
|
|
|
993
1291
|
</UButton>
|
|
994
1292
|
<UButton
|
|
995
1293
|
:loading="bridgeSaving"
|
|
1294
|
+
:disabled="getMissingSecrets(bridgeForm.platform).length > 0"
|
|
996
1295
|
@click="handleBridgeCreate"
|
|
997
1296
|
>
|
|
998
1297
|
Create
|
|
@@ -1032,5 +1331,281 @@ async function handlePasswordSubmit() {
|
|
|
1032
1331
|
</div>
|
|
1033
1332
|
</template>
|
|
1034
1333
|
</UModal>
|
|
1334
|
+
|
|
1335
|
+
<!-- Bridge Config Modal -->
|
|
1336
|
+
<UModal v-model:open="bridgeConfigModal">
|
|
1337
|
+
<template #header>
|
|
1338
|
+
<div class="flex items-center gap-2">
|
|
1339
|
+
<UIcon
|
|
1340
|
+
:name="platformOptions.find(p => p.value === editingBridge?.platform)?.icon || 'i-lucide-plug'"
|
|
1341
|
+
class="size-5"
|
|
1342
|
+
/>
|
|
1343
|
+
<h3 class="text-lg font-semibold">
|
|
1344
|
+
Configure {{ editingBridge?.name }}
|
|
1345
|
+
</h3>
|
|
1346
|
+
</div>
|
|
1347
|
+
</template>
|
|
1348
|
+
<template #body>
|
|
1349
|
+
<div class="space-y-4">
|
|
1350
|
+
<!-- Secret hint -->
|
|
1351
|
+
<div
|
|
1352
|
+
v-if="editingBridge && platformRequiredSecrets[editingBridge.platform].length"
|
|
1353
|
+
class="flex items-start gap-2 rounded-lg bg-elevated p-3 text-sm"
|
|
1354
|
+
>
|
|
1355
|
+
<UIcon
|
|
1356
|
+
name="i-lucide-info"
|
|
1357
|
+
class="size-4 mt-0.5 text-dimmed shrink-0"
|
|
1358
|
+
/>
|
|
1359
|
+
<span class="text-dimmed">
|
|
1360
|
+
Requires
|
|
1361
|
+
<template
|
|
1362
|
+
v-for="(key, i) in platformRequiredSecrets[editingBridge.platform]"
|
|
1363
|
+
:key="key"
|
|
1364
|
+
>
|
|
1365
|
+
<code class="bg-elevated px-1 py-0.5 rounded text-xs">{{ key }}</code><template v-if="i < platformRequiredSecrets[editingBridge.platform].length - 1">, </template>
|
|
1366
|
+
</template>
|
|
1367
|
+
in the Secrets tab.
|
|
1368
|
+
</span>
|
|
1369
|
+
</div>
|
|
1370
|
+
|
|
1371
|
+
<!-- Common: Name -->
|
|
1372
|
+
<UFormField
|
|
1373
|
+
label="Name"
|
|
1374
|
+
name="name"
|
|
1375
|
+
>
|
|
1376
|
+
<UInput
|
|
1377
|
+
v-model="bridgeConfigForm.name"
|
|
1378
|
+
placeholder="Integration name"
|
|
1379
|
+
class="w-full"
|
|
1380
|
+
/>
|
|
1381
|
+
</UFormField>
|
|
1382
|
+
|
|
1383
|
+
<!-- Telegram -->
|
|
1384
|
+
<template v-if="editingBridge?.platform === 'telegram'">
|
|
1385
|
+
<UFormField
|
|
1386
|
+
label="Bot Username"
|
|
1387
|
+
name="botUsername"
|
|
1388
|
+
hint="Without the @ prefix"
|
|
1389
|
+
>
|
|
1390
|
+
<UInput
|
|
1391
|
+
v-model="bridgeConfigForm.botUsername"
|
|
1392
|
+
placeholder="my_bot"
|
|
1393
|
+
class="w-full"
|
|
1394
|
+
/>
|
|
1395
|
+
</UFormField>
|
|
1396
|
+
<UFormField
|
|
1397
|
+
label="Allowed Chat IDs"
|
|
1398
|
+
name="allowedChatIds"
|
|
1399
|
+
hint="Comma-separated. Leave empty for all."
|
|
1400
|
+
>
|
|
1401
|
+
<UInput
|
|
1402
|
+
v-model="bridgeConfigForm.allowedChatIds"
|
|
1403
|
+
placeholder="123456, -100789"
|
|
1404
|
+
class="w-full"
|
|
1405
|
+
/>
|
|
1406
|
+
</UFormField>
|
|
1407
|
+
</template>
|
|
1408
|
+
|
|
1409
|
+
<!-- Discord -->
|
|
1410
|
+
<template v-if="editingBridge?.platform === 'discord'">
|
|
1411
|
+
<UFormField
|
|
1412
|
+
label="Listen Mode"
|
|
1413
|
+
name="listenMode"
|
|
1414
|
+
>
|
|
1415
|
+
<USelect
|
|
1416
|
+
v-model="bridgeConfigForm.listenMode"
|
|
1417
|
+
:items="discordListenModeOptions"
|
|
1418
|
+
value-key="value"
|
|
1419
|
+
class="w-full"
|
|
1420
|
+
/>
|
|
1421
|
+
</UFormField>
|
|
1422
|
+
<UFormField
|
|
1423
|
+
label="Server ID"
|
|
1424
|
+
name="guildId"
|
|
1425
|
+
hint="Optional. Limit to one server."
|
|
1426
|
+
>
|
|
1427
|
+
<UInput
|
|
1428
|
+
v-model="bridgeConfigForm.guildId"
|
|
1429
|
+
placeholder="Discord server ID"
|
|
1430
|
+
class="w-full"
|
|
1431
|
+
/>
|
|
1432
|
+
</UFormField>
|
|
1433
|
+
<UFormField
|
|
1434
|
+
label="Channel ID"
|
|
1435
|
+
name="channelId"
|
|
1436
|
+
hint="Optional. Limit to one channel (for 'all' mode)."
|
|
1437
|
+
>
|
|
1438
|
+
<UInput
|
|
1439
|
+
v-model="bridgeConfigForm.channelId"
|
|
1440
|
+
placeholder="Discord channel ID"
|
|
1441
|
+
class="w-full"
|
|
1442
|
+
/>
|
|
1443
|
+
</UFormField>
|
|
1444
|
+
</template>
|
|
1445
|
+
|
|
1446
|
+
<!-- iMessage -->
|
|
1447
|
+
<template v-if="editingBridge?.platform === 'imessage'">
|
|
1448
|
+
<UFormField
|
|
1449
|
+
label="Strategy"
|
|
1450
|
+
name="strategy"
|
|
1451
|
+
>
|
|
1452
|
+
<USelect
|
|
1453
|
+
v-model="bridgeConfigForm.strategy"
|
|
1454
|
+
:items="imessageStrategyOptions"
|
|
1455
|
+
value-key="value"
|
|
1456
|
+
class="w-full"
|
|
1457
|
+
/>
|
|
1458
|
+
</UFormField>
|
|
1459
|
+
<div
|
|
1460
|
+
v-if="bridgeConfigForm.strategy === 'bluebubbles' && !secretsData.some(s => s.key === 'BLUEBUBBLES_PASSWORD')"
|
|
1461
|
+
class="flex items-start gap-2 rounded-lg bg-error/10 border border-error/20 p-3 text-sm"
|
|
1462
|
+
>
|
|
1463
|
+
<UIcon
|
|
1464
|
+
name="i-lucide-alert-triangle"
|
|
1465
|
+
class="size-4 mt-0.5 text-error shrink-0"
|
|
1466
|
+
/>
|
|
1467
|
+
<span class="text-dimmed">
|
|
1468
|
+
BlueBubbles requires secret <code class="text-xs bg-elevated px-1.5 py-0.5 rounded">BLUEBUBBLES_PASSWORD</code> in the Secrets tab.
|
|
1469
|
+
</span>
|
|
1470
|
+
</div>
|
|
1471
|
+
<UFormField
|
|
1472
|
+
v-if="bridgeConfigForm.strategy === 'bluebubbles'"
|
|
1473
|
+
label="BlueBubbles URL"
|
|
1474
|
+
name="blueBubblesUrl"
|
|
1475
|
+
>
|
|
1476
|
+
<UInput
|
|
1477
|
+
v-model="bridgeConfigForm.blueBubblesUrl"
|
|
1478
|
+
placeholder="http://192.168.1.100:1234"
|
|
1479
|
+
class="w-full"
|
|
1480
|
+
/>
|
|
1481
|
+
</UFormField>
|
|
1482
|
+
<UFormField
|
|
1483
|
+
label="Allowed Numbers"
|
|
1484
|
+
name="allowedNumbers"
|
|
1485
|
+
hint="Comma-separated. Leave empty for all."
|
|
1486
|
+
>
|
|
1487
|
+
<UInput
|
|
1488
|
+
v-model="bridgeConfigForm.allowedNumbers"
|
|
1489
|
+
placeholder="+15551234567, +15559876543"
|
|
1490
|
+
class="w-full"
|
|
1491
|
+
/>
|
|
1492
|
+
</UFormField>
|
|
1493
|
+
</template>
|
|
1494
|
+
|
|
1495
|
+
<!-- Google Suite -->
|
|
1496
|
+
<template v-if="editingBridge?.platform === 'google'">
|
|
1497
|
+
<UFormField
|
|
1498
|
+
label="Enabled Services"
|
|
1499
|
+
name="enabledServices"
|
|
1500
|
+
>
|
|
1501
|
+
<div class="space-y-2">
|
|
1502
|
+
<label
|
|
1503
|
+
v-for="svc in googleServiceOptions"
|
|
1504
|
+
:key="svc.value"
|
|
1505
|
+
class="flex items-center gap-2 cursor-pointer"
|
|
1506
|
+
>
|
|
1507
|
+
<input
|
|
1508
|
+
type="checkbox"
|
|
1509
|
+
:checked="bridgeConfigForm.enabledServices.includes(svc.value)"
|
|
1510
|
+
class="rounded"
|
|
1511
|
+
@change="
|
|
1512
|
+
bridgeConfigForm.enabledServices.includes(svc.value)
|
|
1513
|
+
? bridgeConfigForm.enabledServices = bridgeConfigForm.enabledServices.filter(s => s !== svc.value)
|
|
1514
|
+
: bridgeConfigForm.enabledServices.push(svc.value)
|
|
1515
|
+
"
|
|
1516
|
+
>
|
|
1517
|
+
<span class="text-sm">{{ svc.label }}</span>
|
|
1518
|
+
</label>
|
|
1519
|
+
</div>
|
|
1520
|
+
</UFormField>
|
|
1521
|
+
<UFormField
|
|
1522
|
+
label="Google Account"
|
|
1523
|
+
name="account"
|
|
1524
|
+
hint="Email used with gogcli"
|
|
1525
|
+
>
|
|
1526
|
+
<UInput
|
|
1527
|
+
v-model="bridgeConfigForm.account"
|
|
1528
|
+
placeholder="user@gmail.com"
|
|
1529
|
+
class="w-full"
|
|
1530
|
+
/>
|
|
1531
|
+
</UFormField>
|
|
1532
|
+
</template>
|
|
1533
|
+
|
|
1534
|
+
<!-- Email (IMAP/SMTP) -->
|
|
1535
|
+
<template v-if="editingBridge?.platform === 'email'">
|
|
1536
|
+
<UFormField
|
|
1537
|
+
label="Email Address"
|
|
1538
|
+
name="emailAddress"
|
|
1539
|
+
>
|
|
1540
|
+
<UInput
|
|
1541
|
+
v-model="bridgeConfigForm.emailAddress"
|
|
1542
|
+
placeholder="user@example.com"
|
|
1543
|
+
class="w-full"
|
|
1544
|
+
/>
|
|
1545
|
+
</UFormField>
|
|
1546
|
+
<div class="grid grid-cols-2 gap-4">
|
|
1547
|
+
<UFormField
|
|
1548
|
+
label="IMAP Host"
|
|
1549
|
+
name="imapHost"
|
|
1550
|
+
>
|
|
1551
|
+
<UInput
|
|
1552
|
+
v-model="bridgeConfigForm.imapHost"
|
|
1553
|
+
placeholder="imap.example.com"
|
|
1554
|
+
class="w-full"
|
|
1555
|
+
/>
|
|
1556
|
+
</UFormField>
|
|
1557
|
+
<UFormField
|
|
1558
|
+
label="IMAP Port"
|
|
1559
|
+
name="imapPort"
|
|
1560
|
+
>
|
|
1561
|
+
<UInput
|
|
1562
|
+
v-model="bridgeConfigForm.imapPort"
|
|
1563
|
+
placeholder="993"
|
|
1564
|
+
class="w-full"
|
|
1565
|
+
/>
|
|
1566
|
+
</UFormField>
|
|
1567
|
+
</div>
|
|
1568
|
+
<div class="grid grid-cols-2 gap-4">
|
|
1569
|
+
<UFormField
|
|
1570
|
+
label="SMTP Host"
|
|
1571
|
+
name="smtpHost"
|
|
1572
|
+
>
|
|
1573
|
+
<UInput
|
|
1574
|
+
v-model="bridgeConfigForm.smtpHost"
|
|
1575
|
+
placeholder="smtp.example.com"
|
|
1576
|
+
class="w-full"
|
|
1577
|
+
/>
|
|
1578
|
+
</UFormField>
|
|
1579
|
+
<UFormField
|
|
1580
|
+
label="SMTP Port"
|
|
1581
|
+
name="smtpPort"
|
|
1582
|
+
>
|
|
1583
|
+
<UInput
|
|
1584
|
+
v-model="bridgeConfigForm.smtpPort"
|
|
1585
|
+
placeholder="587"
|
|
1586
|
+
class="w-full"
|
|
1587
|
+
/>
|
|
1588
|
+
</UFormField>
|
|
1589
|
+
</div>
|
|
1590
|
+
</template>
|
|
1591
|
+
|
|
1592
|
+
<!-- Save / Cancel -->
|
|
1593
|
+
<div class="flex justify-end gap-2 pt-4">
|
|
1594
|
+
<UButton
|
|
1595
|
+
variant="ghost"
|
|
1596
|
+
@click="bridgeConfigModal = false"
|
|
1597
|
+
>
|
|
1598
|
+
Cancel
|
|
1599
|
+
</UButton>
|
|
1600
|
+
<UButton
|
|
1601
|
+
:loading="bridgeConfigSaving"
|
|
1602
|
+
@click="handleBridgeConfigSave"
|
|
1603
|
+
>
|
|
1604
|
+
Save
|
|
1605
|
+
</UButton>
|
|
1606
|
+
</div>
|
|
1607
|
+
</div>
|
|
1608
|
+
</template>
|
|
1609
|
+
</UModal>
|
|
1035
1610
|
</UDashboardPanel>
|
|
1036
1611
|
</template>
|