1
0
forked from baron/baron-sso

Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
2026-05-15 18:21:01 +09:00
45 changed files with 4775 additions and 16080 deletions

View File

@@ -16,3 +16,4 @@
**/*.log **/*.log
**/*.swp **/*.swp
**/.DS_Store **/.DS_Store
**/.pnpm-store

View File

@@ -57,11 +57,6 @@ jobs:
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: "24" node-version: "24"
cache: "npm"
cache-dependency-path: |
adminfront/package-lock.json
devfront/package-lock.json
orgfront/package-lock.json
- name: i18n resource check - name: i18n resource check
run: | run: |
@@ -91,7 +86,8 @@ jobs:
- name: Install adminfront dependencies - name: Install adminfront dependencies
run: | run: |
cd adminfront cd adminfront
npm ci npm install -g pnpm
pnpm install -C ../common --no-frozen-lockfile
- name: Biome check adminfront (lint + format) - name: Biome check adminfront (lint + format)
run: | run: |
@@ -102,7 +98,8 @@ jobs:
- name: Install devfront dependencies - name: Install devfront dependencies
run: | run: |
cd devfront cd devfront
npm ci npm install -g pnpm
pnpm install -C ../common --no-frozen-lockfile
- name: Biome check devfront (lint + format) - name: Biome check devfront (lint + format)
run: | run: |
@@ -113,7 +110,8 @@ jobs:
- name: Install orgfront dependencies - name: Install orgfront dependencies
run: | run: |
cd orgfront cd orgfront
npm ci npm install -g pnpm
pnpm install -C ../common --no-frozen-lockfile
- name: Biome check orgfront (lint + format) - name: Biome check orgfront (lint + format)
run: | run: |
@@ -337,7 +335,7 @@ jobs:
id: playwright-version id: playwright-version
run: | run: |
cd userfront-e2e cd userfront-e2e
echo "version=$(npm list @playwright/test | grep @playwright/test | awk -F@ '{print $NF}')" >> "$GITHUB_OUTPUT" echo "version=$(npm list @playwright/test | grep @playwright/test | awk -F@ '{print $NF}' | head -n 1)" >> "$GITHUB_OUTPUT"
- name: Cache Playwright Browsers - name: Cache Playwright Browsers
uses: actions/cache@v4 uses: actions/cache@v4
@@ -561,14 +559,12 @@ jobs:
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: "24" node-version: "24"
cache: "npm"
cache-dependency-path: adminfront/package-lock.json
- name: Get Playwright version - name: Get Playwright version
id: playwright-version id: playwright-version
run: | run: |
cd adminfront cd adminfront
echo "version=$(npm list @playwright/test | grep @playwright/test | awk -F@ '{print $NF}')" >> "$GITHUB_OUTPUT" echo "version=$(pnpm list -C ../common @playwright/test --depth 0 | grep @playwright/test | awk -F@ '{print $NF}' | head -n 1)" >> "$GITHUB_OUTPUT"
- name: Cache Playwright Browsers - name: Cache Playwright Browsers
uses: actions/cache@v4 uses: actions/cache@v4
@@ -656,14 +652,12 @@ jobs:
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: "24" node-version: "24"
cache: "npm"
cache-dependency-path: devfront/package-lock.json
- name: Get Playwright version - name: Get Playwright version
id: playwright-version id: playwright-version
run: | run: |
cd devfront cd devfront
echo "version=$(npm list @playwright/test | grep @playwright/test | awk -F@ '{print $NF}')" >> "$GITHUB_OUTPUT" echo "version=$(pnpm list -C ../common @playwright/test --depth 0 | grep @playwright/test | awk -F@ '{print $NF}' | head -n 1)" >> "$GITHUB_OUTPUT"
- name: Cache Playwright Browsers - name: Cache Playwright Browsers
uses: actions/cache@v4 uses: actions/cache@v4
@@ -679,7 +673,8 @@ jobs:
mkdir -p reports mkdir -p reports
set +e set +e
cd devfront cd devfront
npm ci 2>&1 | tee ../reports/devfront-install.log npm install -g pnpm
pnpm install -C ../common --no-frozen-lockfile 2>&1 | tee ../reports/devfront-install.log
install_exit_code=${PIPESTATUS[0]} install_exit_code=${PIPESTATUS[0]}
cd .. cd ..
set -e set -e
@@ -708,7 +703,7 @@ jobs:
run: | run: |
set +e set +e
cd devfront cd devfront
npx playwright install --with-deps 2>&1 | tee ../reports/devfront-provision.log pnpm exec playwright install --with-deps 2>&1 | tee ../reports/devfront-provision.log
provision_exit_code=${PIPESTATUS[0]} provision_exit_code=${PIPESTATUS[0]}
cd .. cd ..
set -e set -e
@@ -723,7 +718,7 @@ jobs:
echo "- Exit Code: \`$provision_exit_code\`" echo "- Exit Code: \`$provision_exit_code\`"
echo echo
echo "## Command" echo "## Command"
echo "\`cd devfront && npx playwright install --with-deps\`" echo "\`cd devfront && pnpm exec playwright install --with-deps\`"
echo echo
echo "## Provision Log Tail (last 200 lines)" echo "## Provision Log Tail (last 200 lines)"
echo '```text' echo '```text'
@@ -740,7 +735,7 @@ jobs:
mkdir -p reports mkdir -p reports
set +e set +e
cd devfront cd devfront
npm test 2>&1 | tee ../reports/devfront-test.log pnpm run test 2>&1 | tee ../reports/devfront-test.log
test_exit_code=${PIPESTATUS[0]} test_exit_code=${PIPESTATUS[0]}
cd .. cd ..
set -e set -e
@@ -756,8 +751,8 @@ jobs:
echo "## Commands" echo "## Commands"
echo "1. \`cd devfront\`" echo "1. \`cd devfront\`"
echo "2. \`npm ci\`" echo "2. \`npm ci\`"
echo "3. \`npx playwright install --with-deps\`" echo "3. \`pnpm exec playwright install --with-deps\`"
echo "4. \`npm test\`" echo "4. \`pnpm run test\`"
echo echo
echo "## Log Tail (last 200 lines)" echo "## Log Tail (last 200 lines)"
echo '```text' echo '```text'
@@ -839,14 +834,12 @@ jobs:
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: "24" node-version: "24"
cache: "npm"
cache-dependency-path: orgfront/package-lock.json
- name: Get Playwright version - name: Get Playwright version
id: playwright-version id: playwright-version
run: | run: |
cd orgfront cd orgfront
echo "version=$(npm list @playwright/test | grep @playwright/test | awk -F@ '{print $NF}')" >> "$GITHUB_OUTPUT" echo "version=$(pnpm list -C ../common @playwright/test --depth 0 | grep @playwright/test | awk -F@ '{print $NF}' | head -n 1)" >> "$GITHUB_OUTPUT"
- name: Cache Playwright Browsers - name: Cache Playwright Browsers
uses: actions/cache@v4 uses: actions/cache@v4
@@ -862,7 +855,8 @@ jobs:
mkdir -p reports mkdir -p reports
set +e set +e
cd orgfront cd orgfront
npm ci 2>&1 | tee ../reports/orgfront-install.log npm install -g pnpm
pnpm install -C ../common --no-frozen-lockfile 2>&1 | tee ../reports/orgfront-install.log
install_exit_code=${PIPESTATUS[0]} install_exit_code=${PIPESTATUS[0]}
cd .. cd ..
set -e set -e
@@ -891,7 +885,7 @@ jobs:
run: | run: |
set +e set +e
cd orgfront cd orgfront
npx playwright install --with-deps 2>&1 | tee ../reports/orgfront-provision.log pnpm exec playwright install --with-deps 2>&1 | tee ../reports/orgfront-provision.log
provision_exit_code=${PIPESTATUS[0]} provision_exit_code=${PIPESTATUS[0]}
cd .. cd ..
set -e set -e
@@ -906,7 +900,7 @@ jobs:
echo "- Exit Code: \`$provision_exit_code\`" echo "- Exit Code: \`$provision_exit_code\`"
echo echo
echo "## Command" echo "## Command"
echo "\`cd orgfront && npx playwright install --with-deps\`" echo "\`cd orgfront && pnpm exec playwright install --with-deps\`"
echo echo
echo "## Provision Log Tail (last 200 lines)" echo "## Provision Log Tail (last 200 lines)"
echo '```text' echo '```text'
@@ -923,7 +917,7 @@ jobs:
mkdir -p reports mkdir -p reports
set +e set +e
cd orgfront cd orgfront
npm test 2>&1 | tee ../reports/orgfront-test.log pnpm run test 2>&1 | tee ../reports/orgfront-test.log
test_exit_code=${PIPESTATUS[0]} test_exit_code=${PIPESTATUS[0]}
cd .. cd ..
set -e set -e
@@ -939,8 +933,8 @@ jobs:
echo "## Commands" echo "## Commands"
echo "1. \`cd orgfront\`" echo "1. \`cd orgfront\`"
echo "2. \`npm ci\`" echo "2. \`npm ci\`"
echo "3. \`npx playwright install --with-deps\`" echo "3. \`pnpm exec playwright install --with-deps\`"
echo "4. \`npm test\`" echo "4. \`pnpm run test\`"
echo echo
echo "## Log Tail (last 200 lines)" echo "## Log Tail (last 200 lines)"
echo '```text' echo '```text'

2
.gitignore vendored
View File

@@ -18,6 +18,7 @@ config/.generated/
reports reports
reports/* reports/*
config/*.pem config/*.pem
common/node_modules
# Docker Services Data (Volumes) # Docker Services Data (Volumes)
postgres_data/ postgres_data/
@@ -51,3 +52,4 @@ orgfront/playwright-report/
orgfront/node_modules/ orgfront/node_modules/
orgfront/dist/ orgfront/dist/
orgfront/.vite/ orgfront/.vite/
.pnpm-store

View File

@@ -1,16 +1,21 @@
FROM node:lts FROM node:lts
WORKDIR /app WORKDIR /workspace
# 패키지 정보 복사 및 의존성 설치 # Install pnpm
COPY package*.json ./ RUN npm install -g pnpm
RUN npm ci
# Copy workspace configs and common package
COPY common ./common
COPY adminfront ./adminfront
# Install dependencies for the workspace
RUN cd common && pnpm install --no-frozen-lockfile --ignore-scripts
# 프로덕션 서빙을 위한 serve 패키지 글로벌 설치 # 프로덕션 서빙을 위한 serve 패키지 글로벌 설치
RUN npm install -g serve RUN npm install -g serve
# 소스 코드 복사 WORKDIR /workspace/adminfront
COPY . .
# Vite 기본 포트 # Vite 기본 포트
EXPOSE 5173 EXPOSE 5173

View File

@@ -1,32 +1,3 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "extends": ["../common/config/biome.base.json"]
"formatter": {
"enabled": true,
"indentStyle": "space"
},
"linter": {
"enabled": true,
"rules": {
"style": {
"useEnumInitializers": "off"
},
"a11y": {
"noLabelWithoutControl": "off"
}
}
},
"organizeImports": {
"enabled": true
},
"files": {
"ignore": [
"dist",
".vite",
"node_modules",
"tsconfig*.json",
"test-results",
"test-results.nobody-backup",
"playwright-report"
]
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -13,53 +13,51 @@
"lint:fix": "biome check . --write", "lint:fix": "biome check . --write",
"format": "biome format . --write", "format": "biome format . --write",
"preview": "vite preview", "preview": "vite preview",
"test": "node ./node_modules/playwright/cli.js test", "test": "playwright test",
"test:unit": "vitest run", "test:unit": "vitest run",
"test:ui": "node ./node_modules/playwright/cli.js test --ui", "test:ui": "playwright test --ui",
"i18n-scan": "cd .. && node tools/i18n-scanner/index.js && node tools/i18n-scanner/report.js" "i18n-scan": "cd .. && node tools/i18n-scanner/index.js && node tools/i18n-scanner/report.js"
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-avatar": "^1.1.4", "@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-scroll-area": "^1.1.2", "@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6", "@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-switch": "^1.2.6",
"@tanstack/react-query": "^5.66.8", "@tanstack/react-query": "^5.100.10",
"@tanstack/react-query-devtools": "^5.66.8", "@tanstack/react-query-devtools": "^5.100.10",
"@tanstack/react-virtual": "^3.13.24", "@tanstack/react-virtual": "^3.13.24",
"axios": "^1.7.9", "axios": "^1.16.1",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.563.0", "lucide-react": "^1.14.0",
"oidc-client-ts": "^3.4.1", "oidc-client-ts": "^3.5.0",
"react": "^19.2.0", "react": "^19.2.6",
"react-dom": "^19.2.0", "react-dom": "^19.2.6",
"react-hook-form": "^7.71.1", "react-hook-form": "^7.75.0",
"react-oidc-context": "^3.3.0", "react-oidc-context": "^3.3.1",
"react-router-dom": "^6.28.2", "react-router-dom": "^7.15.0",
"tailwind-merge": "^3.4.0", "tailwind-merge": "^3.6.0",
"zod": "^3.24.1" "zod": "^4.4.3"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.4", "@playwright/test": "^1.60.0",
"@playwright/test": "^1.58.0",
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@types/node": "^24.10.1", "@types/node": "^25.7.0",
"@types/react": "^19.2.5", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "^6.0.1",
"autoprefixer": "^10.4.23", "autoprefixer": "^10.5.0",
"jsdom": "^28.1.0", "postcss": "^8.5.14",
"postcss": "^8.5.6", "tailwindcss": "^3.4.19",
"tailwindcss": "^3.4.14",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"typescript": "~5.9.3", "typescript": "^6.0.3",
"vite": "^8.0.3", "vite": "^8.0.12",
"vitest": "^4.0.18" "vitest": "^4.1.6"
} }
} }

View File

@@ -36,21 +36,35 @@ if [ "${1:-}" = "--print-mode" ]; then
fi fi
ensure_frontend_dependencies() { ensure_frontend_dependencies() {
if [ ! -f package.json ] || [ ! -f package-lock.json ]; then # If common workspace exists, manage dependencies from there
if [ -d /common ] && [ -f /common/package.json ]; then
WORKSPACE_DIR="/common"
LOCK_FILE="/common/pnpm-lock.yaml"
else
WORKSPACE_DIR="."
LOCK_FILE="package-lock.json"
fi
if [ ! -f "$WORKSPACE_DIR/package.json" ]; then
return 0 return 0
fi fi
if command -v sha256sum >/dev/null 2>&1; then if command -v sha256sum >/dev/null 2>&1; then
deps_hash="$(sha256sum package.json package-lock.json | sha256sum | awk '{print $1}')" deps_hash="$(sha256sum "$WORKSPACE_DIR/package.json" "$LOCK_FILE" 2>/dev/null | sha256sum | awk '{print $1}')"
else else
deps_hash="$(cksum package.json package-lock.json | cksum | awk '{print $1}')" deps_hash="$(cksum "$WORKSPACE_DIR/package.json" "$LOCK_FILE" 2>/dev/null | cksum | awk '{print $1}')"
fi fi
deps_stamp="node_modules/.baron-deps-hash" deps_stamp="node_modules/.baron-deps-hash"
installed_hash="$(cat "$deps_stamp" 2>/dev/null || true)" installed_hash="$(cat "$deps_stamp" 2>/dev/null || true)"
if [ "$installed_hash" != "$deps_hash" ]; then if [ "$installed_hash" != "$deps_hash" ]; then
echo "Installing frontend dependencies from package-lock.json..." echo "Installing frontend dependencies..."
npm ci if [ "$WORKSPACE_DIR" = "/common" ]; then
(cd /common && rm -rf node_modules .pnpm-store package-lock.json && npm install --no-workspaces --no-fund --no-audit)
else
npm ci
fi
mkdir -p node_modules mkdir -p node_modules
printf '%s\n' "$deps_hash" > "$deps_stamp" printf '%s\n' "$deps_hash" > "$deps_stamp"
fi fi

View File

@@ -10,6 +10,10 @@ import {
Terminal, Terminal,
} from "lucide-react"; } from "lucide-react";
import * as React from "react"; import * as React from "react";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import { Badge } from "../../components/ui/badge"; import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { import {
@@ -27,10 +31,6 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from "../../components/ui/table"; } from "../../components/ui/table";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import type { AuditLog } from "../../lib/adminApi"; import type { AuditLog } from "../../lib/adminApi";
import { fetchAuditLogs } from "../../lib/adminApi"; import { fetchAuditLogs } from "../../lib/adminApi";
import { t } from "../../lib/i18n"; import { t } from "../../lib/i18n";

View File

@@ -1,10 +1,9 @@
import { import {
type UseMutationResult,
useInfiniteQuery, useInfiniteQuery,
useMutation, useMutation,
useQuery, useQuery,
type UseMutationResult,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
import type { UserProfileResponse } from "../../../lib/adminApi";
import { useVirtualizer } from "@tanstack/react-virtual"; import { useVirtualizer } from "@tanstack/react-virtual";
import type { AxiosError } from "axios"; import type { AxiosError } from "axios";
import { import {
@@ -33,16 +32,16 @@ import {
sortableTableHeadBaseClassName, sortableTableHeadBaseClassName,
sortableTableHeaderClassName, sortableTableHeaderClassName,
} from "../../../../../common/core/components/sort"; } from "../../../../../common/core/components/sort";
import {
type SortConfig,
type SortResolverMap,
sortItems,
toggleSort,
} from "../../../../../common/core/utils";
import { import {
commonTableShellClass, commonTableShellClass,
commonTableViewportClass, commonTableViewportClass,
} from "../../../../../common/ui/table"; } from "../../../../../common/ui/table";
import {
sortItems,
toggleSort,
type SortConfig,
type SortResolverMap,
} from "../../../../../common/core/utils";
import { RoleGuard } from "../../../components/auth/RoleGuard"; import { RoleGuard } from "../../../components/auth/RoleGuard";
import { Badge } from "../../../components/ui/badge"; import { Badge } from "../../../components/ui/badge";
import { Button } from "../../../components/ui/button"; import { Button } from "../../../components/ui/button";
@@ -71,7 +70,15 @@ import {
} from "../../../components/ui/dropdown-menu"; } from "../../../components/ui/dropdown-menu";
import { Input } from "../../../components/ui/input"; import { Input } from "../../../components/ui/input";
import { ScrollArea } from "../../../components/ui/scroll-area"; import { ScrollArea } from "../../../components/ui/scroll-area";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../../../components/ui/select";
import { Separator } from "../../../components/ui/separator"; import { Separator } from "../../../components/ui/separator";
import { Switch } from "../../../components/ui/switch";
import { import {
Table, Table,
TableBody, TableBody,
@@ -80,15 +87,8 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from "../../../components/ui/table"; } from "../../../components/ui/table";
import { Switch } from "../../../components/ui/switch";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../../../components/ui/select";
import { toast } from "../../../components/ui/use-toast"; import { toast } from "../../../components/ui/use-toast";
import type { UserProfileResponse } from "../../../lib/adminApi";
import { import {
type TenantSummary, type TenantSummary,
deleteTenant, deleteTenant,

View File

@@ -18,17 +18,17 @@ import {
} from "lucide-react"; } from "lucide-react";
import * as React from "react"; import * as React from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import {
SortableTableHead,
sortableTableHeadBaseClassName,
sortableTableHeaderClassName,
} from "../../../../common/core/components/sort";
import { import {
type SortConfig, type SortConfig,
type SortResolverMap, type SortResolverMap,
sortItems, sortItems,
toggleSort, toggleSort,
} from "../../../../common/core/utils"; } from "../../../../common/core/utils";
import {
SortableTableHead,
sortableTableHeadBaseClassName,
sortableTableHeaderClassName,
} from "../../../../common/core/components/sort";
import { import {
commonTableShellClass, commonTableShellClass,
commonTableViewportClass, commonTableViewportClass,

View File

@@ -21,9 +21,6 @@ createRoot(rootElement).render(
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<RouterProvider <RouterProvider
router={router} router={router}
future={{
v7_startTransition: true,
}}
/> />
<Toaster /> <Toaster />
</QueryClientProvider> </QueryClientProvider>

View File

@@ -1,71 +1,13 @@
import type { Config } from "tailwindcss"; import type { Config } from "tailwindcss";
import { fontFamily } from "tailwindcss/defaultTheme"; import commonPreset from "../common/theme/tailwind.preset";
const config: Config = { const config: Config = {
darkMode: ["class"], presets: [commonPreset],
content: [ content: [
"./index.html", "./index.html",
"./src/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}",
"../common/**/*.{ts,tsx,css}", "../common/**/*.{ts,tsx,css}",
], ],
theme: {
container: {
center: true,
padding: "1.5rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["Space Grotesk", "Pretendard Variable", ...fontFamily.sans],
},
boxShadow: {
card: "0 12px 40px rgba(7, 15, 26, 0.25)",
},
},
},
plugins: [require("tailwindcss-animate")],
}; };
export default config; export default config;

View File

@@ -16,11 +16,12 @@
"noEmit": true, "noEmit": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"baseUrl": ".", "baseUrl": ".",
"ignoreDeprecations": "6.0",
"paths": { "paths": {
"lucide-react": ["./node_modules/lucide-react"], "lucide-react": ["../common/node_modules/lucide-react"],
"react": ["./node_modules/@types/react/index.d.ts"], "react": ["../common/node_modules/@types/react/index.d.ts"],
"react/jsx-dev-runtime": ["./node_modules/@types/react/jsx-dev-runtime.d.ts"], "react/jsx-dev-runtime": ["../common/node_modules/@types/react/jsx-dev-runtime.d.ts"],
"react/jsx-runtime": ["./node_modules/@types/react/jsx-runtime.d.ts"] "react/jsx-runtime": ["../common/node_modules/@types/react/jsx-runtime.d.ts"]
}, },
/* Linting */ /* Linting */

View File

@@ -1,53 +1,44 @@
import react from "@vitejs/plugin-react"; import { defineConfig, mergeConfig } from "vite";
import path from "node:path"; import { commonViteConfig, getAllowedHosts } from "../common/config/vite.base";
import { defineConfig } from "vite";
const buildOutDir = const buildOutDir =
process.env.ADMINFRONT_BUILD_OUT_DIR ?? "/tmp/baron-sso-adminfront-dist"; process.env.ADMINFRONT_BUILD_OUT_DIR ?? "/tmp/baron-sso-adminfront-dist";
export default defineConfig({ const allowedHosts = getAllowedHosts(
plugins: [react()], ["sadmin.hmac.kr", "localhost", "172.16.10.176", "127.0.0.1"],
resolve: { undefined,
alias: { undefined
"lucide-react": path.resolve(__dirname, "node_modules/lucide-react"), );
react: path.resolve(__dirname, "node_modules/react"),
"react/jsx-dev-runtime": path.resolve( export default defineConfig(
__dirname, mergeConfig(commonViteConfig, {
"node_modules/react/jsx-dev-runtime.js", envPrefix: ["VITE_", "USERFRONT_", "ORGFRONT_"],
), cacheDir:
"react/jsx-runtime": path.resolve( process.env.ADMINFRONT_VITE_CACHE_DIR ??
__dirname, "/tmp/baron-sso-adminfront-vite-cache",
"node_modules/react/jsx-runtime.js", build: {
), outDir: buildOutDir,
}, },
}, server: {
envPrefix: ["VITE_", "USERFRONT_", "ORGFRONT_"], host: "127.0.0.1",
cacheDir: allowedHosts,
process.env.ADMINFRONT_VITE_CACHE_DIR ?? proxy: {
"/tmp/baron-sso-adminfront-vite-cache", "/api": {
build: { target: process.env.API_PROXY_TARGET || "http://localhost:3000",
outDir: buildOutDir, changeOrigin: true,
emptyOutDir: true, },
},
server: {
host: "127.0.0.1",
allowedHosts: ["sadmin.hmac.kr", "localhost", "172.16.10.176", "127.0.0.1"],
proxy: {
"/api": {
target: process.env.API_PROXY_TARGET || "http://localhost:3000",
changeOrigin: true,
}, },
}, },
}, preview: {
preview: { host: "127.0.0.1",
host: "127.0.0.1", port: 5173,
port: 5173, allowedHosts,
allowedHosts: ["sadmin.hmac.kr", "localhost", "172.16.10.176", "127.0.0.1"], proxy: {
proxy: { "/api": {
"/api": { target: process.env.API_PROXY_TARGET || "http://localhost:3000",
target: process.env.API_PROXY_TARGET || "http://localhost:3000", changeOrigin: true,
changeOrigin: true, },
}, },
}, },
}, })
}); );

1
common/.npmrc Normal file
View File

@@ -0,0 +1 @@
confirmModulesPurge=false

View File

@@ -0,0 +1,32 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space"
},
"linter": {
"enabled": true,
"rules": {
"style": {
"useEnumInitializers": "off"
},
"a11y": {
"noLabelWithoutControl": "off"
}
}
},
"organizeImports": {
"enabled": true
},
"files": {
"ignore": [
"dist",
".vite",
"node_modules",
"tsconfig*.json",
"test-results",
"test-results.nobody-backup",
"playwright-report"
]
}
}

View File

@@ -0,0 +1,42 @@
import react from "@vitejs/plugin-react";
import { defineConfig, type UserConfig } from "vite";
export const commonViteConfig: UserConfig = {
plugins: [react()],
// Since we are using pnpm and common is our root, we might not need the strict aliases
// for react and lucide-react anymore, as pnpm will resolve them correctly from the root node_modules.
// If we do need them, we can add them back per-app or dynamically resolve from common's __dirname.
build: {
emptyOutDir: true,
},
};
export function hostFromUrl(value: string | undefined) {
if (!value) return undefined;
try {
return new URL(value).hostname;
} catch {
return value;
}
}
export function getAllowedHosts(
defaultHosts: string[],
envUrl?: string,
envAllowedHosts?: string
) {
return Array.from(
new Set(
[
...defaultHosts,
hostFromUrl(envUrl),
...(envAllowedHosts ?? "")
.split(",")
.map((host) => host.trim())
.filter(Boolean),
].filter((host): host is string => Boolean(host))
)
);
}
export default defineConfig(commonViteConfig);

48
common/package.json Normal file
View File

@@ -0,0 +1,48 @@
{
"name": "baron-sso",
"private": true,
"scripts": {
"dev:all": "pnpm -r run dev",
"build:all": "pnpm -r run build",
"lint:all": "pnpm -r run lint"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@playwright/test": "^1.58.0",
"autoprefixer": "^10.4.23",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.14",
"tailwindcss-animate": "^1.0.7",
"typescript": "~5.9.3",
"vite": "^8.0.3",
"vitest": "^4.1.5",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"jsdom": "^28.1.0"
},
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^6.28.2",
"@tanstack/react-query": "^5.66.8",
"@tanstack/react-query-devtools": "^5.66.8",
"axios": "^1.7.9",
"lucide-react": "^0.563.0",
"clsx": "^2.1.1",
"tailwind-merge": "^3.4.0",
"class-variance-authority": "^0.7.1",
"zod": "^3.24.1",
"react-hook-form": "^7.71.1",
"oidc-client-ts": "^3.4.1",
"react-oidc-context": "^3.3.0",
"@radix-ui/react-avatar": "^1.1.4",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-scroll-area": "^1.1.2",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.2"
}
}

4181
common/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
packages:
- "../adminfront"
- "../devfront"
- "../orgfront"
allowBuilds:
'@biomejs/biome': false

View File

@@ -0,0 +1,68 @@
import type { Config } from "tailwindcss";
import { fontFamily } from "tailwindcss/defaultTheme";
import animatePlugin from "tailwindcss-animate";
const commonPreset: Config = {
darkMode: ["class"],
content: [], // Content should be defined by the consuming app
theme: {
container: {
center: true,
padding: "1.5rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["Space Grotesk", "Pretendard Variable", ...fontFamily.sans],
},
boxShadow: {
card: "0 12px 40px rgba(7, 15, 26, 0.25)",
},
},
},
plugins: [animatePlugin],
};
export default commonPreset;

View File

@@ -0,0 +1,8 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@common/*": ["./*"]
}
}
}

View File

@@ -321,6 +321,7 @@ services:
ports: ports:
- "${ADMINFRONT_PORT}:5173" - "${ADMINFRONT_PORT}:5173"
volumes: volumes:
- ../../common:/common
- ../../adminfront:/app - ../../adminfront:/app
- ./adminfront/vite.config.ts:/app/vite.config.ts:ro - ./adminfront/vite.config.ts:/app/vite.config.ts:ro
- ./adminfront/auth.ts:/app/src/lib/auth.ts:ro - ./adminfront/auth.ts:/app/src/lib/auth.ts:ro
@@ -335,6 +336,7 @@ services:
ports: ports:
- "${DEVFRONT_PORT}:5173" - "${DEVFRONT_PORT}:5173"
volumes: volumes:
- ../../common:/common
- ../../devfront:/app - ../../devfront:/app
- ./devfront/vite.config.ts:/app/vite.config.ts:ro - ./devfront/vite.config.ts:/app/vite.config.ts:ro
- ./devfront/auth.ts:/app/src/lib/auth.ts:ro - ./devfront/auth.ts:/app/src/lib/auth.ts:ro
@@ -349,6 +351,7 @@ services:
ports: ports:
- "${ORGFRONT_PORT}:5175" - "${ORGFRONT_PORT}:5175"
volumes: volumes:
- ../../common:/common
- ../../orgfront:/app - ../../orgfront:/app
- ./orgfront/vite.config.ts:/app/vite.config.ts:ro - ./orgfront/vite.config.ts:/app/vite.config.ts:ro
- ./orgfront/auth.ts:/app/src/lib/auth.ts:ro - ./orgfront/auth.ts:/app/src/lib/auth.ts:ro

View File

@@ -1,16 +1,21 @@
FROM node:lts FROM node:lts
WORKDIR /app WORKDIR /workspace
# 패키지 정보 복사 및 의존성 설치 # Install pnpm
COPY package*.json ./ RUN npm install -g pnpm
RUN npm ci
# Copy workspace configs and common package
COPY common ./common
COPY devfront ./devfront
# Install dependencies for the workspace
RUN cd common && pnpm install --no-frozen-lockfile --ignore-scripts
# 프로덕션 서빙을 위한 serve 패키지 글로벌 설치 # 프로덕션 서빙을 위한 serve 패키지 글로벌 설치
RUN npm install -g serve RUN npm install -g serve
# 소스 코드 복사 WORKDIR /workspace/devfront
COPY . .
# Vite 기본 포트 # Vite 기본 포트
EXPOSE 5173 EXPOSE 5173

View File

@@ -1,30 +1,3 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "extends": ["../common/config/biome.base.json"]
"formatter": {
"indentStyle": "space"
},
"linter": {
"enabled": true,
"rules": {
"style": {
"useEnumInitializers": "off"
},
"a11y": {
"noLabelWithoutControl": "off"
}
}
},
"organizeImports": {
"enabled": true
},
"files": {
"ignore": [
"dist",
".vite",
"node_modules",
"tsconfig*.json",
"test-results",
"playwright-report"
]
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -16,40 +16,41 @@
"test:roles": "playwright test tests/devfront-role-switch-report.spec.ts", "test:roles": "playwright test tests/devfront-role-switch-report.spec.ts",
"test:ui": "playwright test --ui" "test:ui": "playwright test --ui"
}, },
"dependencies": {
"@radix-ui/react-avatar": "^1.1.4",
"@radix-ui/react-scroll-area": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.2",
"@tanstack/react-query": "^5.66.8",
"@tanstack/react-query-devtools": "^5.66.8",
"axios": "^1.7.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.563.0",
"oidc-client-ts": "^3.4.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-hook-form": "^7.71.1",
"react-oidc-context": "^3.3.0",
"react-router-dom": "^6.28.2",
"tailwind-merge": "^3.4.0",
"zod": "^3.24.1"
},
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.4", "@playwright/test": "^1.60.0",
"@playwright/test": "^1.58.0", "@types/node": "^25.7.0",
"@types/node": "^24.10.1", "@types/react": "^19.2.14",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "^6.0.1",
"autoprefixer": "^10.4.23", "autoprefixer": "^10.5.0",
"jsdom": "^28.1.0", "postcss": "^8.5.14",
"postcss": "^8.5.6", "tailwindcss": "^3.4.19",
"tailwindcss": "^3.4.14",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"typescript": "~5.9.3", "typescript": "^6.0.3",
"vite": "^8.0.3", "vite": "^8.0.12",
"vitest": "^4.1.5" "vitest": "^4.1.6"
},
"dependencies": {
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@tanstack/react-query": "^5.100.10",
"@tanstack/react-query-devtools": "^5.100.10",
"axios": "^1.16.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.14.0",
"oidc-client-ts": "^3.5.0",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react-hook-form": "^7.75.0",
"react-oidc-context": "^3.3.1",
"react-router-dom": "^7.15.0",
"tailwind-merge": "^3.6.0",
"zod": "^4.4.3"
} }
} }

View File

@@ -36,21 +36,35 @@ if [ "${1:-}" = "--print-mode" ]; then
fi fi
ensure_frontend_dependencies() { ensure_frontend_dependencies() {
if [ ! -f package.json ] || [ ! -f package-lock.json ]; then # If common workspace exists, manage dependencies from there
if [ -d /common ] && [ -f /common/package.json ]; then
WORKSPACE_DIR="/common"
LOCK_FILE="/common/pnpm-lock.yaml"
else
WORKSPACE_DIR="."
LOCK_FILE="package-lock.json"
fi
if [ ! -f "$WORKSPACE_DIR/package.json" ]; then
return 0 return 0
fi fi
if command -v sha256sum >/dev/null 2>&1; then if command -v sha256sum >/dev/null 2>&1; then
deps_hash="$(sha256sum package.json package-lock.json | sha256sum | awk '{print $1}')" deps_hash="$(sha256sum "$WORKSPACE_DIR/package.json" "$LOCK_FILE" 2>/dev/null | sha256sum | awk '{print $1}')"
else else
deps_hash="$(cksum package.json package-lock.json | cksum | awk '{print $1}')" deps_hash="$(cksum "$WORKSPACE_DIR/package.json" "$LOCK_FILE" 2>/dev/null | cksum | awk '{print $1}')"
fi fi
deps_stamp="node_modules/.baron-deps-hash" deps_stamp="node_modules/.baron-deps-hash"
installed_hash="$(cat "$deps_stamp" 2>/dev/null || true)" installed_hash="$(cat "$deps_stamp" 2>/dev/null || true)"
if [ "$installed_hash" != "$deps_hash" ]; then if [ "$installed_hash" != "$deps_hash" ]; then
echo "Installing frontend dependencies from package-lock.json..." echo "Installing frontend dependencies..."
npm ci if [ "$WORKSPACE_DIR" = "/common" ]; then
(cd /common && rm -rf node_modules .pnpm-store package-lock.json && npm install --no-workspaces --no-fund --no-audit)
else
npm ci
fi
mkdir -p node_modules mkdir -p node_modules
printf '%s\n' "$deps_hash" > "$deps_stamp" printf '%s\n' "$deps_hash" > "$deps_stamp"
fi fi

View File

@@ -9,6 +9,10 @@ import {
Search, Search,
} from "lucide-react"; } from "lucide-react";
import * as React from "react"; import * as React from "react";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import { ForbiddenMessage } from "../../components/common/ForbiddenMessage"; import { ForbiddenMessage } from "../../components/common/ForbiddenMessage";
import { Badge } from "../../components/ui/badge"; import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
@@ -28,10 +32,6 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from "../../components/ui/table"; } from "../../components/ui/table";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import type { DevAuditLog } from "../../lib/devApi"; import type { DevAuditLog } from "../../lib/devApi";
import { fetchDevAuditLogs } from "../../lib/devApi"; import { fetchDevAuditLogs } from "../../lib/devApi";
import { t } from "../../lib/i18n"; import { t } from "../../lib/i18n";

View File

@@ -10,6 +10,10 @@ import {
} from "lucide-react"; } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import { ForbiddenMessage } from "../../components/common/ForbiddenMessage"; import { ForbiddenMessage } from "../../components/common/ForbiddenMessage";
import { Badge } from "../../components/ui/badge"; import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
@@ -28,10 +32,6 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from "../../components/ui/table"; } from "../../components/ui/table";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import { fetchClient, fetchConsents, revokeConsent } from "../../lib/devApi"; import { fetchClient, fetchConsents, revokeConsent } from "../../lib/devApi";
import { t } from "../../lib/i18n"; import { t } from "../../lib/i18n";
import { cn } from "../../lib/utils"; import { cn } from "../../lib/utils";

View File

@@ -9,16 +9,16 @@ import {
sortableTableHeadBaseClassName, sortableTableHeadBaseClassName,
sortableTableHeaderClassName, sortableTableHeaderClassName,
} from "../../../../common/core/components/sort"; } from "../../../../common/core/components/sort";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import { import {
type SortConfig, type SortConfig,
type SortResolverMap, type SortResolverMap,
sortItems, sortItems,
toggleSort, toggleSort,
} from "../../../../common/core/utils"; } from "../../../../common/core/utils";
import {
commonTableShellClass,
commonTableViewportClass,
} from "../../../../common/ui/table";
import { ForbiddenMessage } from "../../components/common/ForbiddenMessage"; import { ForbiddenMessage } from "../../components/common/ForbiddenMessage";
import { import {
Avatar, Avatar,

View File

@@ -1,71 +1,13 @@
import type { Config } from "tailwindcss"; import type { Config } from "tailwindcss";
import { fontFamily } from "tailwindcss/defaultTheme"; import commonPreset from "../common/theme/tailwind.preset";
const config: Config = { const config: Config = {
darkMode: ["class"], presets: [commonPreset],
content: [ content: [
"./index.html", "./index.html",
"./src/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}",
"../common/**/*.{ts,tsx,css}", "../common/**/*.{ts,tsx,css}",
], ],
theme: {
container: {
center: true,
padding: "1.5rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["Space Grotesk", "Pretendard Variable", ...fontFamily.sans],
},
boxShadow: {
card: "0 12px 40px rgba(7, 15, 26, 0.25)",
},
},
},
plugins: [require("tailwindcss-animate")],
}; };
export default config; export default config;

View File

@@ -16,11 +16,12 @@
"noEmit": true, "noEmit": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"baseUrl": ".", "baseUrl": ".",
"ignoreDeprecations": "6.0",
"paths": { "paths": {
"lucide-react": ["./node_modules/lucide-react"], "lucide-react": ["../common/node_modules/lucide-react"],
"react": ["./node_modules/@types/react/index.d.ts"], "react": ["../common/node_modules/@types/react/index.d.ts"],
"react/jsx-dev-runtime": ["./node_modules/@types/react/jsx-dev-runtime.d.ts"], "react/jsx-dev-runtime": ["../common/node_modules/@types/react/jsx-dev-runtime.d.ts"],
"react/jsx-runtime": ["./node_modules/@types/react/jsx-runtime.d.ts"] "react/jsx-runtime": ["../common/node_modules/@types/react/jsx-runtime.d.ts"]
}, },
/* Linting */ /* Linting */

View File

@@ -1,80 +1,43 @@
import react from "@vitejs/plugin-react"; import { defineConfig, mergeConfig } from "vite";
import path from "node:path"; import { commonViteConfig, getAllowedHosts } from "../common/config/vite.base";
import { defineConfig } from "vite";
const buildOutDir = const buildOutDir =
process.env.DEVFRONT_BUILD_OUT_DIR ?? "/tmp/baron-sso-devfront-dist"; process.env.DEVFRONT_BUILD_OUT_DIR ?? "/tmp/baron-sso-devfront-dist";
const defaultAllowedHosts = [ const allowedHosts = getAllowedHosts(
"sdev.hmac.kr", ["sdev.hmac.kr", "localhost", "172.16.10.176", "127.0.0.1"],
"localhost", process.env.DEVFRONT_URL,
"172.16.10.176", process.env.DEVFRONT_ALLOWED_HOSTS
"127.0.0.1",
];
function hostFromUrl(value: string | undefined) {
if (!value) return undefined;
try {
return new URL(value).hostname;
} catch {
return value;
}
}
const allowedHosts = Array.from(
new Set(
[
...defaultAllowedHosts,
hostFromUrl(process.env.DEVFRONT_URL),
...(process.env.DEVFRONT_ALLOWED_HOSTS ?? "")
.split(",")
.map((host) => host.trim())
.filter(Boolean),
].filter((host): host is string => Boolean(host)),
),
); );
export default defineConfig({ export default defineConfig(
plugins: [react()], mergeConfig(commonViteConfig, {
resolve: { cacheDir:
alias: { process.env.DEVFRONT_VITE_CACHE_DIR ??
"lucide-react": path.resolve(__dirname, "node_modules/lucide-react"), "/tmp/baron-sso-devfront-vite-cache",
react: path.resolve(__dirname, "node_modules/react"), build: {
"react/jsx-dev-runtime": path.resolve( outDir: buildOutDir,
__dirname,
"node_modules/react/jsx-dev-runtime.js",
),
"react/jsx-runtime": path.resolve(
__dirname,
"node_modules/react/jsx-runtime.js",
),
}, },
}, server: {
cacheDir: host: "127.0.0.1",
process.env.DEVFRONT_VITE_CACHE_DIR ?? "/tmp/baron-sso-devfront-vite-cache", allowedHosts,
build: { proxy: {
outDir: buildOutDir, "/api": {
emptyOutDir: true, target: process.env.API_PROXY_TARGET || "http://localhost:3000",
}, changeOrigin: true,
server: { },
host: "127.0.0.1",
allowedHosts,
proxy: {
"/api": {
target: process.env.API_PROXY_TARGET || "http://localhost:3000",
changeOrigin: true,
}, },
}, },
}, preview: {
preview: { host: "127.0.0.1",
host: "127.0.0.1", port: 5173,
port: 5173, allowedHosts,
allowedHosts, proxy: {
proxy: { "/api": {
"/api": { target: process.env.API_PROXY_TARGET || "http://localhost:3000",
target: process.env.API_PROXY_TARGET || "http://localhost:3000", changeOrigin: true,
changeOrigin: true, },
}, },
}, },
}, })
}); );

View File

@@ -49,8 +49,8 @@ services:
adminfront: adminfront:
build: build:
context: ./adminfront context: .
dockerfile: Dockerfile dockerfile: ./adminfront/Dockerfile
container_name: baron_adminfront container_name: baron_adminfront
env_file: env_file:
- .env - .env
@@ -70,8 +70,8 @@ services:
devfront: devfront:
build: build:
context: ./devfront context: .
dockerfile: Dockerfile dockerfile: ./devfront/Dockerfile
container_name: baron_devfront container_name: baron_devfront
env_file: env_file:
- .env - .env
@@ -91,8 +91,8 @@ services:
orgfront: orgfront:
build: build:
context: ./orgfront context: .
dockerfile: Dockerfile dockerfile: ./orgfront/Dockerfile
container_name: baron_orgfront container_name: baron_orgfront
env_file: env_file:
- .env - .env

View File

@@ -1,16 +1,21 @@
FROM node:lts FROM node:lts
WORKDIR /app WORKDIR /workspace
# 패키지 정보 복사 및 의존성 설치 # Install pnpm
COPY package*.json ./ RUN npm install -g pnpm
RUN npm ci
# Copy workspace configs and common package
COPY common ./common
COPY orgfront ./orgfront
# Install dependencies for the workspace
RUN cd common && pnpm install --no-frozen-lockfile --ignore-scripts
# 프로덕션 서빙을 위한 serve 패키지 글로벌 설치 # 프로덕션 서빙을 위한 serve 패키지 글로벌 설치
RUN npm install -g serve RUN npm install -g serve
# 소스 코드 복사 WORKDIR /workspace/orgfront
COPY . .
# Vite 기본 포트 # Vite 기본 포트
EXPOSE 5175 EXPOSE 5175

View File

@@ -1,30 +1,3 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "extends": ["../common/config/biome.base.json"]
"formatter": {
"indentStyle": "space"
},
"linter": {
"enabled": true,
"rules": {
"style": {
"useEnumInitializers": "off"
},
"a11y": {
"noLabelWithoutControl": "off"
}
}
},
"organizeImports": {
"enabled": true
},
"files": {
"ignore": [
"dist",
".vite",
"node_modules",
"tsconfig*.json",
"test-results",
"playwright-report"
]
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -20,40 +20,41 @@
"test:ui": "playwright test --ui" "test:ui": "playwright test --ui"
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-avatar": "^1.1.4", "@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-scroll-area": "^1.1.2", "@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-scroll-area": "^1.2.10",
"@tanstack/react-query": "^5.66.8", "@radix-ui/react-select": "^2.2.6",
"@tanstack/react-query-devtools": "^5.66.8", "@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@tanstack/react-query": "^5.100.10",
"@tanstack/react-query-devtools": "^5.100.10",
"@xyflow/react": "^12.10.2", "@xyflow/react": "^12.10.2",
"axios": "^1.7.9", "axios": "^1.16.1",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.563.0", "lucide-react": "^1.14.0",
"oidc-client-ts": "^3.4.1", "oidc-client-ts": "^3.5.0",
"react": "^19.2.0", "react": "^19.2.6",
"react-dom": "^19.2.0", "react-dom": "^19.2.6",
"react-hook-form": "^7.71.1", "react-hook-form": "^7.75.0",
"react-oidc-context": "^3.3.0", "react-oidc-context": "^3.3.1",
"react-router-dom": "^6.28.2", "react-router-dom": "^7.15.0",
"tailwind-merge": "^3.4.0", "tailwind-merge": "^3.6.0",
"zod": "^3.24.1" "zod": "^4.4.3"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.4", "@playwright/test": "^1.60.0",
"@playwright/test": "^1.58.0", "@types/node": "^25.7.0",
"@types/node": "^24.10.1", "@types/react": "^19.2.14",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "^6.0.1",
"autoprefixer": "^10.4.23", "autoprefixer": "^10.5.0",
"jsdom": "^28.1.0", "postcss": "^8.5.14",
"postcss": "^8.5.6", "tailwindcss": "^3.4.19",
"tailwindcss": "^3.4.14",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"typescript": "~5.9.3", "typescript": "^6.0.3",
"vite": "^8.0.3", "vite": "^8.0.12",
"vitest": "^4.1.5" "vitest": "^4.1.6"
} }
} }

View File

@@ -36,21 +36,35 @@ if [ "${1:-}" = "--print-mode" ]; then
fi fi
ensure_frontend_dependencies() { ensure_frontend_dependencies() {
if [ ! -f package.json ] || [ ! -f package-lock.json ]; then # If common workspace exists, manage dependencies from there
if [ -d /common ] && [ -f /common/package.json ]; then
WORKSPACE_DIR="/common"
LOCK_FILE="/common/pnpm-lock.yaml"
else
WORKSPACE_DIR="."
LOCK_FILE="package-lock.json"
fi
if [ ! -f "$WORKSPACE_DIR/package.json" ]; then
return 0 return 0
fi fi
if command -v sha256sum >/dev/null 2>&1; then if command -v sha256sum >/dev/null 2>&1; then
deps_hash="$(sha256sum package.json package-lock.json | sha256sum | awk '{print $1}')" deps_hash="$(sha256sum "$WORKSPACE_DIR/package.json" "$LOCK_FILE" 2>/dev/null | sha256sum | awk '{print $1}')"
else else
deps_hash="$(cksum package.json package-lock.json | cksum | awk '{print $1}')" deps_hash="$(cksum "$WORKSPACE_DIR/package.json" "$LOCK_FILE" 2>/dev/null | cksum | awk '{print $1}')"
fi fi
deps_stamp="node_modules/.baron-deps-hash" deps_stamp="node_modules/.baron-deps-hash"
installed_hash="$(cat "$deps_stamp" 2>/dev/null || true)" installed_hash="$(cat "$deps_stamp" 2>/dev/null || true)"
if [ "$installed_hash" != "$deps_hash" ]; then if [ "$installed_hash" != "$deps_hash" ]; then
echo "Installing frontend dependencies from package-lock.json..." echo "Installing frontend dependencies..."
npm ci if [ "$WORKSPACE_DIR" = "/common" ]; then
(cd /common && rm -rf node_modules .pnpm-store package-lock.json && npm install --no-workspaces --no-fund --no-audit)
else
npm ci
fi
mkdir -p node_modules mkdir -p node_modules
printf '%s\n' "$deps_hash" > "$deps_stamp" printf '%s\n' "$deps_hash" > "$deps_stamp"
fi fi

View File

@@ -1,71 +1,13 @@
import type { Config } from "tailwindcss"; import type { Config } from "tailwindcss";
import { fontFamily } from "tailwindcss/defaultTheme"; import commonPreset from "../common/theme/tailwind.preset";
const config: Config = { const config: Config = {
darkMode: ["class"], presets: [commonPreset],
content: [ content: [
"./index.html", "./index.html",
"./src/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}",
"../common/**/*.{ts,tsx,css}", "../common/**/*.{ts,tsx,css}",
], ],
theme: {
container: {
center: true,
padding: "1.5rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["Space Grotesk", "Pretendard Variable", ...fontFamily.sans],
},
boxShadow: {
card: "0 12px 40px rgba(7, 15, 26, 0.25)",
},
},
},
plugins: [require("tailwindcss-animate")],
}; };
export default config; export default config;

View File

@@ -16,10 +16,11 @@
"noEmit": true, "noEmit": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"baseUrl": ".", "baseUrl": ".",
"ignoreDeprecations": "6.0",
"paths": { "paths": {
"react": ["./node_modules/@types/react/index.d.ts"], "react": ["../common/node_modules/@types/react/index.d.ts"],
"react/jsx-dev-runtime": ["./node_modules/@types/react/jsx-dev-runtime.d.ts"], "react/jsx-dev-runtime": ["../common/node_modules/@types/react/jsx-dev-runtime.d.ts"],
"react/jsx-runtime": ["./node_modules/@types/react/jsx-runtime.d.ts"] "react/jsx-runtime": ["../common/node_modules/@types/react/jsx-runtime.d.ts"]
}, },
/* Linting */ /* Linting */

View File

@@ -1,68 +1,51 @@
import react from "@vitejs/plugin-react"; import { defineConfig, mergeConfig } from "vite";
import { defineConfig } from "vite"; import { commonViteConfig, getAllowedHosts } from "../common/config/vite.base";
const buildOutDir = const buildOutDir =
process.env.ORGFRONT_BUILD_OUT_DIR ?? "/tmp/baron-sso-orgfront-dist"; process.env.ORGFRONT_BUILD_OUT_DIR ?? "/tmp/baron-sso-orgfront-dist";
const defaultAllowedHosts = [ const allowedHosts = getAllowedHosts(
"baron-orgchart.hmac.kr", [
"sorg.hmac.kr", "baron-orgchart.hmac.kr",
"sdev.hmac.kr", "sorg.hmac.kr",
"localhost", "sdev.hmac.kr",
"172.16.10.176", "localhost",
"127.0.0.1", "172.16.10.176",
]; "127.0.0.1",
],
function hostFromUrl(value: string | undefined) { process.env.ORGFRONT_URL,
if (!value) return undefined; process.env.ORGFRONT_ALLOWED_HOSTS
try {
return new URL(value).hostname;
} catch {
return value;
}
}
const allowedHosts = Array.from(
new Set(
[
...defaultAllowedHosts,
hostFromUrl(process.env.ORGFRONT_URL),
...(process.env.ORGFRONT_ALLOWED_HOSTS ?? "")
.split(",")
.map((host) => host.trim())
.filter(Boolean),
].filter((host): host is string => Boolean(host)),
),
); );
export default defineConfig({ export default defineConfig(
plugins: [react()], mergeConfig(commonViteConfig, {
cacheDir: cacheDir:
process.env.ORGFRONT_VITE_CACHE_DIR ?? "/tmp/baron-sso-orgfront-vite-cache", process.env.ORGFRONT_VITE_CACHE_DIR ??
build: { "/tmp/baron-sso-orgfront-vite-cache",
outDir: buildOutDir, build: {
emptyOutDir: true, outDir: buildOutDir,
}, },
server: { server: {
host: "0.0.0.0", host: "0.0.0.0",
port: 5175, port: 5175,
allowedHosts, allowedHosts,
proxy: { proxy: {
"/api": { "/api": {
target: process.env.API_PROXY_TARGET || "http://localhost:5000", target: process.env.API_PROXY_TARGET || "http://localhost:5000",
changeOrigin: true, changeOrigin: true,
},
}, },
}, },
}, preview: {
preview: { host: "0.0.0.0",
host: "0.0.0.0", port: 5175,
port: 5175, allowedHosts,
allowedHosts, proxy: {
proxy: { "/api": {
"/api": { target: process.env.API_PROXY_TARGET || "http://localhost:5000",
target: process.env.API_PROXY_TARGET || "http://localhost:5000", changeOrigin: true,
changeOrigin: true, },
}, },
}, },
}, })
}); );

View File

@@ -7,11 +7,12 @@ tmp_dir=""
cleanup() { cleanup() {
if [ -n "${tmp_dir:-}" ] && [ -d "$tmp_dir" ]; then if [ -n "${tmp_dir:-}" ] && [ -d "$tmp_dir" ]; then
rm -rf "$tmp_dir" rm -rf "$tmp_dir" || true
fi fi
} }
trap cleanup EXIT INT TERM trap "cleanup; exit" INT TERM
trap "cleanup" EXIT
mkdir -p reports mkdir -p reports
rm -rf adminfront/node_modules rm -rf adminfront/node_modules
@@ -133,7 +134,7 @@ fi
set +e set +e
( (
cd "$tmp_dir/adminfront" cd "$tmp_dir/adminfront"
npm ci --ignore-scripts npm install -g pnpm && pnpm install -C ../common --no-frozen-lockfile
) 2>&1 | tee reports/adminfront-install.log ) 2>&1 | tee reports/adminfront-install.log
install_exit_code=${PIPESTATUS[0]} install_exit_code=${PIPESTATUS[0]}
set -e set -e
@@ -148,7 +149,7 @@ if [ "$install_exit_code" -ne 0 ]; then
echo "- Exit Code: \`$install_exit_code\`" echo "- Exit Code: \`$install_exit_code\`"
echo echo
echo "## Command" echo "## Command"
echo "\`cd adminfront && npm ci --ignore-scripts\`" echo "\`cd adminfront && npm install -g pnpm && pnpm install -C ../common --no-frozen-lockfile\`"
echo echo
echo "## Install Log Tail (last 200 lines)" echo "## Install Log Tail (last 200 lines)"
echo '```text' echo '```text'
@@ -197,7 +198,7 @@ echo "==> adminfront using PORT=$port"
( (
cd "$tmp_dir/adminfront" cd "$tmp_dir/adminfront"
PORT="$port" PLAYWRIGHT_WORKERS="${PLAYWRIGHT_WORKERS:-1}" \ PORT="$port" PLAYWRIGHT_WORKERS="${PLAYWRIGHT_WORKERS:-1}" \
node ./node_modules/playwright/cli.js test "${playwright_project_args[@]}" npx playwright test "${playwright_project_args[@]}"
) 2>&1 | tee reports/adminfront-test.log ) 2>&1 | tee reports/adminfront-test.log
test_exit_code=${PIPESTATUS[0]} test_exit_code=${PIPESTATUS[0]}
set -e set -e
@@ -215,9 +216,9 @@ if [ "$test_exit_code" -ne 0 ]; then
echo echo
echo "## Commands" echo "## Commands"
echo "1. \`cd adminfront\`" echo "1. \`cd adminfront\`"
echo "2. \`npm ci --ignore-scripts\`" echo "2. \`npm install -g pnpm && pnpm install -C ../common --no-frozen-lockfile\`"
echo "3. \`${playwright_install_desc}\`" echo "3. \`${playwright_install_desc}\`"
echo "4. \`node ./node_modules/playwright/cli.js test\`" echo "4. \`npx playwright test\`"
echo echo
echo "## Log Tail (last 200 lines)" echo "## Log Tail (last 200 lines)"
echo '```text' echo '```text'