89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
// lib/supabase.ts
|
|
import { createClient } from '@supabase/supabase-js';
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
import * as SecureStore from 'expo-secure-store';
|
|
import * as aesjs from 'aes-js';
|
|
import 'react-native-get-random-values';
|
|
import { Platform } from 'react-native';
|
|
|
|
// Create a web-compatible storage implementation
|
|
class WebStorage {
|
|
async getItem(key: string) {
|
|
// In SSR context, return null
|
|
if (typeof window === 'undefined') {
|
|
return null;
|
|
}
|
|
return localStorage.getItem(key);
|
|
}
|
|
|
|
async removeItem(key: string) {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
localStorage.removeItem(key);
|
|
}
|
|
|
|
async setItem(key: string, value: string) {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
localStorage.setItem(key, value);
|
|
}
|
|
}
|
|
|
|
// Original LargeSecureStore implementation for native platforms
|
|
class LargeSecureStore {
|
|
private async _encrypt(key: string, value: string) {
|
|
const encryptionKey = crypto.getRandomValues(new Uint8Array(256 / 8));
|
|
const cipher = new aesjs.ModeOfOperation.ctr(encryptionKey, new aesjs.Counter(1));
|
|
const encryptedBytes = cipher.encrypt(aesjs.utils.utf8.toBytes(value));
|
|
await SecureStore.setItemAsync(key, aesjs.utils.hex.fromBytes(encryptionKey));
|
|
return aesjs.utils.hex.fromBytes(encryptedBytes);
|
|
}
|
|
|
|
private async _decrypt(key: string, value: string) {
|
|
const encryptionKeyHex = await SecureStore.getItemAsync(key);
|
|
if (!encryptionKeyHex) {
|
|
return encryptionKeyHex;
|
|
}
|
|
const cipher = new aesjs.ModeOfOperation.ctr(
|
|
aesjs.utils.hex.toBytes(encryptionKeyHex),
|
|
new aesjs.Counter(1),
|
|
);
|
|
const decryptedBytes = cipher.decrypt(aesjs.utils.hex.toBytes(value));
|
|
return aesjs.utils.utf8.fromBytes(decryptedBytes);
|
|
}
|
|
|
|
async getItem(key: string) {
|
|
const encrypted = await AsyncStorage.getItem(key);
|
|
if (!encrypted) {
|
|
return encrypted;
|
|
}
|
|
return await this._decrypt(key, encrypted);
|
|
}
|
|
|
|
async removeItem(key: string) {
|
|
await AsyncStorage.removeItem(key);
|
|
await SecureStore.deleteItemAsync(key);
|
|
}
|
|
|
|
async setItem(key: string, value: string) {
|
|
const encrypted = await this._encrypt(key, value);
|
|
await AsyncStorage.setItem(key, encrypted);
|
|
}
|
|
}
|
|
|
|
// Choose the appropriate storage implementation based on platform
|
|
const storage = Platform.OS === 'web' ? new WebStorage() : new LargeSecureStore();
|
|
const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_URL as string;
|
|
const supabaseAnonKey = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY as string;
|
|
|
|
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
auth: {
|
|
storage,
|
|
autoRefreshToken: true,
|
|
persistSession: true,
|
|
detectSessionInUrl: false,
|
|
},
|
|
});
|