Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ JAVA_TOOL_OPTIONS=-Xms256m -Xmx1536m -XX:+UseG1GC -XX:MaxGCPauseMillis=200
DB_URL=jdbc:postgresql://localhost:5432/bridge
DB_USERNAME=bridge
DB_PASSWORD=bridge
MAX_HTTP_REQUEST_HEADER_SIZE=64KB

JWT_SECRET=change-this-dev-secret-change-this-dev-secret
STORAGE_PRESIGN_SECRET=change-this-storage-presign-secret
VAULT_MASTER_KEY=0123456789abcdef0123456789abcdef
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001,http://localhost:3002
AUTH_COOKIE_DOMAIN=

MINIO_ENDPOINT=http://localhost:9000
MINIO_BUCKET=bridge
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/deploy-backend-aws.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,36 @@ concurrency:
cancel-in-progress: false

jobs:
quality:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- name: Lint (placeholder)
run: echo "backend: no lint step configured"

- name: Test backend
shell: bash
run: |
chmod +x backend/gradlew
cd backend
./gradlew test

- name: Build backend
shell: bash
run: |
cd backend
./gradlew build -x test

deploy:
needs: quality
runs-on: ubuntu-latest
permissions:
id-token: write
Expand Down
47 changes: 47 additions & 0 deletions .github/workflows/deploy-web-vercel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,55 @@ concurrency:
cancel-in-progress: false

jobs:
quality:
name: Quality Gates
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint web apps
run: |
pnpm --filter pm-web lint
pnpm --filter client-web lint
pnpm --filter admin-web lint

- name: Test backend
shell: bash
run: |
chmod +x backend/gradlew
cd backend
./gradlew test

- name: Build web apps
run: |
pnpm --filter pm-web build
pnpm --filter client-web build
pnpm --filter admin-web build

deploy:
name: Deploy ${{ matrix.app.name }}
needs: quality
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand Down
26 changes: 20 additions & 6 deletions apps/admin-web/src/app/admin/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use client";
"use client";

import { FormEvent, Suspense, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { API_BASE } from "@/lib/api";
import { sanitizeNextPath, setAuthCookies } from "@/lib/auth";
import { sanitizeNextPath } from "@/lib/auth";

type TenantOption = {
tenantId: string;
Expand All @@ -30,6 +30,14 @@ function AdminLoginForm() {
const [error, setError] = useState<string | null>(null);
const [submitting, setSubmitting] = useState(false);

function resolveLoginErrorMessage(payload: unknown): string {
const code = (payload as { error?: { code?: string } })?.error?.code;
if (code === "LOGIN_BLOCKED") {
return "로그인 시도 횟수를 초과해 계정이 잠겼습니다. 로그인 잠금 해제를 진행하세요.";
}
return "로그인에 실패했습니다.";
}

async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
setSubmitting(true);
Expand All @@ -38,7 +46,11 @@ function AdminLoginForm() {
try {
const response = await fetch(`${API_BASE}/api/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
headers: {
"Content-Type": "application/json",
"X-Bridge-App": "admin",
},
credentials: "include",
body: JSON.stringify({
email,
password,
Expand All @@ -47,7 +59,7 @@ function AdminLoginForm() {
});
const json = await response.json();
if (!response.ok) {
throw new Error(json?.error?.message ?? "로그인에 실패했습니다.");
throw new Error(resolveLoginErrorMessage(json));
}

const data = json?.data;
Expand All @@ -61,11 +73,10 @@ function AdminLoginForm() {
return;
}

if (!data?.accessToken || !data?.refreshToken) {
if (!data || !data.userId) {
throw new Error("로그인 응답이 올바르지 않습니다.");
}

setAuthCookies(data.accessToken, data.refreshToken);
router.replace(sanitizeNextPath(params.get("next"), "/admin/tenants"));
} catch (e) {
setError(e instanceof Error ? e.message : "요청 처리 중 오류가 발생했습니다.");
Expand Down Expand Up @@ -184,3 +195,6 @@ function LoginPageFallback() {
return <main className="min-h-screen bg-background" />;
}




Loading