260706_1_계정, 로그인, 보안 초안 작성

This commit is contained in:
2026-07-07 19:17:13 +09:00
parent 404941c47a
commit f8633bb1fe
4696 changed files with 6110 additions and 642171 deletions
+1
View File
@@ -36,6 +36,7 @@ async def init_db_pool() -> aiomysql.Pool:
maxsize=DB_POOL_MAX,
autocommit=False,
charset="utf8mb4",
init_command="SET time_zone='+00:00'",
)
return db_pool
+4 -3
View File
@@ -16,12 +16,12 @@ export const API_BASE_URL = "/api";
/** API 요청 타임아웃 (ms) */
export const API_TIMEOUT_MS = 30_000;
/** 인증 토큰을 저장할 localStorage 키 */
export const AUTH_TOKEN_KEY = "frd_auth_token";
/** B03~B09 워크플로우에서 사용할 현재 프로젝트 UUID 저장 키 */
export const CURRENT_PROJECT_ID_KEY = "frd_current_project_id";
/** 인증 토큰 저장 키 (HttpOnly 쿠키 사용, localStorage는 참조용) */
export const AUTH_TOKEN_KEY = "frd_auth_token";
/* -----------------------------------------------------------------------------
* 2. 파일 업로드 제한 (B03_FileInput)
* -------------------------------------------------------------------------- */
@@ -76,6 +76,7 @@ export const ROUTES = {
A06_LOGIN: "a06-login",
A07_REGISTER: "a07-register",
A08_SUPPORT: "a08-support",
A09_SECURITY: "a09-security",
// 로그인 후
B01_ACCOUNT: "b01-account",
B02_PROJ_REGISTER: "b02-proj-register",
+23 -5
View File
@@ -6,10 +6,12 @@ config_system.py
"""
import os
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
PROJECT_ROOT = Path(__file__).resolve().parent.parent
load_dotenv(PROJECT_ROOT / ".env")
os.environ["PYTHONUTF8"] = "1"
# ─────────────────────────────────────────────────────────────────────────
# 1. FastAPI 서버
@@ -267,11 +269,27 @@ def get_project_storage_path(company: str, user: str, project_id: str) -> str:
# ─────────────────────────────────────────────────────────────────────────
# 7. 인증 (JWT)
# 7. 인증 및 이메일
# ─────────────────────────────────────────────────────────────────────────
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "your-secret-key-change-in-production")
JWT_ALGORITHM = "HS256"
JWT_EXPIRE_MINUTES = int(os.getenv("JWT_EXPIRE_MINUTES", "1440")) # 24시간
SESSION_COOKIE_NAME = os.getenv("SESSION_COOKIE_NAME", "session_id")
SESSION_MAX_AGE_SECONDS = int(os.getenv("SESSION_MAX_AGE_SECONDS", "43200"))
SESSION_IDLE_TIMEOUT_SECONDS = int(os.getenv("SESSION_IDLE_TIMEOUT_SECONDS", "14400"))
SESSION_COOKIE_SECURE = os.getenv("SESSION_COOKIE_SECURE", "True").lower() == "true"
PASSWORD_BCRYPT_ROUNDS = int(os.getenv("PASSWORD_BCRYPT_ROUNDS", "12"))
OTP_VALID_MINUTES = int(os.getenv("EMAIL_OTP_VALID_MINUTES", "5"))
EMAIL_REVERIFY_DAYS = int(os.getenv("EMAIL_REVERIFY_DAYS", "90"))
SMTP_HOST = os.getenv("SMTP_HOST", "smtp.gmail.com")
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
SMTP_USERNAME = os.getenv("SMTP_USERNAME", "")
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
SMTP_USE_TLS = os.getenv("SMTP_USE_TLS", "True").lower() == "true"
EMAIL_FROM_ADDRESS = os.getenv("EMAIL_FROM_ADDRESS", SMTP_USERNAME)
EMAIL_FROM_NAME = os.getenv("EMAIL_FROM_NAME", "Aislo Support")
EMAIL_RETRY_COUNT = int(os.getenv("EMAIL_RETRY_COUNT", "3"))
EMAIL_RETRY_DELAY_SECONDS = int(os.getenv("EMAIL_RETRY_DELAY_SECONDS", "2"))
EMAIL_NOTIFICATION_ENABLED = os.getenv("EMAIL_NOTIFICATION_ENABLED", "True").lower() == "true"
ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "")
# ─────────────────────────────────────────────────────────────────────────
# 8. CORS (프론트엔드 도메인)
-24
View File
@@ -1,24 +0,0 @@
{
"name": "forest-road-webapp",
"version": "0.1.0",
"description": "임도 설계 및 견적 자동화 프로그램 (프론트엔드)",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc --noEmit && vite build",
"preview": "vite preview",
"typecheck": "tsc --noEmit",
"format": "prettier --write \"../**/*.{ts,css,html}\""
},
"dependencies": {
"maplibre-gl": "^5.24.0",
"three": "^0.185.0"
},
"devDependencies": {
"@types/node": "^26.1.0",
"@types/three": "^0.185.0",
"prettier": "^3.0.0",
"typescript": "^6.0.3",
"vite": "^8.1.0"
}
}
-39
View File
@@ -1,39 +0,0 @@
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"allowJs": false,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"types": ["node"],
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"paths": {
"@ui/*": ["../ui_template/*"],
"@config/*": ["./*"],
"@util/*": ["../common_util/*"]
}
},
"include": [
"../A00_Common",
"../A0*",
"../B0*",
"../B1*",
"../ui_template",
"./**/*",
"../common_util"
],
"exclude": ["0_old", "node_modules", "venv", "dist"]
}
-43
View File
@@ -1,43 +0,0 @@
import { defineConfig } from "vite";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = resolve(fileURLToPath(import.meta.url), "..");
/**
* Vite 설정 — 임도 설계 웹앱 (Vanilla TS + Three.js)
*
* - root: A00_Common (index.html 위치)
* - 페이지 폴더(A01_Home 등)는 ../를 통해 접근
* - 0_old(구형 코드), venv(파이썬)는 빌드에서 제외
*/
export default defineConfig({
root: "../A00_Common",
publicDir: "..",
resolve: {
alias: {
"@ui": resolve(__dirname, "../ui_template"),
"@config": resolve(__dirname, "./"),
"@util": resolve(__dirname, "../common_util"),
},
},
server: {
port: 5173,
open: true,
// 백엔드(FastAPI) 프록시 — config_frontend.ts의 API_BASE_URL과 정합
proxy: {
"/api": {
target: "http://localhost:8000",
changeOrigin: true,
},
},
},
build: {
outDir: "../node_modules/.build",
emptyOutDir: true,
target: "es2022",
},
optimizeDeps: {
exclude: ["0_old"],
},
});