WildOjisan commited on
Commit
1c61a98
·
1 Parent(s): b42adf5
Files changed (11) hide show
  1. .dockerignore +8 -0
  2. .env +1 -0
  3. .gitignore +14 -0
  4. Dockerfile +18 -0
  5. app.py +22 -0
  6. readme.txt +7 -0
  7. requirements.txt +7 -0
  8. routers/test_router.py +64 -0
  9. routers/testdb_router.py +106 -0
  10. utils/common.py +10 -0
  11. utils/db.py +67 -0
.dockerignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ *.log
6
+ .git
7
+ .gitignore
8
+ .venv/
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ POSTGRES_URL="postgres://postgres.hjgdlilnfccakdsmsypg:qBXsw226lYJabYgp@aws-1-ap-northeast-2.pooler.supabase.com:6543/postgres?sslmode=require"
.gitignore ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python Bytecode Files
2
+ *.pyc
3
+ __pycache__/
4
+
5
+ # Dependencies
6
+ venv/
7
+ .venv/
8
+ .mypy_cache/
9
+
10
+ # ⭐️ 학습된 모델 및 결과 폴더 제외 ⭐️
11
+ my_local_models/
12
+ model_save/
13
+ model_save_minilm/
14
+
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.13-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # 패키지 설치
6
+ COPY requirements.txt .
7
+
8
+ RUN pip install --no-cache-dir --upgrade pip \
9
+ && pip install --no-cache-dir -r requirements.txt
10
+
11
+ # 프로젝트 전체 복사
12
+ COPY . .
13
+
14
+ # Hugging Face Spaces 기본 포트
15
+ EXPOSE 7860
16
+
17
+ # FastAPI 실행
18
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from routers import test_router, testdb_router
3
+ import uvicorn
4
+
5
+ app = FastAPI()
6
+
7
+ app.include_router(test_router.router)
8
+ app.include_router(testdb_router.router)
9
+
10
+
11
+ @app.get("/")
12
+ def home():
13
+ return {"message": "FastAPI 실행 성공"}
14
+
15
+
16
+ if __name__ == "__main__":
17
+ uvicorn.run(
18
+ "app:app",
19
+ host="127.0.0.1",
20
+ port=8000,
21
+ reload=True
22
+ )
readme.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ py -3.13 -m uv venv venv
2
+
3
+ .\venv\Scripts\Activate.ps1
4
+
5
+ uv pip install -r requirements.txt
6
+
7
+ uv run python app.py
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ python-multipart
4
+ python-dotenv
5
+ pydantic
6
+ sqlalchemy
7
+ psycopg[binary]
routers/test_router.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Form, File, UploadFile
2
+ from utils.common import CommonResponse
3
+
4
+ router = APIRouter(
5
+ prefix="/test",
6
+ tags=["test"]
7
+ )
8
+
9
+ @router.get("/")
10
+ def test_home():
11
+ try:
12
+ return CommonResponse(success=True)
13
+ except Exception as e:
14
+ return CommonResponse(success=False, msg=str(e))
15
+
16
+ @router.get("/info")
17
+ def test_info(name: str = "", age: int = 0):
18
+ try:
19
+ # 비즈니스 로직 처리
20
+ data = {
21
+ "name": name,
22
+ "age": age
23
+ }
24
+ return CommonResponse(success=True, data=data)
25
+ except Exception as e:
26
+ return CommonResponse(success=False, msg=str(e))
27
+
28
+ @router.get("/mytest")
29
+ def mytest(username: str = "", password: str=""):
30
+ try:
31
+ # 비즈니스 로직 처리
32
+ data = {
33
+ "username": username,
34
+ "password": password
35
+ }
36
+ return CommonResponse(success=True, data=data)
37
+ except Exception as e:
38
+ return CommonResponse(success=False, msg=str(e))
39
+
40
+ @router.post("/mytest")
41
+ def post_mytest(username: str = Form(""), password: str = Form("")):
42
+ try:
43
+ # business logic
44
+ data = {
45
+ "username": username,
46
+ "password": password
47
+ }
48
+ return CommonResponse(success=True, data=data)
49
+ except Exception as e:
50
+ return CommonResponse(success=False, msg=str(e))
51
+
52
+ @router.post("/upload")
53
+ async def upload_file(file: UploadFile = File(...)):
54
+ try:
55
+ # business logic
56
+ contents = await file.read()
57
+ data = {
58
+ "filename": file.filename,
59
+ "content_type": file.content_type,
60
+ "size": len(contents)
61
+ }
62
+ return CommonResponse(success=True, data=data)
63
+ except Exception as e:
64
+ return CommonResponse(success=False, msg=str(e))
routers/testdb_router.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Form, File, UploadFile
2
+ from utils.common import CommonResponse
3
+ from utils.db import get_connection
4
+
5
+ router = APIRouter(
6
+ prefix="/testdb",
7
+ tags=["testdb"]
8
+ )
9
+
10
+ @router.get("/")
11
+ def test_home():
12
+ try:
13
+ return CommonResponse(success=True)
14
+ except Exception as e:
15
+ return CommonResponse(success=False, msg=str(e))
16
+
17
+ @router.get("/now")
18
+ def select_now():
19
+ try:
20
+ with get_connection() as conn:
21
+ with conn.cursor() as cur:
22
+ cur.execute("select now()")
23
+ row = cur.fetchone()
24
+
25
+ data = {
26
+ "now": row[0].isoformat() if row else None
27
+ }
28
+ return CommonResponse(success=True, data=data)
29
+ except Exception as e:
30
+ return CommonResponse(success=False, msg=str(e))
31
+
32
+ @router.get("/select_test")
33
+ def select_now(id: int = 0):
34
+ try:
35
+ with get_connection() as conn:
36
+ with conn.cursor() as cur:
37
+ cur.execute("""
38
+ SELECT
39
+ *
40
+ FROM t_test
41
+ WHERE
42
+ CASE
43
+ WHEN %s != 0 THEN id = %s
44
+ ELSE TRUE
45
+ END
46
+ LIMIT 1000
47
+ """, [id, id])
48
+ row = cur.fetchall()
49
+
50
+ data = row
51
+ return CommonResponse(success=True, data=data)
52
+ except Exception as e:
53
+ return CommonResponse(success=False, msg=str(e))
54
+
55
+ @router.post("/upsert_test")
56
+ def upsert_test(id: int = Form(0), name: str = Form("")):
57
+ try:
58
+ with get_connection() as conn:
59
+ with conn.cursor() as cur:
60
+ if id <= 0:
61
+ cur.execute("""
62
+ INSERT INTO t_test (name)
63
+ VALUES (%s)
64
+ RETURNING id, name
65
+ """, [name])
66
+ else:
67
+ cur.execute("""
68
+ UPDATE t_test
69
+ SET name = %s
70
+ WHERE id = %s
71
+ RETURNING id, name
72
+ """, [name, id])
73
+
74
+ row = cur.fetchone()
75
+ if not row:
76
+ conn.rollback()
77
+ return CommonResponse(success=False, msg="data not found")
78
+
79
+ conn.commit()
80
+
81
+ data = {
82
+ "id": row[0],
83
+ "name": row[1]
84
+ }
85
+ return CommonResponse(success=True, data=data)
86
+ except Exception as e:
87
+ return CommonResponse(success=False, msg=str(e))
88
+
89
+ @router.post("/delete_test")
90
+ def delete_test(id: int = Form(0)):
91
+ try:
92
+ with get_connection() as conn:
93
+ with conn.cursor() as cur:
94
+ cur.execute("""
95
+ DELETE FROM t_test
96
+ WHERE id = %s
97
+ """, [id])
98
+
99
+ conn.commit()
100
+
101
+ data = {
102
+ "msg" : "delete success"
103
+ }
104
+ return CommonResponse(success=True, data=data)
105
+ except Exception as e:
106
+ return CommonResponse(success=False, msg=str(e))
utils/common.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Generic, TypeVar, Any, Optional
2
+ from pydantic import BaseModel
3
+
4
+ T = TypeVar("T")
5
+
6
+ class CommonResponse(BaseModel, Generic[T]):
7
+ success: bool=True
8
+ data: Optional[T] = None
9
+ msg: str = ""
10
+
utils/db.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
3
+
4
+ import psycopg
5
+ from dotenv import load_dotenv
6
+
7
+ load_dotenv()
8
+
9
+ ALLOWED_POSTGRES_PARAMS = {
10
+ "application_name",
11
+ "connect_timeout",
12
+ "dbname",
13
+ "fallback_application_name",
14
+ "gssencmode",
15
+ "host",
16
+ "hostaddr",
17
+ "keepalives",
18
+ "keepalives_count",
19
+ "keepalives_idle",
20
+ "keepalives_interval",
21
+ "load_balance_hosts",
22
+ "options",
23
+ "passfile",
24
+ "password",
25
+ "port",
26
+ "replication",
27
+ "require_auth",
28
+ "requiressl",
29
+ "service",
30
+ "sslcert",
31
+ "sslcompression",
32
+ "sslcrl",
33
+ "sslcrldir",
34
+ "sslkey",
35
+ "sslmode",
36
+ "sslnegotiation",
37
+ "sslpassword",
38
+ "sslrootcert",
39
+ "sslsni",
40
+ "target_session_attrs",
41
+ "tcp_user_timeout",
42
+ "user",
43
+ }
44
+
45
+
46
+ def get_postgres_url():
47
+ postgres_url = os.getenv("POSTGRES_URL")
48
+ if not postgres_url:
49
+ raise ValueError("POSTGRES_URL is not set")
50
+
51
+ return clean_postgres_url(postgres_url)
52
+
53
+
54
+ def clean_postgres_url(postgres_url):
55
+ parts = urlsplit(postgres_url.strip().strip("\"'"))
56
+ query = urlencode(
57
+ [
58
+ (key, value)
59
+ for key, value in parse_qsl(parts.query, keep_blank_values=True)
60
+ if key in ALLOWED_POSTGRES_PARAMS
61
+ ]
62
+ )
63
+ return urlunsplit((parts.scheme, parts.netloc, parts.path, query, parts.fragment))
64
+
65
+
66
+ def get_connection():
67
+ return psycopg.connect(get_postgres_url())