import { getContainer } from '@bytelyst/cosmos'; import { config } from '../config/index.js'; import logger from '../utils/logger.js'; import type { UserConfig, supabaseService } from './SupabaseService.js'; type LegacySupabaseService = typeof supabaseService; const USER_PROFILE_CONTAINER = 'trading_users'; interface TradingUserDocument extends UserConfig { id: string; productId: string; type: 'trading_user'; } function isCosmosConfigured(): boolean { return Boolean(config.COSMOS_ENDPOINT && config.COSMOS_KEY); } function normalizeUser(row: Partial | null | undefined): UserConfig | null { const userId = String(row?.user_id || '').trim(); if (!userId) { return null; } return { user_id: userId, first_name: String(row?.first_name || ''), last_name: String(row?.last_name || ''), email: String(row?.email || ''), ALPACA_API_KEY: String(row?.ALPACA_API_KEY || ''), ALPACA_SECRET_KEY: String(row?.ALPACA_SECRET_KEY || ''), REAL_ALPACA_API_KEY: String(row?.REAL_ALPACA_API_KEY || ''), REAL_ALPACA_SECRET_KEY: String(row?.REAL_ALPACA_SECRET_KEY || ''), role: String(row?.role || 'member'), trade_enable: Boolean(row?.trade_enable), drop_threshold_for_buy: Number(row?.drop_threshold_for_buy || 0), gain_threshold_for_sell: Number(row?.gain_threshold_for_sell || 0), market_poll_interval_in_seconds: Number(row?.market_poll_interval_in_seconds || 0), }; } export async function listActiveTradingUsers(legacyService?: LegacySupabaseService): Promise { if (isCosmosConfigured()) { try { const container = getContainer(USER_PROFILE_CONTAINER); const { resources } = await container.items.query({ query: 'SELECT * FROM c WHERE c.productId = @productId AND c.type = @type AND c.trade_enable = true', parameters: [ { name: '@productId', value: config.PRODUCT_ID }, { name: '@type', value: 'trading_user' }, ], }).fetchAll(); const normalized = resources .map((row) => normalizeUser(row as UserConfig)) .filter((user): user is UserConfig => Boolean(user)); if (normalized.length > 0) { return normalized; } } catch (error) { logger.warn(`[Users] Cosmos active trading user lookup failed: ${error instanceof Error ? error.message : 'unknown error'}`); return []; } } const client = legacyService?.getClient?.(); if (!client) { return []; } try { const { data, error } = await client .from('users') .select('*') .eq('trade_enable', true); if (error || !Array.isArray(data)) { return []; } const normalized = data .map((row) => normalizeUser(row as UserConfig)) .filter((user): user is UserConfig => Boolean(user)); if (isCosmosConfigured() && normalized.length > 0) { try { const container = getContainer(USER_PROFILE_CONTAINER); await Promise.all(normalized.map((user) => container.items.upsert({ ...user, id: user.user_id, productId: config.PRODUCT_ID, type: 'trading_user', } as TradingUserDocument))); } catch (error) { logger.warn(`[Users] Cosmos user seed failed: ${error instanceof Error ? error.message : 'unknown error'}`); } } return normalized; } catch (error) { logger.warn(`[Users] Active trading user lookup failed: ${error instanceof Error ? error.message : 'unknown error'}`); return []; } }