feat: add user_id index to profiles table for faster lookups

The index on user_id column in profiles table improves query performance for user-specific profile retrieval operations.
This commit is contained in:
2026-06-02 21:17:51 +00:00
parent 247b7d738e
commit 6bf41a5d84
30 changed files with 406 additions and 103 deletions
+2
View File
@@ -13,6 +13,8 @@ SESSION_MAX_AGE = 86400 * 7
PORT = 10500
SITE_URL = environ.get("DEVPLACE_SITE_URL", "").rstrip("/")
SERVICE_LOCK_FILE = BASE_DIR / "devplace-services.lock"
VAPID_PRIVATE_KEY_FILE = BASE_DIR / "notification-private.pem"
VAPID_PRIVATE_KEY_PKCS8_FILE = BASE_DIR / "notification-private.pkcs8.pem"
VAPID_PUBLIC_KEY_FILE = BASE_DIR / "notification-public.pem"
+6 -1
View File
@@ -75,13 +75,18 @@ def delete_content_item(table_name: str, target_type: str, user: dict, slug: str
item = resolve_by_slug(table, slug)
if is_owner(item, user):
delete_target_attachments(target_type, item["uid"])
comment_uids = []
if "comments" in db.tables:
comments = get_table("comments")
for comment in comments.find(target_uid=item["uid"]):
comment_uids.append(comment["uid"])
delete_target_attachments("comment", comment["uid"])
comments.delete(target_uid=item["uid"])
if "votes" in db.tables:
get_table("votes").delete(target_uid=item["uid"])
votes = get_table("votes")
votes.delete(target_uid=item["uid"])
if comment_uids:
votes.delete(votes.table.columns.target_uid.in_(comment_uids), target_type="comment")
if inline_image_field:
delete_inline_image(item.get(inline_image_field))
table.delete(id=item["id"])
+22 -4
View File
@@ -1,4 +1,5 @@
import asyncio
import fcntl
import logging
import os
import time
@@ -7,7 +8,7 @@ from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.exceptions import RequestValidationError
from devplacepy.config import STATIC_DIR, PORT
from devplacepy.config import STATIC_DIR, PORT, SERVICE_LOCK_FILE
from devplacepy.database import init_db, get_table, db, get_users_by_uids, get_comment_counts_by_post_uids, get_vote_counts, get_news_images_by_uids
from devplacepy.templating import templates
from devplacepy.utils import get_current_user, time_ago
@@ -26,6 +27,20 @@ _rate_limit_store = defaultdict(list)
RATE_LIMIT = int(os.environ.get("DEVPLACE_RATE_LIMIT", "60"))
RATE_WINDOW = 60
_service_lock_handle = None
def acquire_service_lock() -> bool:
global _service_lock_handle
handle = open(SERVICE_LOCK_FILE, "w")
try:
fcntl.flock(handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError:
handle.close()
return False
_service_lock_handle = handle
return True
class UploadStaticFiles(StaticFiles):
async def get_response(self, path, scope):
response = await super().get_response(path, scope)
@@ -145,9 +160,12 @@ async def startup():
from devplacepy.push import ensure_certificates
ensure_certificates()
if not os.environ.get("DEVPLACE_DISABLE_SERVICES"):
news_service = NewsService()
service_manager.register(news_service)
asyncio.create_task(service_manager.start_all())
if acquire_service_lock():
service_manager.register(NewsService())
asyncio.create_task(service_manager.start_all())
logger.info(f"Background services started in worker pid {os.getpid()}")
else:
logger.info(f"Worker pid {os.getpid()} declined service lock; another worker owns background services")
logger.info(f"DevPlace started on port {PORT}")
+2 -1
View File
@@ -7,7 +7,7 @@ from fastapi import APIRouter, Request, Form
from fastapi.responses import RedirectResponse, HTMLResponse
from devplacepy.database import get_table
from devplacepy.templating import templates
from devplacepy.utils import hash_password, verify_password, create_session, generate_uid, get_current_user, award_badge
from devplacepy.utils import hash_password, verify_password, create_session, generate_uid, get_current_user, award_badge, clear_session_cache
from devplacepy.seo import base_seo_context
from devplacepy.models import SignupForm, LoginForm, ForgotPasswordForm, ResetPasswordForm
@@ -205,6 +205,7 @@ async def logout(request: Request):
session = sessions.find_one(session_token=token)
if session:
sessions.delete(id=session["id"])
clear_session_cache(token)
response = RedirectResponse(url="/", status_code=302)
response.delete_cookie("session")
return response
+3
View File
@@ -152,6 +152,9 @@ async def send_message(request: Request, data: Annotated[MessageForm, Form()]):
content = data.content.strip()
receiver_uid = data.receiver_uid
if not get_table("users").find_one(uid=receiver_uid):
return RedirectResponse(url="/messages", status_code=302)
messages_table = get_table("messages")
msg_uid = generate_uid()
messages_table.insert({
+2 -2
View File
@@ -39,8 +39,8 @@ async def vote(request: Request, target_type: str, target_uid: str, data: Annota
})
did_upvote = value == 1
up_count = votes.count(target_uid=target_uid, value=1)
down_count = votes.count(target_uid=target_uid, value=-1)
up_count = votes.count(target_uid=target_uid, target_type=target_type, value=1)
down_count = votes.count(target_uid=target_uid, target_type=target_type, value=-1)
net = up_count - down_count
update_target_stars(target_type, target_uid, net)
+10 -3
View File
@@ -1,7 +1,14 @@
export class Avatar {
static imgHtml(username, size = 24) {
const url = `/avatar/multiavatar/${encodeURIComponent(username)}?size=${size}`;
return `<img src="${url}" class="avatar-img" style="width:${size}px;height:${size}px;border-radius:50%" alt="" loading="lazy">`;
static imgElement(username, size = 24) {
const img = document.createElement("img");
img.src = `/avatar/multiavatar/${encodeURIComponent(username)}?size=${size}`;
img.className = "avatar-img";
img.style.width = `${size}px`;
img.style.height = `${size}px`;
img.style.borderRadius = "50%";
img.alt = "";
img.loading = "lazy";
return img;
}
}
+3 -1
View File
@@ -75,7 +75,9 @@ export class MentionInput {
item.type = "button";
item.className = "mention-dropdown-item";
item.dataset.username = r.username;
item.innerHTML = Avatar.imgHtml(r.username) + "<span>@" + r.username + "</span>";
const label = document.createElement("span");
label.textContent = "@" + r.username;
item.append(Avatar.imgElement(r.username), label);
item.addEventListener("mousedown", (e) => {
e.preventDefault();
this.insert(r.username);
+3 -1
View File
@@ -41,7 +41,9 @@ export class MessageSearch {
const item = document.createElement("a");
item.className = "search-dropdown-item";
item.href = `/messages?with_uid=${r.uid}`;
item.innerHTML = `${Avatar.imgHtml(r.username)}<span>${r.username}</span>`;
const label = document.createElement("span");
label.textContent = r.username;
item.append(Avatar.imgElement(r.username), label);
dropdown.appendChild(item);
}
DomUtils.show(dropdown);
+2 -1
View File
@@ -34,6 +34,7 @@ export class ProfileEditor {
const tag = document.createElement("span");
tag.className = "platform-tag";
tag.textContent = val;
tag.dataset.value = val;
const remove = document.createElement("button");
remove.type = "button";
remove.textContent = "x";
@@ -57,7 +58,7 @@ export class ProfileEditor {
const updatePlatforms = () => {
const values = [];
tagsContainer.querySelectorAll(".platform-tag").forEach((t) => {
values.push(t.textContent.replace("x", "").trim());
values.push(t.dataset.value);
});
hiddenInput.value = values.join(",");
};
+1 -1
View File
@@ -20,7 +20,7 @@
<a href="/profile/{{ item.author['username'] if item.author else '#' }}">
<img src="{{ avatar_url('multiavatar', item.author['username'] if item.author else '?', 32) }}" class="avatar-img avatar-sm" alt="{{ item.author['username'] if item.author else '?' }}" loading="lazy">
</a>
<a href="/profile/{{ item.author['username'] if item.author else '#' }}" class="comment-author">{{ item.author['username'] if item.author else 'Unknown' }}</a>
{% set _user = item.author %}{% set _class = "comment-author" %}{% include "_user_link.html" %}
<span class="comment-time">{{ item.time_ago }}</span>
</div>
<div class="comment-text rendered-content" data-render>{{ item.comment['content'] }}</div>
+1 -1
View File
@@ -35,7 +35,7 @@
<form class="feed-comment-form" method="POST" action="/comments/create">
<input type="hidden" name="post_uid" value="{{ item.post['uid'] }}">
<img src="{{ avatar_url('multiavatar', user['username'], 24) }}" class="avatar-img avatar-24" alt="" loading="lazy">
<input type="text" name="content" placeholder="Your opinion goes here..." maxlength="1000" autocomplete="off">
<input type="text" name="content" placeholder="Your opinion goes here..." maxlength="1000" autocomplete="off" data-mention>
<button type="submit" class="feed-comment-submit">Post</button>
</form>
{% endif %}
+1 -1
View File
@@ -1,7 +1,7 @@
<div class="post-header">
{% set _user = _author %}{% set _size = 32 %}{% set _size_class = "sm" %}{% include "_avatar_link.html" %}
<div class="post-author-wrap">
<a href="/profile/{{ _author['username'] if _author else '#' }}" class="post-author-link">{{ _author['username'] if _author else 'Unknown' }}</a>
{% set _user = _author %}{% set _class = "post-author-link" %}{% include "_user_link.html" %}
{% if _author and _author.get('role') %}
<span class="post-author-role">{{ _author['role'] }}</span>
{% endif %}
+5
View File
@@ -0,0 +1,5 @@
{% if _user %}
<a href="/profile/{{ _user['username'] }}"{% if _class %} class="{{ _class }}"{% endif %}>{{ _user['username'] }}</a>
{% else %}
<a href="#"{% if _class %} class="{{ _class }}"{% endif %}>Unknown</a>
{% endif %}
+1 -1
View File
@@ -105,7 +105,7 @@
<div class="stat-row">
<span class="label">
{% set _user = author %}{% set _size = 20 %}{% set _size_class = "xs" %}{% include "_avatar_link.html" %}
<a href="/profile/{{ author['username'] }}" class="top-author-name">{{ author['username'] }}</a>
{% set _user = author %}{% set _class = "top-author-name" %}{% include "_user_link.html" %}
</span>
<span class="value">{{ author.get('stars', 0) }}</span>
</div>
+1 -1
View File
@@ -19,7 +19,7 @@
<div class="gist-detail-author">
{% set _size = 32 %}{% set _size_class = "sm" %}{% set _user = author %}{% include "_avatar_link.html" %}
<div>
<a href="/profile/{{ author['username'] if author else '#' }}">{{ author['username'] if author else 'Unknown' }}</a>
{% set _user = author %}{% set _class = none %}{% include "_user_link.html" %}
{% if author and author.get('role') %}
<span style="font-size: 0.75rem; color: var(--text-muted);">&middot; {{ author['role'] }}</span>
{% endif %}
+1 -1
View File
@@ -50,7 +50,7 @@
{% set _size_class = "sm" %}
{% include "_avatar_link.html" %}
<div class="landing-post-author-wrap">
<a href="/profile/{{ item.author['username'] }}" class="landing-post-author">{{ item.author['username'] }}</a>
{% set _user = item.author %}{% set _class = "landing-post-author" %}{% include "_user_link.html" %}
<span class="landing-post-time">{{ item.time_ago }}</span>
</div>
<span class="badge badge-{{ item.post['topic'] }}">{{ item.post['topic'] }}</span>
+2 -2
View File
@@ -28,7 +28,7 @@
<div class="stat-row">
<span class="label">
{% set _user = author %}{% set _size = 20 %}{% set _size_class = "xs" %}{% include "_avatar_link.html" %}
<a href="/profile/{{ author['username'] }}" class="top-author-name">{{ author['username'] }}</a>
{% set _user = author %}{% set _class = "top-author-name" %}{% include "_user_link.html" %}
</span>
<span class="value">{{ author.get('stars', 0) }}</span>
</div>
@@ -52,7 +52,7 @@
<li class="leaderboard-row{% if user and entry['uid'] == user['uid'] %} leaderboard-row-self{% endif %}">
<span class="leaderboard-rank">#{{ entry['rank'] }}</span>
{% set _user = entry %}{% set _size = 32 %}{% set _size_class = "sm" %}{% include "_avatar_link.html" %}
<a href="/profile/{{ entry['username'] }}" class="leaderboard-name">{{ entry['username'] }}</a>
{% set _user = entry %}{% set _class = "leaderboard-name" %}{% include "_user_link.html" %}
<span class="leaderboard-level">Level {{ entry.get('level', 1) }}</span>
<span class="leaderboard-stars">{{ entry['stars'] }} <span class="leaderboard-star-icon">&#x2605;</span></span>
</li>
+1 -1
View File
@@ -34,7 +34,7 @@
<a href="/profile/{{ other_user['username'] }}">
<img src="{{ avatar_url('multiavatar', other_user['username'], 32) }}" class="avatar-img avatar-sm" alt="{{ other_user['username'] }}" loading="lazy">
</a>
<a href="/profile/{{ other_user['username'] }}"><h3>{{ other_user['username'] }}</h3></a>
<h3>{% set _user = other_user %}{% set _class = none %}{% include "_user_link.html" %}</h3>
</div>
<div class="messages-thread">
+1 -1
View File
@@ -14,7 +14,7 @@
<img src="{{ avatar_url('multiavatar', author['username'] if author else '?', 40) }}" class="avatar-img avatar-md" alt="{{ author['username'] if author else '?' }}" loading="lazy">
</a>
<div>
<a href="/profile/{{ author['username'] if author else '#' }}" class="post-detail-author">{{ author['username'] if author else 'Unknown' }}</a>
{% set _user = author %}{% set _class = "post-detail-author" %}{% include "_user_link.html" %}
{% if author and author.get('role') %}
<span class="post-detail-role">&middot; {{ author['role'] }}</span>
{% endif %}
+1 -1
View File
@@ -28,7 +28,7 @@
<div class="project-detail-author">
{% set _size = 32 %}{% set _size_class = "sm" %}{% set _user = author %}{% include "_avatar_link.html" %}
<div>
<a href="/profile/{{ author['username'] if author else '#' }}">{{ author['username'] if author else 'Unknown' }}</a>
{% set _user = author %}{% set _class = none %}{% include "_user_link.html" %}
{% if author and author.get('role') %}
<span style="font-size: 0.75rem; color: var(--text-muted);">&middot; {{ author['role'] }}</span>
{% endif %}
+4
View File
@@ -43,6 +43,10 @@ def clear_user_cache(user_uid: str) -> None:
_user_cache.pop(token)
def clear_session_cache(token: str) -> None:
_user_cache.pop(token)
def get_current_user(request: Request):
token = request.cookies.get("session")
if not token: