Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Deploy App
run-name: Deploy Tiny App to Cloud Server

on:
push:
branches: [release]

jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/tz_apps/tiny
# Fetch latest code
git pull origin release

# Install dependencies (Production mode, no dev deps)
/root/.local/bin/poetry install --only main --sync

# Restart the service
sudo systemctl restart tiny.service
2 changes: 2 additions & 0 deletions .vscode/dictionaries/project-words.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
appleboy
projectx
RZRO
xkeshav
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ All notable changes to this repository will be documented in this file.

- Restructure folder structure
- added poetry dev script

## [1.0.0] Sun, Feb 15, 2026

- Added In-Memory cache strategy
- DB dependency optional
- Change UI
31 changes: 10 additions & 21 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from pathlib import Path
from typing import Optional

from fastapi import FastAPI, Form, Request
from fastapi import FastAPI, Form, Request, status
from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
Expand All @@ -28,7 +28,6 @@
from app.utils.qr import generate_qr_with_logo



# -----------------------------
# Lifespan: env + DB connect ONCE
# -----------------------------
Expand Down Expand Up @@ -103,37 +102,27 @@ async def create_short_url(
original_url: str = Form(""),
generate_qr: Optional[str] = Form(None),
qr_type: str = Form("short"),
):
) -> RedirectResponse:
session = request.session
qr_enabled = bool(generate_qr)
original_url = sanitize_url(original_url)

if not original_url:
session["error"] = "URL cannot be empty."
return RedirectResponse("/", status_code=303)

if not is_valid_url(original_url):
session["error"] = (
"Please enter a valid URL (must start with http:// or https://)."
)
return RedirectResponse("/", status_code=303)
# Basic validation (FastAPI can also handle this via Pydantic)
if not original_url or not is_valid_url(original_url):
session["error"] = "Please enter a valid URL."
return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)

# 1. Try Cache First
short_code: Optional[str] = get_short_from_cache(original_url)

if short_code:
session["info_message"] = "Already shortened before — fetched from cache."
else:
if not short_code:
# 2. Try Database
existing = db_data.find_by_original_url(original_url)
# Pull the value and check it in one go
db_code = existing.get("short_code") if existing else None
if isinstance(db_code, str):
short_code = db_code
set_cache_pair(short_code, original_url) # Cache it for future
session["info_message"] = (
"Already shortened before — fetched from database."
)
set_cache_pair(short_code, original_url) # Cache it for future requests

# 3. Generate New if still None
if not short_code:
Expand All @@ -147,7 +136,7 @@ async def create_short_url(
if not isinstance(short_code, str):
# This acts as a final safety net for production
session["error"] = "Internal server error: Code generation failed."
return RedirectResponse("/", status_code=303)
return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)

# Mypy now knows short_code is strictly 'str'
new_short_url = build_short_url(short_code, DOMAIN)
Expand All @@ -162,7 +151,7 @@ async def create_short_url(
}
)

return RedirectResponse("/", status_code=303)
return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)


@app.get("/recent", response_class=HTMLResponse)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "tiny"
version = "1.0.1"
version = "1.0.2"
description = "A package for URL Shortener with QR Code generation"
authors = [
{ name = "recursivezero", email = "152776938+recursivezero@users.noreply.github.com" },
Expand Down