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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ logs/
log/


.vscode/
.idea/
*.swp
*.swo
Expand Down
1 change: 1 addition & 0 deletions .vscode/dictionaries/project-words.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
APIHTTP
appleboy
projectx
RZRO
Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ All notable changes to this repository will be documented in this file.

- Added Database connection retry logic

## [1.1.1] THu, Mar 12, 2026
## [1.1.1] Thu, Mar 12, 2026

- Added Memory Cache
- Redesign Page

## [1.1.2] Sat, Mar 14, 2026

- Mobile responsiveness
- Added Contact us Page
34 changes: 21 additions & 13 deletions app/routes.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,52 @@
import os
from datetime import datetime, timezone
from typing import Optional
from app.utils.cache import list_cache_clean, clear_cache

from fastapi import (
APIRouter,
Form,
Request,
status,
HTTPException,
BackgroundTasks,
Form,
Header,
HTTPException,
Query,
Request,
status,
)
from fastapi.responses import (
HTMLResponse,
JSONResponse,
PlainTextResponse,
RedirectResponse,
JSONResponse,
)
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel, Field


from app import __version__
from app.utils import db
from app.utils.cache import (
clear_cache,
get_from_cache,
get_recent_from_cache,
get_short_from_cache,
set_cache_pair,
increment_visit_cache,
url_cache,
list_cache_clean,
remove_cache_key,
rev_cache,
set_cache_pair,
url_cache,
)
from app.utils.config import (
CACHE_PURGE_TOKEN,
DOMAIN,
MAX_RECENT_URLS,
CACHE_PURGE_TOKEN,
QR_DIR,
)
from app.utils.helper import (
generate_code,
sanitize_url,
is_valid_url,
authorize_url,
format_date,
generate_code,
is_valid_url,
sanitize_url,
)
from app.utils.qr import generate_qr_with_logo

Expand Down Expand Up @@ -112,12 +113,14 @@ async def create_short_url(

if not original_url or not is_valid_url(original_url): # validate the URL
session["error"] = "Please enter a valid URL."
session["original_url"] = original_url # preserve user input
return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)

if not authorize_url(
original_url
): # authorize the URL based on whitelist/blacklist
session["error"] = "This domain is not allowed."
session["original_url"] = original_url # preserve user input
return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)

short_code: Optional[str] = get_short_from_cache(original_url)
Expand Down Expand Up @@ -150,6 +153,11 @@ async def create_short_url(
return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)


@ui_router.get("/contact", response_class=HTMLResponse)
async def contact(request: Request):
return templates.TemplateResponse("contact.html", {"request": request})


@ui_router.get("/history", response_class=HTMLResponse)
async def recent_urls(request: Request):
recent_urls_list = db.get_recent_urls(MAX_RECENT_URLS) or get_recent_from_cache(
Expand Down
124 changes: 103 additions & 21 deletions app/static/css/tiny.css
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
html,
body {
height: 100%;
margin: 0;
font-family: Arial;
padding: 0;
font-family: "Poppins", system-ui, Arial, sans-serif;
background: var(--bg);
background-size: cover;
background-position: center;
background-size: cover;
background-position: center;
*,
*::before,
*::after {
box-sizing: border-box;
}

:root {
Expand All @@ -27,6 +19,11 @@ body {
font-family: "Inter", system-ui, sans-serif;
margin: 0;
overflow-x: hidden;
background-image: radial-gradient(circle at 50% -20%, #1e1e2e 0%, transparent 50%);
}

a {
color: var(--accent);
}

body.light-theme {
Expand All @@ -47,6 +44,16 @@ body.light-theme {
min-height: 100vh;
}

.main-layout {
max-width: 870px;
margin: 0 auto;
padding: 6rem 1rem 4rem;
display: flex;
flex-direction: column;
gap: 2rem;
flex: 1;
}

.main-layout {
width: 90%;
margin: 2rem auto;
Expand All @@ -63,7 +70,6 @@ body.light-theme {
justify-content: space-between;
padding: 0 10px;
box-sizing: border-box;

background: var(--glass);
border-bottom: 1px solid var(--glass-border);
z-index: 1000;
Expand Down Expand Up @@ -103,7 +109,9 @@ body.light-theme .app-header {
.header-nav {
display: flex;
gap: 26px;
margin: 0 auto;
flex-wrap: wrap;
white-space: nowrap;
flex-shrink: 1;
}

.nav-link,
Expand Down Expand Up @@ -175,9 +183,49 @@ body.dark-theme .app-header {
color: var(--accent);
}

/* hamburger hidden on desktop */

.hamburger {
display: none;
font-size: 22px;
background: transparent;
border: none;
color: var(--text-primary);
cursor: pointer;
}

@media (max-width: 600px) {
.logo {
font-size: 1.2rem;
.app-logo {
transform: scale(0.9);
}

.app-name {
font-size: 1.1rem;
}
}

@media (max-width: 700px) {
.hamburger {
display: block;
}

.header-nav {
display: none;
position: fixed;
top: 55px;
left: 0;
right: 0;
width: 100%;
flex-direction: column;
background: var(--bg);
padding: 20px;
display: none;
gap: 16px;
border-bottom: 1px solid var(--glass-border);
}

.header-nav.open {
display: flex;
}
}

Expand Down Expand Up @@ -275,6 +323,10 @@ body.dark-theme .app-header {
gap: 1.5rem;
}

.short-url {
max-width: 100%;
}

.qr-image {
width: 80px;
height: 80px;
Expand All @@ -295,6 +347,8 @@ body.dark-theme .app-header {
font-weight: 700;
color: var(--text-primary);
text-decoration: none;
word-break: break-all;
overflow-wrap: anywhere;
}

.result-actions {
Expand Down Expand Up @@ -388,6 +442,21 @@ body.dark-theme .app-header {
font-weight: 700;
}

@media (max-width: 768px) {
.input-wrapper {
flex-direction: column;
}

.btn-primary {
width: 100%;
}

.scroll-container {
padding-left: 12px;
padding-right: 12px;
}
}

/* ===============================
MODERN GLASS RECENT TABLE
================================= */
Expand All @@ -402,6 +471,7 @@ body.dark-theme .app-header {
.recent-table-wrapper {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}

/* ===============================
Expand All @@ -414,7 +484,7 @@ body.dark-theme .app-header {
border-radius: 12px;
overflow: hidden;
table-layout: fixed;
min-width: 800px;
min-width: 720px;
}

.recent-table thead {
Expand Down Expand Up @@ -590,10 +660,7 @@ footer.big-footer {
background: var(--bg);
border-top: 1px solid var(--glass-border);
padding: 4rem 1rem 2rem;
margin-top: 4rem;
position: fixed;
bottom: 0;
width: 100%;
margin-top: auto;
}

.footer-grid {
Expand Down Expand Up @@ -777,3 +844,18 @@ body.light-theme .history-link {
.history-link:hover {
opacity: 0.7;
}

.contact-info {
max-width: 600px;
margin: 0 auto;
background: var(--glass);
border: 1px solid var(--glass-border);
border-radius: 1rem;
padding: 2rem;
list-style-type: none;
& li {
margin-bottom: 1rem;
font-size: 1.1rem;
color: var(--text-primary);
}
}
6 changes: 3 additions & 3 deletions app/templates/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<html>

<head>
<title>Page Not Found</title>
<title>rzro.link: Page Not Found</title>
<style>
body {
margin: 0;
Expand Down Expand Up @@ -41,8 +41,8 @@
<h1>🚫 Page not found</h1>
<p>The page you are looking for does not exist.</p>

<a href="/" onclick="history.back(); return false;">
← Go Back
<a href="/">
← Go Home
</a>
</div>
</body>
Expand Down
14 changes: 14 additions & 0 deletions app/templates/contact.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% extends "layout.html" %} {% block content %}
<div class="main-layout">
<div class="contact-card">
<h1>Contact Us</h1>
<p>If you have any questions, feedback, or need assistance, feel free to reach out to us!</p>
<ul class="contact-info">
<li><strong>Email:</strong> <a href="mailto:hello@recursivezero.com">hello@recursivezero.com</a></li>
<li><strong>Twitter:</strong> <a href="https://twitter.com/recursivezero" target="_blank">@recursivezero</a></li>
<li><strong>GitHub:</strong> <a href="https://github.com/recursivezero/tiny/issues" target="_blank">GitHub
Issues</a></li>
</ul>
</div>
</div>
{% endblock %}
2 changes: 0 additions & 2 deletions app/templates/footer.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ <h3>🔗 RZRO.link</h3>
<div class="footer-col">
<h4>Product</h4>
<ul>
<li><a href="cache/list">CACHE URLs</a></li>
<li><a href="#">API Documentation</a></li>
<li><a href="#">Custom Branded Links</a></li>
<li><a href="#">QR Code Engine</a></li>
Expand All @@ -15,7 +14,6 @@ <h4>Product</h4>
<div class="footer-col">
<h4>Support</h4>
<ul>
<li><a href="/docs"> UI docs</a></li>
<li><a href="#">Help Center</a></li>
<li><a href="#">System Status</a></li>
<li><a href="#">Contact Us</a></li>
Expand Down
Loading