Initial commit: IT센터 출입자관리시스템 (ACS)
방문자 사전신청·승인, 입·출입 체크인/아웃, QR 배지, 재실현황, 블랙리스트, 대시보드 통계, 방문 리포트(엑셀)까지 7단계 전 기능 구현. - backend: Spring Boot 3.4.5 / Java 21 (JDK 26 빌드), 세션 인증, JPA, H2/PostgreSQL, POI, ZXing, Flyway - frontend: React 19 / Vite 6 / TypeScript - infra: Docker Compose (db·app·web nginx), Flyway V1__init, Python 사용자 시드 - docs: 워크플로우 / 시퀀스 다이어그램(Mermaid) / 이슈·유의사항 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
105
scripts/seed-load.py
Normal file
105
scripts/seed-load.py
Normal file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
seed-load.py — Load users into the ACS (Access Control System) database.
|
||||
|
||||
CSV/XLSX columns:
|
||||
username, full_name, email, department, password, must_change_password, roles
|
||||
- roles: semicolon-separated, e.g. "ADMIN;SECURITY" (values: ADMIN, SECURITY, HOST)
|
||||
|
||||
Usage:
|
||||
python scripts/seed-load.py --file scripts/seeds/users.csv
|
||||
|
||||
Connection via env (PGHOST/PGPORT/PGUSER/PGPASSWORD/PGDATABASE) or --dsn.
|
||||
Idempotent: existing usernames are updated (password re-hashed), roles replaced.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
|
||||
import pandas as pd
|
||||
import psycopg2
|
||||
from passlib.hash import bcrypt
|
||||
|
||||
VALID_ROLES = {"ADMIN", "SECURITY", "HOST"}
|
||||
|
||||
|
||||
def read_sheet(path):
|
||||
if path.lower().endswith(".csv"):
|
||||
return pd.read_csv(path, dtype=str).fillna("")
|
||||
return pd.read_excel(path, dtype=str).fillna("")
|
||||
|
||||
|
||||
def get_conn(dsn=None):
|
||||
if dsn:
|
||||
return psycopg2.connect(dsn)
|
||||
return psycopg2.connect(
|
||||
host=os.getenv("PGHOST", "localhost"),
|
||||
port=os.getenv("PGPORT", "5432"),
|
||||
user=os.getenv("PGUSER", "acs"),
|
||||
password=os.getenv("PGPASSWORD", ""),
|
||||
dbname=os.getenv("PGDATABASE", "acs"),
|
||||
)
|
||||
|
||||
|
||||
def parse_bool(v):
|
||||
return str(v).strip().lower() in ("1", "true", "yes", "y")
|
||||
|
||||
|
||||
def upsert_user(cur, row):
|
||||
username = row["username"].strip()
|
||||
if not username:
|
||||
return False
|
||||
password = row.get("password", "").strip() or "ChangeMe123!"
|
||||
pw_hash = bcrypt.hash(password)
|
||||
roles = [r.strip().upper() for r in str(row.get("roles", "")).split(";") if r.strip()]
|
||||
roles = [r for r in roles if r in VALID_ROLES] or ["HOST"]
|
||||
|
||||
cur.execute("SELECT id FROM users WHERE username = %s", (username,))
|
||||
existing = cur.fetchone()
|
||||
|
||||
if existing:
|
||||
user_id = existing[0]
|
||||
cur.execute(
|
||||
"""UPDATE users SET updated_at = now(), password_hash = %s, full_name = %s,
|
||||
email = %s, department = %s, must_change_password = %s WHERE id = %s""",
|
||||
(pw_hash, row.get("full_name", "").strip() or username,
|
||||
row.get("email", "").strip() or None, row.get("department", "").strip() or None,
|
||||
parse_bool(row.get("must_change_password", "true")), user_id),
|
||||
)
|
||||
else:
|
||||
cur.execute(
|
||||
"""INSERT INTO users (created_at, updated_at, username, password_hash, full_name,
|
||||
email, department, must_change_password, enabled, locked)
|
||||
VALUES (now(), now(), %s, %s, %s, %s, %s, %s, TRUE, FALSE) RETURNING id""",
|
||||
(username, pw_hash, row.get("full_name", "").strip() or username,
|
||||
row.get("email", "").strip() or None, row.get("department", "").strip() or None,
|
||||
parse_bool(row.get("must_change_password", "true"))),
|
||||
)
|
||||
user_id = cur.fetchone()[0]
|
||||
|
||||
cur.execute("DELETE FROM user_roles WHERE user_id = %s", (user_id,))
|
||||
for role in set(roles):
|
||||
cur.execute("INSERT INTO user_roles (user_id, role) VALUES (%s, %s)", (user_id, role))
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--file", required=True)
|
||||
parser.add_argument("--dsn", default=None)
|
||||
args = parser.parse_args()
|
||||
|
||||
df = read_sheet(args.file)
|
||||
conn = get_conn(args.dsn)
|
||||
count = 0
|
||||
try:
|
||||
with conn, conn.cursor() as cur:
|
||||
for _, row in df.iterrows():
|
||||
if upsert_user(cur, row):
|
||||
count += 1
|
||||
print(f"[seed-load] upserted {count} user(s).")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user