triflix commited on
Commit
6fe8a61
·
verified ·
1 Parent(s): 8a754bb

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +147 -147
main.py CHANGED
@@ -1,147 +1,147 @@
1
- from fastapi import FastAPI, Request, Form, Depends, HTTPException
2
- from fastapi.templating import Jinja2Templates
3
- from fastapi.staticfiles import StaticFiles
4
- from fastapi.responses import HTMLResponse, RedirectResponse
5
- from sqlalchemy import create_engine, Column, Integer, String, Boolean, DateTime, Text
6
- from sqlalchemy.ext.declarative import declarative_base
7
- from sqlalchemy.orm import sessionmaker, Session
8
- from datetime import datetime
9
- import re
10
- import os
11
- import bcrypt
12
- import random
13
- import string
14
- from typing import Optional
15
-
16
- # Define base URL (update for deployment)
17
- BASE_URL = "http://localhost:7860"
18
-
19
- # Create the FastAPI app
20
- app = FastAPI(title="Notepad App")
21
-
22
- # Set up templates and static files directories
23
- templates_dir = os.path.join(os.path.dirname(__file__), "templates")
24
- templates = Jinja2Templates(directory=templates_dir)
25
-
26
- # Create static directory if it doesn't exist
27
- static_dir = os.path.join(os.path.dirname(__file__), "static")
28
- os.makedirs(static_dir, exist_ok=True)
29
- app.mount("/static", StaticFiles(directory=static_dir), name="static")
30
-
31
- # Database setup
32
- DATABASE_URL = "sqlite:///./notepad.db"
33
- engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
34
- SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
35
- Base = declarative_base()
36
-
37
- # Define the Note model (custom slug stored in "code")
38
- class Note(Base):
39
- __tablename__ = "notes"
40
-
41
- id = Column(Integer, primary_key=True, index=True)
42
- code = Column(String, unique=True, index=True) # Custom slug
43
- content = Column(Text)
44
- is_private = Column(Boolean, default=False)
45
- password = Column(String, nullable=True)
46
- created_at = Column(DateTime, default=datetime.utcnow)
47
-
48
- # Create the database tables
49
- Base.metadata.create_all(bind=engine)
50
-
51
- # Dependency to get DB session
52
- def get_db():
53
- db = SessionLocal()
54
- try:
55
- yield db
56
- finally:
57
- db.close()
58
-
59
- # Function to detect image URLs in text and convert them to HTML img tags
60
- def process_content(content):
61
- image_pattern = r'(https?://\S+\.(?:jpg|jpeg|png|gif|webp|svg))'
62
- processed_content = re.sub(
63
- image_pattern,
64
- r'<img src="\1" class="my-2 max-w-full h-auto rounded" alt="Image">',
65
- content
66
- )
67
- processed_content = processed_content.replace('\n', '<br>')
68
- return processed_content
69
-
70
- # Function to generate a random code for auto-generated slug (6-character)
71
- def generate_random_code(length=6):
72
- characters = string.ascii_letters + string.digits
73
- return ''.join(random.choice(characters) for _ in range(length))
74
-
75
- # GET /{slug}: Check for note; if exists, show it; otherwise, show creation form
76
- @app.get("/{slug}", response_class=HTMLResponse)
77
- async def custom_slug(request: Request, slug: str, db: Session = Depends(get_db)):
78
- note = db.query(Note).filter(Note.code == slug).first()
79
- if note:
80
- if note.is_private:
81
- return templates.TemplateResponse("password.html", {"request": request, "slug": slug})
82
- processed_content = process_content(note.content)
83
- return templates.TemplateResponse("view.html", {"request": request, "note": note, "content": processed_content})
84
- else:
85
- # No note exists for this slug; show creation form with slug pre-set.
86
- return templates.TemplateResponse("index.html", {"request": request, "slug": slug})
87
-
88
- # POST /create: Create a note with the provided custom slug
89
- @app.post("/create", response_class=HTMLResponse)
90
- async def create_note(
91
- request: Request,
92
- slug: str = Form(...),
93
- content: str = Form(...),
94
- is_private: bool = Form(False),
95
- password: Optional[str] = Form(None),
96
- db: Session = Depends(get_db)
97
- ):
98
- # Check if slug already exists
99
- if db.query(Note).filter(Note.code == slug).first():
100
- raise HTTPException(status_code=400, detail="Slug already exists. Choose a different one.")
101
-
102
- hashed_password = None
103
- if is_private and password:
104
- hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
105
-
106
- new_note = Note(
107
- code=slug,
108
- content=content,
109
- is_private=is_private,
110
- password=hashed_password
111
- )
112
- db.add(new_note)
113
- db.commit()
114
-
115
- return RedirectResponse(url=f"{BASE_URL}/{slug}", status_code=303)
116
-
117
- # POST /{slug}/verify: Verify password for private notes
118
- @app.post("/{slug}/verify", response_class=HTMLResponse)
119
- async def verify_password(
120
- request: Request,
121
- slug: str,
122
- password: str = Form(...),
123
- db: Session = Depends(get_db)
124
- ):
125
- note = db.query(Note).filter(Note.code == slug).first()
126
- if not note:
127
- raise HTTPException(status_code=404, detail="Note not found")
128
-
129
- if not note.is_private or not note.password:
130
- return RedirectResponse(url=f"{BASE_URL}/{slug}", status_code=303)
131
-
132
- is_valid = bcrypt.checkpw(password.encode('utf-8'), note.password.encode('utf-8'))
133
- if not is_valid:
134
- return templates.TemplateResponse("password.html", {"request": request, "slug": slug, "error": "Invalid password", "error_effect": True})
135
-
136
- processed_content = process_content(note.content)
137
- return templates.TemplateResponse("view.html", {"request": request, "note": note, "content": processed_content})
138
-
139
- # Landing page: If no slug provided, auto-generate one for the creation form
140
- @app.get("/", response_class=HTMLResponse)
141
- async def landing(request: Request):
142
- random_slug = generate_random_code()
143
- return templates.TemplateResponse("index.html", {"request": request, "slug": random_slug})
144
-
145
- if __name__ == "__main__":
146
- import uvicorn
147
- uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=True)
 
1
+ from fastapi import FastAPI, Request, Form, Depends, HTTPException
2
+ from fastapi.templating import Jinja2Templates
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.responses import HTMLResponse, RedirectResponse
5
+ from sqlalchemy import create_engine, Column, Integer, String, Boolean, DateTime, Text
6
+ from sqlalchemy.ext.declarative import declarative_base
7
+ from sqlalchemy.orm import sessionmaker, Session
8
+ from datetime import datetime
9
+ import re
10
+ import os
11
+ import bcrypt
12
+ import random
13
+ import string
14
+ from typing import Optional
15
+
16
+ # Define base URL (update for deployment)
17
+ BASE_URL = "https://triflix-slugs.hf.space"
18
+
19
+ # Create the FastAPI app
20
+ app = FastAPI(title="Notepad App")
21
+
22
+ # Set up templates and static files directories
23
+ templates_dir = os.path.join(os.path.dirname(__file__), "templates")
24
+ templates = Jinja2Templates(directory=templates_dir)
25
+
26
+ # Create static directory if it doesn't exist
27
+ static_dir = os.path.join(os.path.dirname(__file__), "static")
28
+ os.makedirs(static_dir, exist_ok=True)
29
+ app.mount("/static", StaticFiles(directory=static_dir), name="static")
30
+
31
+ # Database setup
32
+ DATABASE_URL = "sqlite:///./notepad.db"
33
+ engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
34
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
35
+ Base = declarative_base()
36
+
37
+ # Define the Note model (custom slug stored in "code")
38
+ class Note(Base):
39
+ __tablename__ = "notes"
40
+
41
+ id = Column(Integer, primary_key=True, index=True)
42
+ code = Column(String, unique=True, index=True) # Custom slug
43
+ content = Column(Text)
44
+ is_private = Column(Boolean, default=False)
45
+ password = Column(String, nullable=True)
46
+ created_at = Column(DateTime, default=datetime.utcnow)
47
+
48
+ # Create the database tables
49
+ Base.metadata.create_all(bind=engine)
50
+
51
+ # Dependency to get DB session
52
+ def get_db():
53
+ db = SessionLocal()
54
+ try:
55
+ yield db
56
+ finally:
57
+ db.close()
58
+
59
+ # Function to detect image URLs in text and convert them to HTML img tags
60
+ def process_content(content):
61
+ image_pattern = r'(https?://\S+\.(?:jpg|jpeg|png|gif|webp|svg))'
62
+ processed_content = re.sub(
63
+ image_pattern,
64
+ r'<img src="\1" class="my-2 max-w-full h-auto rounded" alt="Image">',
65
+ content
66
+ )
67
+ processed_content = processed_content.replace('\n', '<br>')
68
+ return processed_content
69
+
70
+ # Function to generate a random code for auto-generated slug (6-character)
71
+ def generate_random_code(length=6):
72
+ characters = string.ascii_letters + string.digits
73
+ return ''.join(random.choice(characters) for _ in range(length))
74
+
75
+ # GET /{slug}: Check for note; if exists, show it; otherwise, show creation form
76
+ @app.get("/{slug}", response_class=HTMLResponse)
77
+ async def custom_slug(request: Request, slug: str, db: Session = Depends(get_db)):
78
+ note = db.query(Note).filter(Note.code == slug).first()
79
+ if note:
80
+ if note.is_private:
81
+ return templates.TemplateResponse("password.html", {"request": request, "slug": slug})
82
+ processed_content = process_content(note.content)
83
+ return templates.TemplateResponse("view.html", {"request": request, "note": note, "content": processed_content})
84
+ else:
85
+ # No note exists for this slug; show creation form with slug pre-set.
86
+ return templates.TemplateResponse("index.html", {"request": request, "slug": slug})
87
+
88
+ # POST /create: Create a note with the provided custom slug
89
+ @app.post("/create", response_class=HTMLResponse)
90
+ async def create_note(
91
+ request: Request,
92
+ slug: str = Form(...),
93
+ content: str = Form(...),
94
+ is_private: bool = Form(False),
95
+ password: Optional[str] = Form(None),
96
+ db: Session = Depends(get_db)
97
+ ):
98
+ # Check if slug already exists
99
+ if db.query(Note).filter(Note.code == slug).first():
100
+ raise HTTPException(status_code=400, detail="Slug already exists. Choose a different one.")
101
+
102
+ hashed_password = None
103
+ if is_private and password:
104
+ hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
105
+
106
+ new_note = Note(
107
+ code=slug,
108
+ content=content,
109
+ is_private=is_private,
110
+ password=hashed_password
111
+ )
112
+ db.add(new_note)
113
+ db.commit()
114
+
115
+ return RedirectResponse(url=f"{BASE_URL}/{slug}", status_code=303)
116
+
117
+ # POST /{slug}/verify: Verify password for private notes
118
+ @app.post("/{slug}/verify", response_class=HTMLResponse)
119
+ async def verify_password(
120
+ request: Request,
121
+ slug: str,
122
+ password: str = Form(...),
123
+ db: Session = Depends(get_db)
124
+ ):
125
+ note = db.query(Note).filter(Note.code == slug).first()
126
+ if not note:
127
+ raise HTTPException(status_code=404, detail="Note not found")
128
+
129
+ if not note.is_private or not note.password:
130
+ return RedirectResponse(url=f"{BASE_URL}/{slug}", status_code=303)
131
+
132
+ is_valid = bcrypt.checkpw(password.encode('utf-8'), note.password.encode('utf-8'))
133
+ if not is_valid:
134
+ return templates.TemplateResponse("password.html", {"request": request, "slug": slug, "error": "Invalid password", "error_effect": True})
135
+
136
+ processed_content = process_content(note.content)
137
+ return templates.TemplateResponse("view.html", {"request": request, "note": note, "content": processed_content})
138
+
139
+ # Landing page: If no slug provided, auto-generate one for the creation form
140
+ @app.get("/", response_class=HTMLResponse)
141
+ async def landing(request: Request):
142
+ random_slug = generate_random_code()
143
+ return templates.TemplateResponse("index.html", {"request": request, "slug": random_slug})
144
+
145
+ if __name__ == "__main__":
146
+ import uvicorn
147
+ uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=True)