from fastapi import FastAPI, Request, Form, Depends, HTTPException from fastapi.templating import Jinja2Templates from fastapi.staticfiles import StaticFiles from fastapi.responses import HTMLResponse, RedirectResponse from sqlalchemy import create_engine, Column, Integer, String, Boolean, DateTime, Text from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, Session from datetime import datetime import random import string import re import os import bcrypt from typing import Optional # Define your base URL BASE_URL = "https://triflix-notepad.hf.space" # Create the FastAPI app app = FastAPI(title="Notepad App") # Set up templates and static files directories templates_dir = os.path.join(os.path.dirname(__file__), "templates") templates = Jinja2Templates(directory=templates_dir) # Create static directory if it doesn't exist static_dir = os.path.join(os.path.dirname(__file__), "static") os.makedirs(static_dir, exist_ok=True) app.mount("/static", StaticFiles(directory=static_dir), name="static") # Database setup DATABASE_URL = "sqlite:///./notepad.db" engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() # Define the Note model class Note(Base): __tablename__ = "notes" id = Column(Integer, primary_key=True, index=True) code = Column(String, unique=True, index=True) content = Column(Text) is_private = Column(Boolean, default=False) password = Column(String, nullable=True) created_at = Column(DateTime, default=datetime.utcnow) # Create the database tables Base.metadata.create_all(bind=engine) # Dependency to get the database session def get_db(): db = SessionLocal() try: yield db finally: db.close() # Function to generate a random code for the note URL def generate_random_code(length=6): characters = string.ascii_letters + string.digits return ''.join(random.choice(characters) for _ in range(length)) # Function to detect image URLs in text and convert them to HTML img tags def process_content(content): # Regular expression to find image URLs image_pattern = r'(https?://\S+\.(?:jpg|jpeg|png|gif|webp|svg))' # Replace image URLs with img tags processed_content = re.sub( image_pattern, r'Image', content ) # Convert newlines to
tags for proper HTML rendering processed_content = processed_content.replace('\n', '
') return processed_content # Home page route @app.get("/", response_class=HTMLResponse) async def home(request: Request): return templates.TemplateResponse("index.html", {"request": request}) # Create note route @app.post("/create", response_class=HTMLResponse) async def create_note( request: Request, content: str = Form(...), is_private: bool = Form(False), password: Optional[str] = Form(None), db: Session = Depends(get_db) ): # Generate a unique random code code = generate_random_code() while db.query(Note).filter(Note.code == code).first(): code = generate_random_code() # Hash password if provided hashed_password = None if is_private and password: hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') # Create new note new_note = Note( code=code, content=content, is_private=is_private, password=hashed_password ) db.add(new_note) db.commit() # Redirect to the note view page using the BASE_URL return RedirectResponse(url=f"{BASE_URL}/{code}", status_code=303) # View note route @app.get("/{code}", response_class=HTMLResponse) async def view_note(request: Request, code: str, db: Session = Depends(get_db)): # Find the note by code note = db.query(Note).filter(Note.code == code).first() if not note: raise HTTPException(status_code=404, detail="Note not found") # If the note is private, show password prompt if note.is_private: return templates.TemplateResponse( "password.html", {"request": request, "code": code} ) # Process content to render images processed_content = process_content(note.content) return templates.TemplateResponse( "view.html", {"request": request, "note": note, "content": processed_content} ) # Verify password route @app.post("/{code}/verify", response_class=HTMLResponse) async def verify_password( request: Request, code: str, password: str = Form(...), db: Session = Depends(get_db) ): # Find the note by code note = db.query(Note).filter(Note.code == code).first() if not note: raise HTTPException(status_code=404, detail="Note not found") # Verify the password if not note.is_private or not note.password: return RedirectResponse(url=f"{BASE_URL}/{code}", status_code=303) is_valid = bcrypt.checkpw(password.encode('utf-8'), note.password.encode('utf-8')) if not is_valid: return templates.TemplateResponse( "password.html", {"request": request, "code": code, "error": "Invalid password"} ) # Process content to render images processed_content = process_content(note.content) return templates.TemplateResponse( "view.html", {"request": request, "note": note, "content": processed_content} ) # Run the application if __name__ == "__main__": import uvicorn # Running the app on host 0.0.0.0 and port 7860. uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=True)