REFACTOR(repo): simplify project structure
Some checks failed
Build Docker Image / build-and-push (push) Has been cancelled
CI / lint-and-build (push) Has been cancelled

- Move services/nextjs/ to nextjs/
- Move Dockerfile.prod to Dockerfile at root
- Remove deploy/ folder (K8s manifests moved to K3S-HOME/web-apps)
- Remove .gitea/ workflows
- Update GitHub Actions for new structure
- Remove develop branch triggers
This commit is contained in:
2026-01-05 02:03:53 +09:00
parent b459ca9b9d
commit b5bb97aa16
84 changed files with 17 additions and 917 deletions

View File

@@ -0,0 +1,84 @@
import { NextRequest, NextResponse } from 'next/server'
import { corsHeaders, handleCorsPreFlight } from '@/shared/lib/cors'
const PASSWORD = '5364'
const SESSION_COOKIE_NAME = 'todo-auth-session'
// OPTIONS - CORS preflight 처리
export async function OPTIONS() {
return handleCorsPreFlight()
}
// POST - 비밀번호 인증
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { password } = body
if (!password) {
const response = NextResponse.json(
{ success: false, error: '비밀번호를 입력해주세요' },
{ status: 400 }
)
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
if (password === PASSWORD) {
// 인증 성공 - 세션 쿠키 설정
const response = NextResponse.json(
{ success: true, message: '인증 성공' },
{ status: 200 }
)
// HttpOnly 쿠키로 세션 저장 (보안 강화)
response.cookies.set(SESSION_COOKIE_NAME, 'authenticated', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 60 * 60 * 24, // 24시간
path: '/',
})
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
} else {
const response = NextResponse.json(
{ success: false, error: '비밀번호가 일치하지 않습니다' },
{ status: 401 }
)
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
} catch (error) {
console.error('Password auth error:', error)
const response = NextResponse.json(
{ success: false, error: '서버 오류가 발생했습니다' },
{ status: 500 }
)
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
}
// GET - 인증 상태 확인
export async function GET(request: NextRequest) {
const session = request.cookies.get(SESSION_COOKIE_NAME)?.value
const response = NextResponse.json(
{ success: true, authenticated: session === 'authenticated' },
{ status: 200 }
)
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}

View File

@@ -0,0 +1,25 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/shared/lib/prisma'
export async function GET() {
try {
// 데이터베이스 연결 확인
await prisma.$queryRaw`SELECT 1`
return NextResponse.json({
status: 'ok',
timestamp: new Date().toISOString(),
database: 'connected'
})
} catch {
return NextResponse.json(
{
status: 'error',
timestamp: new Date().toISOString(),
error: 'Database connection failed'
},
{ status: 503 }
)
}
}

View File

@@ -0,0 +1,79 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/shared/lib/prisma'
import { corsHeaders, handleCorsPreFlight } from '@/shared/lib/cors'
import type { Prisma } from '@prisma/client'
// OPTIONS - CORS preflight 처리
export async function OPTIONS() {
return handleCorsPreFlight()
}
// PUT - TODO 업데이트
export async function PUT(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id: idParam } = await params
const id = parseInt(idParam)
const body = await request.json()
const updateData: Prisma.TodoUpdateInput = {}
if (body.title !== undefined) updateData.title = body.title
if (body.description !== undefined) updateData.description = body.description
if (body.completed !== undefined) updateData.completed = body.completed
if (body.priority !== undefined) updateData.priority = body.priority
const todo = await prisma.todo.update({
where: { id },
data: updateData,
})
const response = NextResponse.json({ success: true, data: todo })
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
} catch (error) {
console.error('Error updating todo:', error)
const response = NextResponse.json(
{ success: false, error: 'Failed to update todo' },
{ status: 500 }
)
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
}
// DELETE - TODO 삭제
export async function DELETE(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id: idParam } = await params
const id = parseInt(idParam)
await prisma.todo.delete({
where: { id },
})
const response = NextResponse.json({ success: true, message: 'Todo deleted' })
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
} catch (error) {
console.error('Error deleting todo:', error)
const response = NextResponse.json(
{ success: false, error: 'Failed to delete todo' },
{ status: 500 }
)
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
}

View File

@@ -0,0 +1,65 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/shared/lib/prisma'
import { corsHeaders, handleCorsPreFlight } from '@/shared/lib/cors'
// OPTIONS - CORS preflight 처리
export async function OPTIONS() {
return handleCorsPreFlight()
}
// GET - 모든 TODO 가져오기
export async function GET() {
try {
const todos = await prisma.todo.findMany({
orderBy: { createdAt: 'desc' },
})
const response = NextResponse.json({ success: true, data: todos })
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
} catch (error) {
console.error('Error fetching todos:', error)
const response = NextResponse.json(
{ success: false, error: 'Failed to fetch todos' },
{ status: 500 }
)
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
}
// POST - TODO 생성
export async function POST(request: Request) {
try {
const body = await request.json()
const todo = await prisma.todo.create({
data: {
title: body.title,
description: body.description || null,
completed: body.completed || false,
priority: body.priority || 'medium',
},
})
const response = NextResponse.json({ success: true, data: todo }, { status: 201 })
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
} catch (error) {
console.error('Error creating todo:', error)
const response = NextResponse.json(
{ success: false, error: 'Failed to create todo' },
{ status: 500 }
)
Object.entries(corsHeaders()).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
}