Compare commits

..

No commits in common. "3e9a3d0769fd58ad8245370bb694c5065df1a34a" and "efcc4a14b6a0577d8ba4de1a5886b16bf009098a" have entirely different histories.

3 changed files with 128 additions and 161 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
*.db
uploads
merged_source_files.txt
__pycache__

95
main.py
View File

@ -24,7 +24,7 @@ app.add_middleware(
)
# Database setup
DB_PATH = "rant_community.db"
DB_PATH = "devrant_community.db"
UPLOAD_DIR = Path("uploads")
UPLOAD_DIR.mkdir(exist_ok=True)
@ -145,7 +145,7 @@ def hash_password(password: str) -> str:
def generate_token() -> str:
return secrets.token_urlsafe(32)
async def DELETE_get_current_user(token_id: Optional[int] = Form(None),
async def get_current_user(token_id: Optional[int] = Form(None),
token_key: Optional[str] = Form(None),
user_id: Optional[int] = Form(None)):
if not all([token_id, token_key, user_id]):
@ -318,7 +318,7 @@ async def login(
}
}
@app.get("/api/rant/rants")
@app.get("/api/devrant/rants")
async def get_rants(
sort: str = "recent",
limit: int = 20,
@ -328,7 +328,7 @@ async def get_rants(
token_key: Optional[str] = None,
user_id: Optional[int] = None
):
current_user_id = await authenticate_user(token_id, token_key, user_id) if token_id else None
current_user_id = await get_current_user(token_id, token_key, user_id) if token_id else None
# Get rants with user info
order_by = "r.created_time DESC" if sort == "recent" else "r.score DESC"
@ -384,7 +384,7 @@ async def get_rants(
}
}
@app.get("/api/rant/rants/{rant_id}")
@app.get("/api/devrant/rants/{rant_id}")
async def get_rant(
rant_id: int,
app: int = 3,
@ -393,7 +393,7 @@ async def get_rant(
token_key: Optional[str] = None,
user_id: Optional[int] = None
):
current_user_id = await authenticate_user(token_id, token_key, user_id) if token_id else None
current_user_id = await get_current_user(token_id, token_key, user_id) if token_id else None
# Get rant with user info
rant_row = await db.query_one(
@ -474,7 +474,7 @@ async def get_rant(
"subscribed": subscribed
}
@app.post("/api/rant/rants")
@app.post("/api/devrant/rants")
async def create_rant(
rant: str = Form(...),
tags: str = Form(...),
@ -485,7 +485,7 @@ async def create_rant(
user_id: int = Form(...),
image: Optional[UploadFile] = File(None)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -499,7 +499,7 @@ async def create_rant(
if duplicate:
return {
"success": False,
"error": "It looks like you just posted this same rant! Your connection might have timed out while posting so you might have seen an error, but sometimes the rant still gets posted and in this case it seems it did, so please check :) If this was not the case please contact info@rant.io. Thanks!"
"error": "It looks like you just posted this same rant! Your connection might have timed out while posting so you might have seen an error, but sometimes the rant still gets posted and in this case it seems it did, so please check :) If this was not the case please contact info@devrant.io. Thanks!"
}
# Handle image upload
@ -529,7 +529,7 @@ async def create_rant(
return {"success": True, "rant_id": rant_id}
@app.post("/api/rant/rants/{rant_id}")
@app.post("/api/devrant/rants/{rant_id}")
async def update_rant(
rant_id: int,
rant: str = Form(...),
@ -539,7 +539,7 @@ async def update_rant(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -560,7 +560,7 @@ async def update_rant(
return {"success": True}
@app.delete("/api/rant/rants/{rant_id}")
@app.delete("/api/devrant/rants/{rant_id}")
async def delete_rant(
rant_id: int,
app: int = 3,
@ -568,7 +568,7 @@ async def delete_rant(
token_key: str = None,
user_id: int = None
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -589,7 +589,7 @@ async def delete_rant(
return {"success": True}
@app.post("/api/rant/rants/{rant_id}/vote")
@app.post("/api/devrant/rants/{rant_id}/vote")
async def vote_rant(
rant_id: int,
vote: int = Form(...),
@ -599,7 +599,7 @@ async def vote_rant(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -688,7 +688,7 @@ async def vote_rant(
"rant": await format_rant(rant_data, user_data, current_user_id)
}
@app.post("/api/rant/rants/{rant_id}/favorite")
@app.post("/api/devrant/rants/{rant_id}/favorite")
async def favorite_rant(
rant_id: int,
app: int = Form(3),
@ -696,7 +696,7 @@ async def favorite_rant(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -709,7 +709,7 @@ async def favorite_rant(
except Exception:
return {"success": False, "error": "Already favorited"}
@app.post("/api/rant/rants/{rant_id}/unfavorite")
@app.post("/api/devrant/rants/{rant_id}/unfavorite")
async def unfavorite_rant(
rant_id: int,
app: int = Form(3),
@ -717,7 +717,7 @@ async def unfavorite_rant(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -728,7 +728,7 @@ async def unfavorite_rant(
return {"success": True}
@app.post("/api/rant/rants/{rant_id}/comments")
@app.post("/api/devrant/rants/{rant_id}/comments")
async def create_comment(
rant_id: int,
comment: str = Form(...),
@ -738,7 +738,7 @@ async def create_comment(
user_id: int = Form(...),
image: Optional[UploadFile] = File(None)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "confirmed": False}
@ -792,7 +792,7 @@ async def get_comment(
token_key: Optional[str] = None,
user_id: Optional[int] = None
):
current_user_id = await authenticate_user(token_id, token_key, user_id) if token_id else None
current_user_id = await get_current_user(token_id, token_key, user_id) if token_id else None
row = await db.query_one(
"""SELECT c.*, u.id as user_id, u.username, u.score as user_score,
@ -835,7 +835,7 @@ async def update_comment(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -864,7 +864,7 @@ async def delete_comment(
token_key: str = None,
user_id: int = None
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -893,7 +893,7 @@ async def vote_comment(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -952,7 +952,7 @@ async def get_profile(
token_key: Optional[str] = None,
auth_user_id: Optional[int] = None
):
current_user_id = await authenticate_user(token_id, token_key, auth_user_id) if token_id else None
current_user_id = await get_current_user(token_id, token_key, auth_user_id) if token_id else None
# Get user
user = await db.get("users", {"id": user_id})
@ -1097,7 +1097,7 @@ async def get_user_id(
return {"success": True, "user_id": user['id']}
@app.get("/api/rant/search")
@app.get("/api/devrant/search")
async def search(
term: str,
app: int = 3,
@ -1105,7 +1105,7 @@ async def search(
token_key: Optional[str] = None,
user_id: Optional[int] = None
):
current_user_id = await authenticate_user(token_id, token_key, user_id) if token_id else None
current_user_id = await get_current_user(token_id, token_key, user_id) if token_id else None
# Search rants
rows = await db.query_raw(
@ -1148,12 +1148,11 @@ async def get_notifications(
ext_prof: int = 1,
last_time: Optional[int] = None,
app: int = 3,
token_id: Optional[int] = None,
token_key: Optional[str] = None,
user_id: Optional[int] = None
token_id: int = None,
token_key: str = None,
user_id: int = None
):
# Use the generic authenticate_user function
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -1186,8 +1185,7 @@ async def get_notifications(
if not row['read']:
unread_count += 1
# Mark notifications as read
if rows: # Only update if there are notifications
# Mark as read
await db.update("notifications", {"read": 1}, {"user_id": current_user_id})
return {
@ -1208,25 +1206,6 @@ async def get_notifications(
}
}
async def authenticate_user(token_id: Optional[int] = None,
token_key: Optional[str] = None,
user_id: Optional[int] = None):
"""Generic authentication function that works with any parameter source"""
if not all([token_id, token_key, user_id]):
return None
token = await db.get("auth_tokens", {
"id": token_id,
"token_key": token_key,
"user_id": user_id
})
if not token or token['expire_time'] <= int(datetime.now().timestamp()):
return None
return user_id
@app.delete("/api/users/me/notif-feed")
async def clear_notifications(
app: int = Form(3),
@ -1234,7 +1213,7 @@ async def clear_notifications(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -1254,7 +1233,7 @@ async def edit_profile(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -1283,7 +1262,7 @@ async def resend_confirmation(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}
@ -1298,7 +1277,7 @@ async def mark_news_read(
token_key: str = Form(...),
user_id: int = Form(...)
):
current_user_id = await authenticate_user(token_id, token_key, user_id)
current_user_id = await get_current_user(token_id, token_key, user_id)
if not current_user_id:
return {"success": False, "error": "Authentication required"}

View File

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rant Community</title>
<title>DevRant Community</title>
<style>
* {
margin: 0;
@ -555,7 +555,7 @@
<!-- Navigation -->
<nav>
<div class="nav-container">
<a href="#" class="logo" onclick="showFeed()">Rant</a>
<a href="#" class="logo" onclick="showFeed()">DevRant</a>
<div class="nav-links">
<a href="#" onclick="showFeed()">Feed</a>
<a href="#" onclick="showSearch()">Search</a>
@ -636,9 +636,8 @@
document.getElementById('createRantBtn').style.display = isLoggedIn ? 'flex' : 'none';
}
async function apiCall(endpoint, options = {}) {
let url = `${API_URL}${endpoint}`;
const url = `${API_URL}${endpoint}`;
// Add auth to FormData or URLSearchParams if logged in
if (currentUser && options.body) {
@ -658,7 +657,7 @@ async function apiCall(endpoint, options = {}) {
// Add auth to query params for GET requests
if (currentUser && (options.method === 'GET' || !options.method)) {
const separator = endpoint.includes('?') ? '&' : '?';
url += `${separator}app=${APP_ID}&token_id=${currentUser.token_id}&token_key=${currentUser.token_key}&user_id=${currentUser.id}`;
endpoint += `${separator}app=${APP_ID}&token_id=${currentUser.token_id}&token_key=${currentUser.token_key}&user_id=${currentUser.id}`;
}
try {
@ -671,7 +670,6 @@ async function apiCall(endpoint, options = {}) {
}
}
// View functions
async function showFeed(sort = 'recent') {
currentView = 'feed';
@ -691,7 +689,7 @@ async function apiCall(endpoint, options = {}) {
`;
const params = new URLSearchParams({ sort, limit: 50, skip: 0, app: APP_ID });
const data = await apiCall(`/rant/rants?${params}`);
const data = await apiCall(`/devrant/rants?${params}`);
if (data.success) {
content.innerHTML = `
@ -753,7 +751,7 @@ async function apiCall(endpoint, options = {}) {
`;
const params = new URLSearchParams({ app: APP_ID });
const data = await apiCall(`/rant/rants/${rantId}?${params}`);
const data = await apiCall(`/devrant/rants/${rantId}?${params}`);
if (data.success) {
const rant = data.rant;
@ -972,7 +970,7 @@ async function apiCall(endpoint, options = {}) {
`;
const params = new URLSearchParams({ term, app: APP_ID });
const data = await apiCall(`/rant/search?${params}`);
const data = await apiCall(`/devrant/search?${params}`);
if (data.success) {
if (data.results.length === 0) {
@ -996,9 +994,9 @@ async function showNotifications() {
const params = new URLSearchParams({
ext_prof: 1,
last_time: Math.floor(Date.now() / 1000) - 86400
last_time: Math.floor(Date.now() / 1000) - 86400,
app: APP_ID
});
const data = await apiCall(`/users/me/notif-feed?${params}`);
if (data.success) {
@ -1008,7 +1006,7 @@ async function showNotifications() {
<h2>Notifications</h2>
${items.length === 0 ? '<p style="text-align: center; color: var(--text-dim); margin-top: 2rem;">No notifications</p>' : ''}
${items.map(notif => `
<div class="rant-card" onclick="showRant(${notif.rant_id})" style="cursor: pointer;">
<div class="rant-card" onclick="showRant(${notif.rant_id})">
<p><strong>${notif.username}</strong> ${notif.type === 'comment' ? 'commented on your rant' : 'mentioned you'}</p>
<p style="color: var(--text-dim); font-size: 0.9rem;">${formatTime(notif.created_time)}</p>
</div>
@ -1017,25 +1015,17 @@ async function showNotifications() {
// Update notification count
updateNotificationCount(0);
} else {
content.innerHTML = `
<h2>Notifications</h2>
<p style="text-align: center; color: var(--error); margin-top: 2rem;">Failed to load notifications: ${data.error || 'Unknown error'}</p>
`;
}
}
async function checkNotifications() {
if (!currentUser) return;
const params = new URLSearchParams({
ext_prof: 1,
last_time: Math.floor(Date.now() / 1000) - 86400
last_time: Math.floor(Date.now() / 1000) - 86400,
app: APP_ID
});
const data = await apiCall(`/users/me/notif-feed?${params}`);
if (data.success) {
@ -1043,7 +1033,6 @@ async function checkNotifications() {
}
}
function updateNotificationCount(count) {
const notifCount = document.getElementById('notifCount');
if (count > 0) {
@ -1230,7 +1219,7 @@ async function checkNotifications() {
event.preventDefault();
const formData = new FormData(event.target);
const data = await apiCall('/rant/rants', {
const data = await apiCall('/devrant/rants', {
method: 'POST',
body: formData
});
@ -1255,7 +1244,7 @@ async function checkNotifications() {
formData.append('reason', 0); // Not for me
}
const data = await apiCall(`/rant/rants/${rantId}/vote`, {
const data = await apiCall(`/devrant/rants/${rantId}/vote`, {
method: 'POST',
body: formData
});
@ -1300,7 +1289,7 @@ async function checkNotifications() {
const endpoint = isFavorited ? 'unfavorite' : 'favorite';
const formData = new FormData();
const data = await apiCall(`/rant/rants/${rantId}/${endpoint}`, {
const data = await apiCall(`/devrant/rants/${rantId}/${endpoint}`, {
method: 'POST',
body: formData
});
@ -1314,7 +1303,7 @@ async function checkNotifications() {
event.preventDefault();
const formData = new FormData(event.target);
const data = await apiCall(`/rant/rants/${rantId}/comments`, {
const data = await apiCall(`/devrant/rants/${rantId}/comments`, {
method: 'POST',
body: formData
});
@ -1333,7 +1322,7 @@ async function checkNotifications() {
params.append('token_key', currentUser.token_key);
params.append('user_id', currentUser.id);
const data = await apiCall(`/rant/rants/${rantId}?${params}`, {
const data = await apiCall(`/devrant/rants/${rantId}?${params}`, {
method: 'DELETE'
});