Corin1998 commited on
Commit
2c102d1
·
verified ·
1 Parent(s): df249b3

Update src/main.py

Browse files
Files changed (1) hide show
  1. src/main.py +28 -14
src/main.py CHANGED
@@ -1,8 +1,9 @@
 
1
  import os
2
  from fastapi import FastAPI, HTTPException, Response, Request
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from fastapi.staticfiles import StaticFiles
5
- from fastapi.responses import StreamingResponse, JSONResponse, RedirectResponse
6
  from sqlmodel import select
7
  from dotenv import load_dotenv
8
 
@@ -19,6 +20,7 @@ load_dotenv()
19
 
20
  app = FastAPI(title="Checkin-CRM")
21
 
 
22
  origins = os.getenv("CORS_ALLOW_ORIGINS", "*").split(",")
23
  app.add_middleware(
24
  CORSMiddleware,
@@ -28,22 +30,25 @@ app.add_middleware(
28
  allow_headers=["*"],
29
  )
30
 
31
- # static
32
  app.mount("/static", StaticFiles(directory="static"), name="static")
33
 
34
  @app.on_event("startup")
35
  def on_startup():
 
36
  init_db()
37
 
38
- # ここを UI に転送(管理画面)
39
  @app.get("/")
40
  def root_redirect():
41
  return RedirectResponse(url="/static/admin.html", status_code=307)
42
 
43
- # --- 管理まわり(簡易・デモ用) ---
 
 
44
  @app.post("/api/admin/venues")
45
  def create_venue(vc: VenueCreate):
46
- # デモではトークン検査を省略(本番は認証を入れてください)
47
  with get_session() as s:
48
  exists = s.exec(select(Venue).where(Venue.slug == vc.slug)).first()
49
  if exists:
@@ -68,7 +73,9 @@ def seed_demo():
68
  s.add(v); s.commit(); s.refresh(v)
69
  return {"id": v.id, "name": v.name, "slug": v.slug, "created": True}
70
 
71
- # --- チェックイン発行 ---
 
 
72
  @app.post("/api/checkin/session")
73
  def create_session(sc: SessionCreate):
74
  with get_session() as s:
@@ -91,15 +98,22 @@ def get_session_info(session_id: str):
91
  venue = s.get(Venue, cs.venue_id)
92
  return SessionInfo(session_id=session_id, venue_name=venue.name, nonce=cs.nonce, expires_at=cs.expires_at)
93
 
94
- # 絶対URLを埋め込んだ QR を返す(スマホのQRアプリからでも遷移できる
95
  @app.get("/qrcode/{session_id}")
96
  def qrcode_png(session_id: str, request: Request):
97
- base = str(request.base_url).rstrip("/") # https://hogefuga.hf.space
98
  url = f"{base}/checkin/{session_id}"
99
  png = make_qr_png(url)
100
- return StreamingResponse(iter([png]), media_type="image/png")
101
-
102
- # チェックイン画面(静的HTMLにリダイレクト)
 
 
 
 
 
 
 
103
  @app.get("/checkin/{session_id}")
104
  def checkin_page(session_id: str):
105
  return Response(status_code=307, headers={"Location": f"/static/index.html?session={session_id}"})
@@ -147,7 +161,9 @@ def verify_and_reward(payload: VerifyPayload):
147
 
148
  return VerifyResult(ok=True, message="Check-in success", points_awarded=POINTS_PER_CHECKIN)
149
 
150
- # 残高
 
 
151
  @app.get("/api/wallet/{address}/balance", response_model=Balance)
152
  def get_balance(address: str):
153
  with get_session() as s:
@@ -157,12 +173,10 @@ def get_balance(address: str):
157
  bal = s.exec(select(TokenBalance).where(TokenBalance.user_id == user.id, TokenBalance.symbol == "POINT")).first()
158
  return Balance(address=address, balance=bal.amount if bal else 0)
159
 
160
- # 共有URL
161
  @app.get("/api/share")
162
  def get_share(text: str, url: str):
163
  return JSONResponse(share_urls(text, url))
164
 
165
- # CRM
166
  @app.get("/api/crm/segments")
167
  def crm_segments():
168
  with get_session() as s:
 
1
+ # src/main.py
2
  import os
3
  from fastapi import FastAPI, HTTPException, Response, Request
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from fastapi.staticfiles import StaticFiles
6
+ from fastapi.responses import JSONResponse, RedirectResponse, Response as FastAPIResponse
7
  from sqlmodel import select
8
  from dotenv import load_dotenv
9
 
 
20
 
21
  app = FastAPI(title="Checkin-CRM")
22
 
23
+ # CORS(必要に応じて制限)
24
  origins = os.getenv("CORS_ALLOW_ORIGINS", "*").split(",")
25
  app.add_middleware(
26
  CORSMiddleware,
 
30
  allow_headers=["*"],
31
  )
32
 
33
+ # 静的ファイル(UI)
34
  app.mount("/static", StaticFiles(directory="static"), name="static")
35
 
36
  @app.on_event("startup")
37
  def on_startup():
38
+ # ディレクトリ作成やパスの可否は db.py 側で吸収。ここはテーブル作成のみ。
39
  init_db()
40
 
41
+ # ルートは管理UIへ
42
  @app.get("/")
43
  def root_redirect():
44
  return RedirectResponse(url="/static/admin.html", status_code=307)
45
 
46
+ # -------------------------
47
+ # 管理系(簡易デモ用)
48
+ # -------------------------
49
  @app.post("/api/admin/venues")
50
  def create_venue(vc: VenueCreate):
51
+ # デモ化のため認証なし(本番は認証を追加してください)
52
  with get_session() as s:
53
  exists = s.exec(select(Venue).where(Venue.slug == vc.slug)).first()
54
  if exists:
 
73
  s.add(v); s.commit(); s.refresh(v)
74
  return {"id": v.id, "name": v.name, "slug": v.slug, "created": True}
75
 
76
+ # -------------------------
77
+ # チェックイン発行〜検証
78
+ # -------------------------
79
  @app.post("/api/checkin/session")
80
  def create_session(sc: SessionCreate):
81
  with get_session() as s:
 
98
  venue = s.get(Venue, cs.venue_id)
99
  return SessionInfo(session_id=session_id, venue_name=venue.name, nonce=cs.nonce, expires_at=cs.expires_at)
100
 
101
+ # QR画像(絶対URLを埋め込み/キャッシュ抑止/埋め込み環境でも安定描画
102
  @app.get("/qrcode/{session_id}")
103
  def qrcode_png(session_id: str, request: Request):
104
+ base = str(request.base_url).rstrip("/") # 例: https://xxx.hf.space
105
  url = f"{base}/checkin/{session_id}"
106
  png = make_qr_png(url)
107
+ return FastAPIResponse(
108
+ content=png,
109
+ media_type="image/png",
110
+ headers={
111
+ "Cache-Control": "no-store, max-age=0",
112
+ "Content-Disposition": f'inline; filename="checkin_{session_id}.png"',
113
+ },
114
+ )
115
+
116
+ # チェックイン画面(利用者UIへリダイレクト)
117
  @app.get("/checkin/{session_id}")
118
  def checkin_page(session_id: str):
119
  return Response(status_code=307, headers={"Location": f"/static/index.html?session={session_id}"})
 
161
 
162
  return VerifyResult(ok=True, message="Check-in success", points_awarded=POINTS_PER_CHECKIN)
163
 
164
+ # -------------------------
165
+ # ウォレット/共有/CRM
166
+ # -------------------------
167
  @app.get("/api/wallet/{address}/balance", response_model=Balance)
168
  def get_balance(address: str):
169
  with get_session() as s:
 
173
  bal = s.exec(select(TokenBalance).where(TokenBalance.user_id == user.id, TokenBalance.symbol == "POINT")).first()
174
  return Balance(address=address, balance=bal.amount if bal else 0)
175
 
 
176
  @app.get("/api/share")
177
  def get_share(text: str, url: str):
178
  return JSONResponse(share_urls(text, url))
179
 
 
180
  @app.get("/api/crm/segments")
181
  def crm_segments():
182
  with get_session() as s: