11import { NextRequest , NextResponse } from "next/server" ;
22import { getSupabaseClient } from "@/lib/auth/supabase-client" ;
33import { decodeJWT } from "@/lib/jwt-utils" ;
4+ import { decryptSecret , encryptSecret } from "@/lib/crypto" ;
5+
6+ function encryptApiKeys (
7+ apiKeys : Record < string , string > ,
8+ ) : Record < string , string > {
9+ const encryptionKey = process . env . SECRETS_ENCRYPTION_KEY ;
10+ if ( ! encryptionKey ) {
11+ throw new Error ( "Encryption key not found" ) ;
12+ }
13+
14+ const encryptedApiKeys = Object . fromEntries (
15+ Object . entries ( apiKeys ) . map ( ( [ key , value ] ) => {
16+ return [ key , encryptSecret ( value , encryptionKey ) ] ;
17+ } ) ,
18+ ) ;
19+ return encryptedApiKeys ;
20+ }
21+
22+ function decryptApiKeys (
23+ apiKeys : Record < string , string > ,
24+ ) : Record < string , string > {
25+ const encryptionKey = process . env . SECRETS_ENCRYPTION_KEY ;
26+ if ( ! encryptionKey ) {
27+ throw new Error ( "Encryption key not found" ) ;
28+ }
29+
30+ const decryptedApiKeys = Object . fromEntries (
31+ Object . entries ( apiKeys ) . map ( ( [ key , value ] ) => {
32+ return [ key , decryptSecret ( value , encryptionKey ) ] ;
33+ } ) ,
34+ ) ;
35+ return decryptedApiKeys ;
36+ }
37+
38+ function isTokenExpired ( exp : number ) : boolean {
39+ const currentTime = Date . now ( ) / 1000 ;
40+ return currentTime > exp ;
41+ }
442
543export async function POST ( request : NextRequest ) {
644 try {
@@ -18,7 +56,7 @@ export async function POST(request: NextRequest) {
1856 }
1957
2058 const payload = decodeJWT ( accessToken , jwtSecret ) ;
21- if ( ! payload || ! payload . sub ) {
59+ if ( ! payload || ! payload . sub || isTokenExpired ( payload . exp ) ) {
2260 return NextResponse . json (
2361 { error : "Invalid or expired token" } ,
2462 { status : 401 } ,
@@ -39,10 +77,11 @@ export async function POST(request: NextRequest) {
3977
4078 // Filter out null, undefined, or empty string values
4179 const nonNullApiKeys = Object . fromEntries (
42- Object . entries ( apiKeys ) . filter ( ( [ _ , value ] ) => {
80+ Object . entries < string > ( apiKeys ) . filter ( ( [ _ , value ] ) => {
4381 return value && typeof value === "string" && value . trim ( ) !== "" ;
4482 } ) ,
4583 ) ;
84+ const encryptedApiKeys = encryptApiKeys ( nonNullApiKeys ) ;
4685
4786 await supabase . auth . setSession ( {
4887 access_token : accessToken ,
@@ -52,7 +91,7 @@ export async function POST(request: NextRequest) {
5291 const { error : upsertError } = await supabase . from ( "users_config" ) . upsert (
5392 {
5493 user_id : userId ,
55- api_keys : nonNullApiKeys ,
94+ api_keys : encryptedApiKeys ,
5695 } as any ,
5796 {
5897 onConflict : "user_id" ,
@@ -82,3 +121,75 @@ export async function POST(request: NextRequest) {
82121 ) ;
83122 }
84123}
124+
125+ export async function GET ( request : NextRequest ) {
126+ try {
127+ const supabase = getSupabaseClient ( ) ;
128+
129+ const accessToken = request . headers . get ( "x-access-token" ) ;
130+ const refreshToken = request . headers . get ( "x-refresh-token" ) ;
131+ const jwtSecret = process . env . SUPABASE_JWT_SECRET ;
132+
133+ if ( ! accessToken || ! refreshToken || ! jwtSecret ) {
134+ return NextResponse . json (
135+ { error : "Authentication required" } ,
136+ { status : 401 } ,
137+ ) ;
138+ }
139+
140+ const payload = decodeJWT ( accessToken , jwtSecret ) ;
141+ if ( ! payload || ! payload . sub || isTokenExpired ( payload . exp ) ) {
142+ return NextResponse . json (
143+ { error : "Invalid or expired token" } ,
144+ { status : 401 } ,
145+ ) ;
146+ }
147+
148+ const userId = payload . sub ;
149+
150+ await supabase . auth . setSession ( {
151+ access_token : accessToken ,
152+ refresh_token : refreshToken ,
153+ } ) ;
154+
155+ const { data, error } = await supabase
156+ . from ( "users_config" )
157+ . select ( "api_keys" )
158+ . eq ( "user_id" , userId )
159+ . single ( ) ;
160+
161+ if ( error && error . code === "PGRST116" ) {
162+ return NextResponse . json ( { error : "No API keys found" } , { status : 404 } ) ;
163+ }
164+
165+ if ( ! data || error ) {
166+ return NextResponse . json (
167+ { error : "Failed to fetch API keys" } ,
168+ { status : 500 } ,
169+ ) ;
170+ }
171+
172+ if ( ! ( "api_keys" in data ) ) {
173+ return NextResponse . json (
174+ { error : "API keys not found" } ,
175+ { status : 404 } ,
176+ ) ;
177+ }
178+
179+ const encryptedApiKeys = ( data as Record < string , any > ) . api_keys ;
180+ const decryptedApiKeys = decryptApiKeys ( encryptedApiKeys ) ;
181+
182+ return NextResponse . json (
183+ {
184+ apiKeys : decryptedApiKeys ,
185+ } ,
186+ { status : 200 } ,
187+ ) ;
188+ } catch ( error ) {
189+ console . error ( "API keys save error:" , error ) ;
190+ return NextResponse . json (
191+ { error : "Internal server error" } ,
192+ { status : 500 } ,
193+ ) ;
194+ }
195+ }
0 commit comments