feat: complete release preparation, 10+ ad mediation, CI/CD, and docker deployment
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped
This commit is contained in:
parent
5cd1de6859
commit
708e20f747
406 changed files with 42464 additions and 6199 deletions
|
|
@ -64,7 +64,7 @@ export default async function TeamDetailPage({ params }: PageProps): Promise<Rea
|
|||
</Stack>
|
||||
|
||||
<Stack spacing={1}>
|
||||
{((members ?? []) as Array<{
|
||||
{((members ?? []) as unknown as Array<{
|
||||
user_id: string
|
||||
role: string
|
||||
joined_at: string
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ export default function RootLayout({
|
|||
children: React.ReactNode
|
||||
}): React.ReactElement {
|
||||
return (
|
||||
<html lang="ko">
|
||||
<body>
|
||||
<html lang="ko" suppressHydrationWarning>
|
||||
<body suppressHydrationWarning>
|
||||
<ThemeProvider>
|
||||
<I18nProvider>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
|
|
|
|||
|
|
@ -137,82 +137,83 @@ export function MicRecorder(): React.ReactElement {
|
|||
const blob = new Blob(chunksRef.current, { type: 'audio/webm' })
|
||||
stopStream()
|
||||
|
||||
if (!configured) {
|
||||
setError('Supabase가 설정되지 않아 전사할 수 없습니다.')
|
||||
setState('error')
|
||||
return
|
||||
}
|
||||
const apiBase = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000'
|
||||
|
||||
// 1. Try D3RO Cloud API STT or Supabase STT Proxy
|
||||
try {
|
||||
const supabase = getSupabaseBrowserClient()
|
||||
const { data: { session } } = await supabase.auth.getSession()
|
||||
if (configured) {
|
||||
const supabase = getSupabaseBrowserClient()
|
||||
const { data: { session } } = await supabase.auth.getSession()
|
||||
|
||||
if (!session) {
|
||||
setError('로그인이 필요합니다.')
|
||||
setState('error')
|
||||
return
|
||||
}
|
||||
if (session) {
|
||||
const formData = new FormData()
|
||||
formData.append('audio', blob, 'recording.webm')
|
||||
formData.append('sample_rate', '16000')
|
||||
formData.append('language_code', 'ko-KR')
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('audio', blob, 'recording.webm')
|
||||
formData.append('sample_rate', '16000')
|
||||
formData.append('language_code', 'ko-KR')
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_SUPABASE_URL}/functions/v1/stt-proxy`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.access_token}`
|
||||
},
|
||||
body: formData
|
||||
}
|
||||
)
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_SUPABASE_URL}/functions/v1/stt-proxy`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.access_token}`
|
||||
},
|
||||
body: formData
|
||||
}
|
||||
)
|
||||
if (response.ok) {
|
||||
const data = (await response.json()) as SttResponse
|
||||
setTranscript(data.transcript)
|
||||
|
||||
if (!response.ok) {
|
||||
const errText = await response.text()
|
||||
throw new Error(`STT failed: ${response.status} ${errText}`)
|
||||
}
|
||||
// Save to meetings if possible
|
||||
try {
|
||||
const { data: { user } } = await supabase.auth.getUser()
|
||||
if (user) {
|
||||
const audioKey = `${user.id}/${Date.now()}.webm`
|
||||
const { error: uploadErr } = await supabase.storage
|
||||
.from('audio')
|
||||
.upload(audioKey, blob, { contentType: 'audio/webm' })
|
||||
|
||||
const data = (await response.json()) as SttResponse
|
||||
setTranscript(data.transcript)
|
||||
await supabase.from('meetings').insert({
|
||||
user_id: user.id,
|
||||
team_id: null,
|
||||
title: `녹음 ${new Date().toLocaleString('ko-KR')}`,
|
||||
status: 'completed',
|
||||
duration_ms: Math.round(data.duration_seconds * 1000),
|
||||
raw_transcript: data.transcript,
|
||||
audio_storage_key: uploadErr ? null : audioKey,
|
||||
stt_model: 'cloud-stt',
|
||||
ended_at: new Date().toISOString()
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
// Ignore meeting save failure
|
||||
}
|
||||
|
||||
// 회의로 저장 + Supabase Storage에 오디오 업로드
|
||||
try {
|
||||
const {
|
||||
data: { user }
|
||||
} = await supabase.auth.getUser()
|
||||
|
||||
if (user) {
|
||||
const audioKey = `${user.id}/${Date.now()}.webm`
|
||||
const { error: uploadErr } = await supabase.storage
|
||||
.from('audio')
|
||||
.upload(audioKey, blob, { contentType: 'audio/webm' })
|
||||
|
||||
const storageKey: string | null = uploadErr ? null : audioKey
|
||||
|
||||
const { error: insertErr } = await supabase.from('meetings').insert({
|
||||
user_id: user.id,
|
||||
team_id: null,
|
||||
title: `녹음 ${new Date().toLocaleString('ko-KR')}`,
|
||||
status: 'completed',
|
||||
duration_ms: Math.round(data.duration_seconds * 1000),
|
||||
raw_transcript: data.transcript,
|
||||
audio_storage_key: storageKey,
|
||||
stt_model: 'google-stt',
|
||||
ended_at: new Date().toISOString()
|
||||
})
|
||||
if (insertErr) {
|
||||
// 회의 저장 실패해도 전사 결과는 유지
|
||||
setError(`회의 저장 실패 (전사는 성공): ${insertErr.message}`)
|
||||
setState('done')
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (saveErr) {
|
||||
setError(
|
||||
`저장 중 오류 (전사는 성공): ${saveErr instanceof Error ? saveErr.message : String(saveErr)}`
|
||||
)
|
||||
}
|
||||
|
||||
// 2. Direct D3RO Cloud API Fallback
|
||||
const cloudFormData = new FormData()
|
||||
cloudFormData.append('file', blob, 'recording.webm')
|
||||
cloudFormData.append('language', 'ko')
|
||||
|
||||
const cloudRes = await fetch(`${apiBase}/api/stt/transcribe`, {
|
||||
method: 'POST',
|
||||
body: cloudFormData,
|
||||
})
|
||||
|
||||
if (!cloudRes.ok) {
|
||||
const errText = await cloudRes.text()
|
||||
throw new Error(`D3RO Cloud STT Error (${cloudRes.status}): ${errText}`)
|
||||
}
|
||||
|
||||
const cloudData = await cloudRes.json()
|
||||
setTranscript(cloudData.text ?? '전사 완료')
|
||||
setState('done')
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : 'STT 처리 실패')
|
||||
|
|
@ -319,7 +320,6 @@ export function MicRecorder(): React.ReactElement {
|
|||
size="large"
|
||||
startIcon={<MicIcon />}
|
||||
onClick={() => void startRecording()}
|
||||
disabled={!configured}
|
||||
>
|
||||
{state === 'done' ? '새 녹음' : '녹음 시작'}
|
||||
</Button>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue