1+ import { createHash } from 'crypto'
2+
13import { HTTP_STATUS , apiError , apiResponse } from '@/packages/lib/api/response'
24import { requireAdmin } from '@/packages/lib/auth/api-auth'
35import { loggers } from '@/packages/lib/logger'
@@ -13,6 +15,8 @@ type IntegrationKey =
1315 | 'github'
1416 | 'smtp'
1517 | 'vultr'
18+ | 'linode'
19+ | 'ovhcloud'
1620
1721interface TestIntegrationBody {
1822 integration : IntegrationKey
@@ -328,6 +332,90 @@ async function testVultr(apiKey: string): Promise<TestResult> {
328332 }
329333}
330334
335+ async function testLinode ( apiKey : string ) : Promise < TestResult > {
336+ if ( ! apiKey ) return { ok : false , message : 'API key is not configured' }
337+ try {
338+ const res = await fetch (
339+ 'https://api.linode.com/v4/object-storage/clusters' ,
340+ {
341+ headers : { Authorization : `Bearer ${ apiKey } ` } ,
342+ signal : AbortSignal . timeout ( 8000 ) ,
343+ }
344+ )
345+ if ( res . status === 401 || res . status === 400 )
346+ return { ok : false , message : 'Invalid API key' }
347+ if ( ! res . ok )
348+ return { ok : false , message : `Linode API error (${ res . status } )` }
349+ const json = await res . json ( ) . catch ( ( ) => null )
350+ const count = json ?. data ?. length ?? 0
351+ return {
352+ ok : true ,
353+ message : `Connected to Linode — ${ count } cluster${ count !== 1 ? 's' : '' } available` ,
354+ }
355+ } catch ( err ) {
356+ return {
357+ ok : false ,
358+ message : 'Failed to reach Linode API' ,
359+ detail : String ( err ) ,
360+ }
361+ }
362+ }
363+
364+ const OVH_ENDPOINTS : Record < string , string > = {
365+ 'ovh-eu' : 'https://eu.api.ovh.com/v2' ,
366+ 'ovh-us' : 'https://api.us.ovhcloud.com/v2' ,
367+ 'ovh-ca' : 'https://ca.api.ovh.com/v2' ,
368+ }
369+
370+ async function testOVHCloud (
371+ appKey : string ,
372+ appSecret : string ,
373+ consumerKey : string ,
374+ endpoint : string
375+ ) : Promise < TestResult > {
376+ if ( ! appKey || ! appSecret || ! consumerKey )
377+ return { ok : false , message : 'All four OVH credentials are required' }
378+ const baseUrl = OVH_ENDPOINTS [ endpoint ] ?? OVH_ENDPOINTS [ 'ovh-eu' ]
379+ try {
380+ const url = `${ baseUrl } /cloud/project`
381+ const timestamp = Math . round ( Date . now ( ) / 1000 )
382+ const toSign = [
383+ appSecret ,
384+ consumerKey ,
385+ 'GET' ,
386+ url ,
387+ '' ,
388+ String ( timestamp ) ,
389+ ] . join ( '+' )
390+ const signature = '$1$' + createHash ( 'sha1' ) . update ( toSign ) . digest ( 'hex' )
391+ const res = await fetch ( url , {
392+ headers : {
393+ 'X-Ovh-Application' : appKey ,
394+ 'X-Ovh-Consumer' : consumerKey ,
395+ 'X-Ovh-Timestamp' : String ( timestamp ) ,
396+ 'X-Ovh-Signature' : signature ,
397+ } ,
398+ signal : AbortSignal . timeout ( 8000 ) ,
399+ } )
400+ if ( res . status === 401 || res . status === 403 )
401+ return { ok : false , message : 'Invalid OVHcloud credentials' }
402+ if ( ! res . ok )
403+ return { ok : false , message : `OVHcloud API error (${ res . status } )` }
404+ const json = await res . json ( ) . catch ( ( ) => null )
405+ const count = Array . isArray ( json ) ? json . length : 0
406+ return {
407+ ok : true ,
408+ message : `Connected to OVHcloud — ${ count } project${ count !== 1 ? 's' : '' } accessible` ,
409+ }
410+ } catch ( err ) {
411+ return {
412+ ok : false ,
413+ message : 'Failed to reach OVHcloud API' ,
414+ detail : String ( err ) ,
415+ }
416+ }
417+ }
418+
331419async function testSmtp (
332420 host : string ,
333421 port : number ,
@@ -402,6 +490,17 @@ export async function POST(req: Request) {
402490 case 'vultr' :
403491 result = await testVultr ( credentials . apiKey )
404492 break
493+ case 'linode' :
494+ result = await testLinode ( credentials . apiKey )
495+ break
496+ case 'ovhcloud' :
497+ result = await testOVHCloud (
498+ credentials . appKey ,
499+ credentials . appSecret ,
500+ credentials . consumerKey ,
501+ credentials . endpoint ?? 'ovh-eu'
502+ )
503+ break
405504 default :
406505 return apiError ( 'Unknown integration' , HTTP_STATUS . BAD_REQUEST )
407506 }
0 commit comments