feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
|
|
@ -2,8 +2,7 @@
|
|||
// Phase 3.2: 모델별 쿼터 + 주간/일간 기간 분리
|
||||
// Free=Haiku 250/주간, Pro=모델별 일간, Pro+=Haiku 무제한
|
||||
|
||||
// @ts-expect-error — Deno 런타임 import
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.39.7'
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
export type Tier = 'free' | 'pro' | 'pro_plus' | 'team' | 'enterprise'
|
||||
|
||||
|
|
@ -93,6 +92,76 @@ export interface QuotaConsumeResult {
|
|||
consumedFrom: 'base' | 'overage' | 'unlimited' | 'none'
|
||||
}
|
||||
|
||||
export interface SttQuotaReservation {
|
||||
allowed: boolean
|
||||
reservationId: string | null
|
||||
status: 'reserved' | 'completed' | 'released' | 'denied'
|
||||
current: number
|
||||
limit: number
|
||||
period: QuotaPeriod
|
||||
tier: Tier
|
||||
overageCredits: number
|
||||
consumedFrom: 'base' | 'overage' | 'unlimited' | 'none'
|
||||
}
|
||||
|
||||
export async function reserveSttQuota(
|
||||
userId: string,
|
||||
reservationId: string,
|
||||
serviceRoleClient: ReturnType<typeof createClient>,
|
||||
): Promise<SttQuotaReservation> {
|
||||
const { data, error } = await serviceRoleClient.rpc('reserve_stt_quota', {
|
||||
p_user_id: userId,
|
||||
p_reservation_id: reservationId,
|
||||
})
|
||||
if (error || !data || typeof data !== 'object' || Array.isArray(data)) {
|
||||
throw new Error('Failed to reserve STT quota.')
|
||||
}
|
||||
const result = data as Record<string, unknown>
|
||||
if (
|
||||
typeof result.allowed !== 'boolean'
|
||||
|| (result.reservation_id !== null && typeof result.reservation_id !== 'string')
|
||||
|| !['reserved', 'completed', 'released', 'denied'].includes(String(result.status))
|
||||
|| typeof result.current !== 'number'
|
||||
|| typeof result.limit !== 'number'
|
||||
|| !['daily', 'weekly'].includes(String(result.period))
|
||||
|| !['free', 'pro', 'pro_plus', 'team', 'enterprise'].includes(String(result.tier))
|
||||
|| typeof result.overage_credits !== 'number'
|
||||
|| !['base', 'overage', 'unlimited', 'none'].includes(String(result.consumed_from))
|
||||
) {
|
||||
throw new Error('Invalid STT quota reservation response.')
|
||||
}
|
||||
return {
|
||||
allowed: result.allowed,
|
||||
reservationId: result.reservation_id as string | null,
|
||||
status: result.status as SttQuotaReservation['status'],
|
||||
current: result.current,
|
||||
limit: result.limit,
|
||||
period: result.period as QuotaPeriod,
|
||||
tier: result.tier as Tier,
|
||||
overageCredits: result.overage_credits,
|
||||
consumedFrom: result.consumed_from as SttQuotaReservation['consumedFrom'],
|
||||
}
|
||||
}
|
||||
|
||||
export async function finalizeSttQuota(
|
||||
reservationId: string,
|
||||
succeeded: boolean,
|
||||
serviceRoleClient: ReturnType<typeof createClient>,
|
||||
): Promise<'completed' | 'released'> {
|
||||
const { data, error } = await serviceRoleClient.rpc('finalize_stt_quota', {
|
||||
p_reservation_id: reservationId,
|
||||
p_succeeded: succeeded,
|
||||
})
|
||||
if (error || !data || typeof data !== 'object' || Array.isArray(data)) {
|
||||
throw new Error('Failed to finalize STT quota.')
|
||||
}
|
||||
const status = (data as Record<string, unknown>).status
|
||||
if (status !== 'completed' && status !== 'released') {
|
||||
throw new Error('Invalid STT quota finalization response.')
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
/**
|
||||
* 쿼터 확인 — 모델별, 기간별(daily/weekly).
|
||||
* weekly인 경우 최근 7일 daily_usage를 합산.
|
||||
|
|
@ -135,7 +204,8 @@ export async function checkQuota(
|
|||
.eq('user_id', userId)
|
||||
.eq('feature', feature)
|
||||
.gte('date', weekAgo.toISOString().slice(0, 10))
|
||||
current = rows?.reduce((sum: number, row: { count: number }) => sum + row.count, 0) ?? 0
|
||||
const usageRows = (rows ?? []) as Array<{ count: number | null }>
|
||||
current = usageRows.reduce((sum, row) => sum + (row.count ?? 0), 0)
|
||||
} else {
|
||||
// 오늘만
|
||||
const today = new Date().toISOString().slice(0, 10)
|
||||
|
|
@ -202,9 +272,7 @@ export async function consumeQuota(
|
|||
* service role 클라이언트 생성 헬퍼
|
||||
*/
|
||||
export function createServiceRoleClient(): ReturnType<typeof createClient> {
|
||||
// @ts-expect-error — Deno.env는 Deno 런타임 전역
|
||||
const url = Deno.env.get('SUPABASE_URL') ?? ''
|
||||
// @ts-expect-error — Deno.env는 Deno 런타임 전역
|
||||
const serviceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
|
||||
return createClient(url, serviceKey, {
|
||||
auth: { persistSession: false, autoRefreshToken: false },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue