login画面追加
This commit is contained in:
@@ -6,7 +6,6 @@ from fastapi.responses import JSONResponse, RedirectResponse
|
||||
from pydantic import ValidationError
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
|
||||
# FastAPI アプリケーション作成
|
||||
app = FastAPI(
|
||||
title="日本小規模企業向け会計システム"
|
||||
@@ -32,13 +31,15 @@ async def validation_exception_handler(request: Request, exc: ValidationError):
|
||||
# ─────────────────────────
|
||||
# ルート(稼働確認)
|
||||
# ─────────────────────────
|
||||
#@app.get("/", include_in_schema=False)
|
||||
#def root():
|
||||
# return RedirectResponse(url="/index.html")
|
||||
|
||||
@app.get("/", include_in_schema=False)
|
||||
def root():
|
||||
return RedirectResponse("/login.html")
|
||||
# ─────────────────────────
|
||||
# ルーター登録(※ 必ず app 定義の後)
|
||||
# ─────────────────────────
|
||||
from app.modules.users.router import router as users_router
|
||||
app.include_router(users_router)
|
||||
|
||||
from app.modules.accounts.router import router as accounts_router
|
||||
app.include_router(accounts_router)
|
||||
|
||||
@@ -139,7 +140,7 @@ print("Frontend path =", frontend_path)
|
||||
if frontend_path.exists():
|
||||
app.mount(
|
||||
"/",
|
||||
StaticFiles(directory=frontend_path, html=True),
|
||||
StaticFiles(directory=frontend_path),
|
||||
name="frontend",
|
||||
)
|
||||
else:
|
||||
|
||||
1
backend/app/modules/users/__init__.py
Normal file
1
backend/app/modules/users/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Users Module
|
||||
104
backend/app/modules/users/router.py
Normal file
104
backend/app/modules/users/router.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
import hashlib
|
||||
import os
|
||||
from app.core.database import get_connection
|
||||
|
||||
router = APIRouter(prefix="/api/users", tags=["users"])
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class LoginResponse(BaseModel):
|
||||
id: int
|
||||
username: str
|
||||
full_name: str | None
|
||||
email: str | None
|
||||
message: str
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
"""パスワードをハッシュ化"""
|
||||
# 実運用ではより安全なハッシュアルゴリズムを使用してください
|
||||
return hashlib.sha256(password.encode()).hexdigest()
|
||||
|
||||
|
||||
@router.post("/login", response_model=LoginResponse)
|
||||
async def login(request: LoginRequest):
|
||||
"""ユーザーのログイン処理"""
|
||||
try:
|
||||
conn = get_connection()
|
||||
cur = conn.cursor()
|
||||
|
||||
# ユーザーが存在するか確認
|
||||
cur.execute(
|
||||
"SELECT id, username, password, full_name, email FROM users WHERE username = %s AND is_active = TRUE",
|
||||
(request.username,)
|
||||
)
|
||||
user = cur.fetchone()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
if not user:
|
||||
raise HTTPException(status_code=401, detail="ユーザー名またはパスワードが正しくありません")
|
||||
|
||||
# パスワードの検証
|
||||
hashed_password = hash_password(request.password)
|
||||
if user["password"] != hashed_password:
|
||||
raise HTTPException(status_code=401, detail="ユーザー名またはパスワードが正しくありません")
|
||||
|
||||
return LoginResponse(
|
||||
id=user["id"],
|
||||
username=user["username"],
|
||||
full_name=user["full_name"],
|
||||
email=user["email"],
|
||||
message="ログインに成功しました"
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"ログイン処理エラー: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/register")
|
||||
async def register(request: LoginRequest):
|
||||
"""新規ユーザー登録"""
|
||||
try:
|
||||
if len(request.password) < 6:
|
||||
raise HTTPException(status_code=400, detail="パスワードは6文字以上である必要があります")
|
||||
|
||||
conn = get_connection()
|
||||
cur = conn.cursor()
|
||||
|
||||
# ユーザーが既に存在するか確認
|
||||
cur.execute("SELECT id FROM users WHERE username = %s", (request.username,))
|
||||
if cur.fetchone():
|
||||
cur.close()
|
||||
conn.close()
|
||||
raise HTTPException(status_code=400, detail="このユーザー名は既に使用されています")
|
||||
|
||||
# ユーザーを登録
|
||||
hashed_password = hash_password(request.password)
|
||||
cur.execute(
|
||||
"INSERT INTO users (username, password) VALUES (%s, %s) RETURNING id, username",
|
||||
(request.username, hashed_password)
|
||||
)
|
||||
new_user = cur.fetchone()
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return {
|
||||
"id": new_user["id"],
|
||||
"username": new_user["username"],
|
||||
"message": "ユーザー登録が完了しました"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"登録処理エラー: {str(e)}")
|
||||
14
backend/sql/create_users_table.sql
Normal file
14
backend/sql/create_users_table.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
-- ユーザーテーブル作成
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(100),
|
||||
full_name VARCHAR(100),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- ユーザーの一覧表示用インデックス
|
||||
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
|
||||
Reference in New Issue
Block a user