1
0
forked from baron/baron-sso

make dev/dev-debug 구분.

This commit is contained in:
2026-05-20 13:34:19 +09:00
parent 0155ee4ee7
commit 5496735e2f
15 changed files with 192 additions and 45 deletions

View File

@@ -29,7 +29,7 @@ ifneq (,$(wildcard ./.env))
COMPOSE_DROP_ENV_ARGS += --env-file .env
endif
.PHONY: build-auth-config validate-auth-config verify-auth-config render-ory-config up up-all up-infra up-ory up-app up-backend ensure-networks ensure-infra ensure-ory up-dev up-front-dev dev down drop down-app down-backend down-infra down-ory check-infra ps logs-infra logs-ory logs-app
.PHONY: build-auth-config validate-auth-config verify-auth-config render-ory-config up up-all up-infra up-ory up-app up-backend ensure-networks ensure-infra ensure-ory up-dev up-front-dev dev dev-debug down drop down-app down-backend down-infra down-ory check-infra ps logs-infra logs-ory logs-app
# --- 인증 설정 빌드/검증 ---
build-auth-config:
@@ -128,7 +128,11 @@ up-front-dev: up-infra up-ory up-backend
dev: up-dev
@echo "Starting development app containers in foreground attach mode..."
docker compose $(COMPOSE_CLI_ENV_ARGS) -f $(COMPOSE_APP) up --build $(DEV_SERVICES)
BACKEND_LOG_LEVEL=info CLIENT_LOG_DEBUG=false VITE_CLIENT_LOG_DEBUG=false docker compose $(COMPOSE_CLI_ENV_ARGS) -f $(COMPOSE_APP) up --build $(DEV_SERVICES)
dev-debug: up-dev
@echo "Starting development app containers in foreground attach debug mode..."
BACKEND_LOG_LEVEL=debug CLIENT_LOG_DEBUG=true VITE_CLIENT_LOG_DEBUG=true USERFRONT_FLUTTER_RUN_FLAGS=--debug docker compose $(COMPOSE_CLI_ENV_ARGS) -f $(COMPOSE_APP) up --build $(DEV_SERVICES)
# --- 종료 (Down) ---
down:

View File

@@ -34,6 +34,7 @@ import {
} from "../../../../common/shell";
import { buildAuthenticatedOrgChartUrl } from "../../features/users/orgChartPicker";
import { fetchMe } from "../../lib/adminApi";
import { debugLog } from "../../lib/debugLog";
import { t } from "../../lib/i18n";
import { isSuperAdminRole } from "../../lib/roles";
import {
@@ -154,10 +155,10 @@ function AppLayout() {
} = useQuery({
queryKey: ["me"],
queryFn: async () => {
console.debug("[AppLayout] Fetching profile...");
debugLog("[AppLayout] Fetching profile...");
try {
const data = await fetchMe();
console.debug("[AppLayout] Profile fetched successfully:", data.email);
debugLog("[AppLayout] Profile fetched successfully:", data.email);
return data;
} catch (err) {
console.error("[AppLayout] Failed to fetch profile:", err);
@@ -273,7 +274,7 @@ function AppLayout() {
(window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean })
._IS_TEST_MODE === true;
console.debug("[AppLayout] Auth state check:", {
debugLog("[AppLayout] Auth state check:", {
isLoading: auth.isLoading,
isAuthenticated: auth.isAuthenticated,
isTest,

View File

@@ -2,13 +2,14 @@ import { ShieldHalf } from "lucide-react";
import { useEffect } from "react";
import { useAuth } from "react-oidc-context";
import { useNavigate } from "react-router-dom";
import { debugLog } from "../../lib/debugLog";
function AuthCallbackPage() {
const auth = useAuth();
const navigate = useNavigate();
useEffect(() => {
console.debug("[AuthCallbackPage] State:", {
debugLog("[AuthCallbackPage] State:", {
isAuthenticated: auth.isAuthenticated,
isLoading: auth.isLoading,
error: auth.error,

View File

@@ -10,6 +10,7 @@ import {
CardHeader,
CardTitle,
} from "../../components/ui/card";
import { debugLog } from "../../lib/debugLog";
function LoginPage() {
const auth = useAuth();
@@ -20,7 +21,7 @@ function LoginPage() {
const shouldAutoLogin = searchParams.get("auto") === "1";
useEffect(() => {
console.debug("[LoginPage] Auth state check:", {
debugLog("[LoginPage] Auth state check:", {
isAuthenticated: auth.isAuthenticated,
isLoading: auth.isLoading,
returnTo,

View File

@@ -0,0 +1,8 @@
const CLIENT_DEBUG_LOG_ENABLED = new Set(["1", "true", "yes", "y", "on"]).has(
String(import.meta.env.VITE_CLIENT_LOG_DEBUG ?? "").trim().toLowerCase(),
);
export function debugLog(...args: Parameters<typeof console.debug>) {
if (!CLIENT_DEBUG_LOG_ENABLED) return;
console.debug(...args);
}

View File

@@ -37,20 +37,25 @@ func IsProductionEnv(appEnv string) bool {
return IsProductionLikeEnv(appEnv)
}
func parseBoolFlag(raw string) bool {
func parseOptionalBoolFlag(raw string) (bool, bool) {
switch strings.ToLower(strings.TrimSpace(raw)) {
case "1", "true", "yes", "y", "on":
return true
return true, true
case "0", "false", "no", "n", "off":
return false, true
default:
return false
return false, false
}
}
func ClientDebugEnabled(appEnv, productionDebugFlag string) bool {
func ClientDebugEnabled(appEnv, debugFlag string) bool {
if enabled, ok := parseOptionalBoolFlag(debugFlag); ok {
return enabled
}
if !IsProductionEnv(appEnv) {
return true
}
return parseBoolFlag(productionDebugFlag)
return false
}
func NormalizeClientLogLevel(level string) slog.Level {

View File

@@ -9,7 +9,14 @@ import (
func TestClientDebugEnabled(t *testing.T) {
t.Run("non production enables debug by default", func(t *testing.T) {
assert.True(t, ClientDebugEnabled("dev", ""))
assert.True(t, ClientDebugEnabled("local", "false"))
assert.True(t, ClientDebugEnabled("local", ""))
})
t.Run("explicit debug flag applies in non production", func(t *testing.T) {
assert.True(t, ClientDebugEnabled("dev", "true"))
assert.True(t, ClientDebugEnabled("local", "1"))
assert.False(t, ClientDebugEnabled("dev", "false"))
assert.False(t, ClientDebugEnabled("local", "0"))
})
t.Run("production disables debug by default", func(t *testing.T) {
@@ -37,6 +44,8 @@ func TestShouldAcceptClientLog(t *testing.T) {
assert.True(t, ShouldAcceptClientLog("stage", "", "ERROR"))
assert.True(t, ShouldAcceptClientLog("production", "true", "INFO"))
assert.True(t, ShouldAcceptClientLog("dev", "", "INFO"))
assert.False(t, ShouldAcceptClientLog("dev", "false", "INFO"))
assert.True(t, ShouldAcceptClientLog("dev", "false", "WARN"))
}
func TestShouldFilterNoisyClientInfo(t *testing.T) {
@@ -44,6 +53,7 @@ func TestShouldFilterNoisyClientInfo(t *testing.T) {
assert.True(t, ShouldFilterNoisyClientInfo("stage", "", "Navigating to /ko/signin"))
assert.False(t, ShouldFilterNoisyClientInfo("production", "true", "Navigating to /ko/signin"))
assert.False(t, ShouldFilterNoisyClientInfo("dev", "", "Navigating to /ko/signin"))
assert.True(t, ShouldFilterNoisyClientInfo("dev", "false", "Navigating to /ko/signin"))
}
func TestSanitizeClientLogData(t *testing.T) {

View File

@@ -9,6 +9,8 @@ services:
environment:
- APP_ENV=${APP_ENV:-development}
- GO_ENV=${APP_ENV:-development}
- BACKEND_LOG_LEVEL=${BACKEND_LOG_LEVEL:-info}
- CLIENT_LOG_DEBUG=${CLIENT_LOG_DEBUG:-false}
- COOKIE_SECRET=${COOKIE_SECRET}
- JWT_SECRET=${JWT_SECRET}
- NAVER_CLOUD_ACCESS_KEY=${NAVER_CLOUD_ACCESS_KEY}
@@ -58,6 +60,7 @@ services:
- APP_ENV=${APP_ENV:-development}
- API_PROXY_TARGET=http://baron_backend:3000
- USERFRONT_URL=${USERFRONT_URL}
- VITE_CLIENT_LOG_DEBUG=${VITE_CLIENT_LOG_DEBUG:-false}
ports:
- "${ADMINFRONT_PORT:-5173}:5173"
volumes:
@@ -82,6 +85,7 @@ services:
- APP_ENV=${APP_ENV:-development}
- API_PROXY_TARGET=http://baron_backend:3000
- USERFRONT_URL=${USERFRONT_URL}
- VITE_CLIENT_LOG_DEBUG=${VITE_CLIENT_LOG_DEBUG:-false}
ports:
- "${DEVFRONT_PORT:-5174}:5173"
volumes:
@@ -106,6 +110,7 @@ services:
- APP_ENV=${APP_ENV:-development}
- API_PROXY_TARGET=http://baron_backend:3000
- USERFRONT_URL=${USERFRONT_URL}
- VITE_CLIENT_LOG_DEBUG=${VITE_CLIENT_LOG_DEBUG:-false}
ports:
- "${ORGFRONT_PORT:-5175}:5175"
volumes:
@@ -132,7 +137,9 @@ services:
- BACKEND_URL=${BACKEND_URL:-}
- USERFRONT_URL=${USERFRONT_URL}
- APP_ENV=${APP_ENV}
- CLIENT_LOG_DEBUG=${CLIENT_LOG_DEBUG:-false}
- USERFRONT_INTERNAL_PORT=5000
- USERFRONT_FLUTTER_RUN_FLAGS=${USERFRONT_FLUTTER_RUN_FLAGS:-}
volumes:
- ./userfront/lib:/workspace/userfront/lib
- ./userfront/assets:/workspace/userfront/assets

View File

@@ -40,7 +40,14 @@ grep -Fq -- "AS dev" "$USERFRONT_DOCKERFILE" || fail "userfront Dockerfile must
grep -Fq -- "AS production" "$USERFRONT_DOCKERFILE" || fail "userfront Dockerfile must keep an explicit production target"
grep -Fq -- "flutter run" "$USERFRONT_DEV_SERVER" || fail "userfront dev server must use flutter run"
grep -Fq -- "--wasm" "$USERFRONT_DEV_SERVER" || fail "userfront dev server must keep WebAssembly enabled"
grep -Fq -- "--debug" "$USERFRONT_DEV_SERVER" || fail "userfront dev server must run in debug mode"
grep -Fq -- "--dart-define=CLIENT_LOG_DEBUG=" "$USERFRONT_DEV_SERVER" || fail "userfront dev server must pass client log debug mode through dart-define"
grep -Fq -- "--dart-define=APP_ENV=" "$USERFRONT_DEV_SERVER" || fail "userfront dev server must pass app env through dart-define"
grep -Fq -- 'USERFRONT_FLUTTER_RUN_FLAGS' "$USERFRONT_DEV_SERVER" || fail "userfront dev server must accept optional Flutter run flags"
assert_contains 'CLIENT_LOG_DEBUG=${CLIENT_LOG_DEBUG:-false}'
assert_contains 'USERFRONT_FLUTTER_RUN_FLAGS=${USERFRONT_FLUTTER_RUN_FLAGS:-}'
if grep -Fq -- "--debug" "$USERFRONT_DEV_SERVER"; then
fail "make dev must not hard-code Flutter debug mode in the userfront dev server"
fi
if grep -Fq -- "--release" "$USERFRONT_DEV_SERVER"; then
fail "userfront dev server must not run Flutter in release mode"
fi

View File

@@ -54,6 +54,54 @@ if ! grep -q -- " --build" <<<"$app_up_line"; then
exit 1
fi
if ! grep -q -- "BACKEND_LOG_LEVEL=info" <<<"$app_up_line"; then
echo "make dev must run backend at info log level." >&2
exit 1
fi
if ! grep -q -- "CLIENT_LOG_DEBUG=false" <<<"$app_up_line"; then
echo "make dev must disable verbose client debug log ingestion." >&2
exit 1
fi
if ! grep -q -- "VITE_CLIENT_LOG_DEBUG=false" <<<"$app_up_line"; then
echo "make dev must disable React client debug console logs." >&2
exit 1
fi
if grep -q -- "BACKEND_LOG_LEVEL=debug" <<<"$app_up_line"; then
echo "make dev must not run backend at debug log level." >&2
exit 1
fi
if grep -q -- "USERFRONT_FLUTTER_RUN_FLAGS=--debug" <<<"$app_up_line"; then
echo "make dev must not run userfront with explicit Flutter debug flags." >&2
exit 1
fi
dry_run_dev_debug="$(
make --dry-run --always-make -C "$repo_root" dev-debug DEV_SERVICES="backend userfront" 2>&1
)"
if ! grep -q "Ensuring Infra stack" <<<"$dry_run_dev_debug"; then
echo "make dev-debug must ensure the infra stack first." >&2
exit 1
fi
if ! grep -q "Ensuring Ory stack" <<<"$dry_run_dev_debug"; then
echo "make dev-debug must ensure the Ory stack first." >&2
exit 1
fi
dev_debug_app_up_line="$(
grep -E "BACKEND_LOG_LEVEL=debug CLIENT_LOG_DEBUG=true VITE_CLIENT_LOG_DEBUG=true USERFRONT_FLUTTER_RUN_FLAGS=--debug docker compose .* -f docker-compose.yaml up .*backend.*userfront" <<<"$dry_run_dev_debug" | tail -1
)"
if [[ -z "$dev_debug_app_up_line" ]]; then
echo "make dev-debug must run app services with explicit backend, client log, and userfront debug flags." >&2
exit 1
fi
dry_run_up_dev="$(
make --dry-run --always-make -C "$repo_root" up-dev 2>&1
)"

View File

@@ -29,23 +29,37 @@ class LogPolicy {
env == 'staging';
}
static bool parseBoolFlag(String? raw) {
static ({bool enabled, bool specified}) parseOptionalBoolFlag(String? raw) {
final value = (raw ?? '').trim().toLowerCase();
return value == '1' ||
if (value == '1' ||
value == 'true' ||
value == 'yes' ||
value == 'y' ||
value == 'on';
value == 'on') {
return (enabled: true, specified: true);
}
if (value == '0' ||
value == 'false' ||
value == 'no' ||
value == 'n' ||
value == 'off') {
return (enabled: false, specified: true);
}
return (enabled: false, specified: false);
}
static bool debugEnabled({
required String? appEnv,
required String? productionDebugFlag,
}) {
final flag = parseOptionalBoolFlag(productionDebugFlag);
if (flag.specified) {
return flag.enabled;
}
if (!isProductionEnv(appEnv)) {
return true;
}
return parseBoolFlag(productionDebugFlag);
return false;
}
static bool shouldRelayClientLog({

View File

@@ -1,10 +1,10 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:logging/logging.dart' as std_log;
import 'package:logger/logger.dart' as pretty_log;
import 'auth_proxy_service.dart';
import 'log_policy.dart';
import 'runtime_env.dart';
/// Global Logger Service for Baron SSO Frontend
class LoggerService {
@@ -16,10 +16,10 @@ class LoggerService {
late final String _productionDebugFlag;
LoggerService._internal() {
_appEnv = _envOrDefault('APP_ENV', 'dev');
_productionDebugFlag = _envOrDefault(
_appEnv = envOrDefault('APP_ENV', 'dev');
_productionDebugFlag = envOrDefault(
'CLIENT_LOG_DEBUG',
_envOrDefault('USERFRONT_DEBUG_LOG', ''),
envOrDefault('USERFRONT_DEBUG_LOG', ''),
);
final debugEnabled = LogPolicy.debugEnabled(
appEnv: _appEnv,
@@ -54,17 +54,6 @@ class LoggerService {
});
}
static String _envOrDefault(String key, String fallback) {
if (!dotenv.isInitialized) {
return fallback;
}
final value = dotenv.env[key];
if (value == null || value.trim().isEmpty) {
return fallback;
}
return value;
}
/// Initialize the logger. Call this in main.dart
static void init() {
// Accessing the instance triggers the constructor

View File

@@ -1,5 +1,11 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
const _compileTimeEnv = {
'APP_ENV': String.fromEnvironment('APP_ENV'),
'CLIENT_LOG_DEBUG': String.fromEnvironment('CLIENT_LOG_DEBUG'),
'USERFRONT_DEBUG_LOG': String.fromEnvironment('USERFRONT_DEBUG_LOG'),
};
String runtimeOriginFallback() {
try {
final origin = Uri.base.origin;
@@ -11,14 +17,18 @@ String runtimeOriginFallback() {
}
String envOrDefault(String key, String fallback) {
if (!dotenv.isInitialized) {
return fallback;
if (dotenv.isInitialized) {
final value = dotenv.env[key];
if (value != null && value.trim().isNotEmpty) {
return value;
}
}
final value = dotenv.env[key];
if (value == null || value.trim().isEmpty) {
return fallback;
final compileTimeValue = _compileTimeEnv[key];
if (compileTimeValue != null && compileTimeValue.trim().isNotEmpty) {
return compileTimeValue;
}
return value;
return fallback;
}
String sanitizedUrl(String value) {

View File

@@ -5,10 +5,14 @@ cd /workspace
/bin/sh ./scripts/sync_userfront_locales.sh
cd /workspace/userfront
exec flutter run \
set -- flutter run \
-d web-server \
--web-hostname 0.0.0.0 \
--web-port "${USERFRONT_INTERNAL_PORT:-5000}" \
--wasm \
--debug \
--dart-define=CLIENT_LOG_DEBUG="${CLIENT_LOG_DEBUG:-false}" \
--dart-define=APP_ENV="${APP_ENV:-dev}" \
${USERFRONT_FLUTTER_RUN_FLAGS:-} \
--no-web-resources-cdn
exec "$@"

View File

@@ -9,14 +9,30 @@ void main() {
isTrue,
);
expect(
LogPolicy.debugEnabled(
appEnv: 'development',
productionDebugFlag: 'false',
),
LogPolicy.debugEnabled(appEnv: 'development', productionDebugFlag: ''),
isTrue,
);
});
test('explicit debug flag applies in development-like environment', () {
expect(
LogPolicy.debugEnabled(appEnv: 'dev', productionDebugFlag: 'true'),
isTrue,
);
expect(
LogPolicy.debugEnabled(appEnv: 'development', productionDebugFlag: '1'),
isTrue,
);
expect(
LogPolicy.debugEnabled(appEnv: 'dev', productionDebugFlag: 'false'),
isFalse,
);
expect(
LogPolicy.debugEnabled(appEnv: 'development', productionDebugFlag: '0'),
isFalse,
);
});
test('production disables debug unless explicitly enabled', () {
expect(
LogPolicy.debugEnabled(appEnv: 'production', productionDebugFlag: ''),
@@ -94,6 +110,28 @@ void main() {
isTrue,
);
});
test(
'explicit development debug false forwards only warning or higher',
() {
expect(
LogPolicy.shouldRelayClientLog(
level: 'INFO',
appEnv: 'dev',
productionDebugFlag: 'false',
),
isFalse,
);
expect(
LogPolicy.shouldRelayClientLog(
level: 'WARN',
appEnv: 'dev',
productionDebugFlag: 'false',
),
isTrue,
);
},
);
});
group('LogPolicy.sanitize', () {