Changes for messages added.

This commit is contained in:
Michael Woods
2025-12-24 20:38:28 -05:00
parent 2eb43ebcd1
commit f59c9a36e3
6 changed files with 166 additions and 2 deletions

View File

@@ -0,0 +1,33 @@
from fastapi import APIRouter, Path, Request, Depends, HTTPException
from fastapi.responses import HTMLResponse
from packetserver.http.dependencies import get_current_http_user
from packetserver.http.auth import HttpUser
from packetserver.http.server import templates
router = APIRouter(tags=["message-detail"])
@router.get("/dashboard/message/{msg_id}", response_class=HTMLResponse)
async def message_detail_page(
request: Request,
msg_id: str = Path(..., description="Message UUID as string"),
current_user: HttpUser = Depends(get_current_http_user)
):
# Reuse the existing API endpoint logic internally
from packetserver.http.routers.messages import get_message as api_get_message
# Call with mark_retrieved=True to auto-mark as read on view (optional—remove if you prefer manual)
message_data = await api_get_message(
msg_id=msg_id,
mark_retrieved=True,
current_user=current_user
)
return templates.TemplateResponse(
"message_detail.html",
{
"request": request,
"message": message_data,
"current_user": current_user.username
}
)

View File

@@ -1,5 +1,6 @@
# packetserver/http/routers/messages.py # packetserver/http/routers/messages.py
from fastapi import APIRouter, Depends, Query, HTTPException, Path from fastapi import APIRouter, Depends, Query, HTTPException, Path, Request
from fastapi.responses import HTMLResponse
from typing import Optional from typing import Optional
from datetime import datetime from datetime import datetime
from persistent.mapping import PersistentMapping from persistent.mapping import PersistentMapping
@@ -10,6 +11,8 @@ from pydantic import BaseModel, Field, validator
from packetserver.http.dependencies import get_current_http_user from packetserver.http.dependencies import get_current_http_user
from packetserver.http.auth import HttpUser from packetserver.http.auth import HttpUser
html_router = APIRouter(tags=["messages-html"])
router = APIRouter(prefix="/api/v1", tags=["messages"]) router = APIRouter(prefix="/api/v1", tags=["messages"])
# Simple request model (only allow setting to true) # Simple request model (only allow setting to true)
@@ -156,4 +159,53 @@ async def mark_message_retrieved(
mailbox._p_changed = True mailbox._p_changed = True
transaction.get().commit() transaction.get().commit()
return {"status": "marked_retrieved", "id": msg_id} return {"status": "marked_retrieved", "id": msg_id}
@html_router.get("/messages", response_class=HTMLResponse)
async def message_list_page(
request: Request,
msg_type: str = Query("all", description="Filter: received, sent, or all"),
limit: Optional[int] = Query(50, le=200),
current_user: HttpUser = Depends(get_current_http_user)
):
# Reuse the existing API endpoint logic directly (duplicate minimal code for simplicity)
from packetserver.runners.http_server import get_db_connection
from ..server import templates
conn = get_db_connection()
root = conn.root()
mailbox = root.get("mailbox", [])
# Filter based on type
if msg_type == "received":
filtered = [m for m in mailbox if current_user.username.upper() in [t.upper() for t in m.to]]
elif msg_type == "sent":
filtered = [m for m in mailbox if m.from_call.upper() == current_user.username.upper()]
else: # all
filtered = mailbox
# Sort newest first
filtered.sort(key=lambda m: m.sent_at, reverse=True)
messages = []
for msg in filtered[:limit]:
messages.append({
"id": msg.msg_id,
"from": msg.from_call,
"to": ", ".join(msg.to),
"subject": msg.subject or "(no subject)",
"text_preview": msg.text[:100] + ("..." if len(msg.text) > 100 else ""),
"sent_at": msg.sent_at.isoformat() + "Z",
"retrieved": getattr(msg, "retrieved", False),
"has_attachments": bool(getattr(msg, "attachments", []))
})
return templates.TemplateResponse(
"message_list.html",
{
"request": request,
"messages": messages,
"msg_type": msg_type,
"current_user": current_user.username
}
)

View File

@@ -36,6 +36,8 @@ app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static")
# Now safe to import dashboard (it needs templates) # Now safe to import dashboard (it needs templates)
from .routers import dashboard, bulletins from .routers import dashboard, bulletins
from .routers.message_detail import router as message_detail_router
from .routers.messages import html_router
# Include routers # Include routers
app.include_router(public.router) app.include_router(public.router)
@@ -45,4 +47,6 @@ app.include_router(send.router)
app.include_router(dashboard.router) app.include_router(dashboard.router)
app.include_router(bulletins.router) app.include_router(bulletins.router)
app.include_router(bulletins.html_router) app.include_router(bulletins.html_router)
app.include_router(message_detail_router)
app.include_router(messages.html_router)

View File

@@ -17,6 +17,8 @@
<small class="text-light ms-3">(Close browser to logout)</small> <small class="text-light ms-3">(Close browser to logout)</small>
</span> </span>
<a href="{{ url_for('profile_page') }}" class="btn btn-outline-light btn-sm me-2">Profile</a> <a href="{{ url_for('profile_page') }}" class="btn btn-outline-light btn-sm me-2">Profile</a>
<a href="/messages" class="btn btn-outline-light btn-sm me-2">Messages</a>
<a href="/bulletins" class="btn btn-outline-light btn-sm me-2">Bulletins</a>
</div> </div>
</nav> </nav>

View File

@@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block title %}Message {{ message.id }} - {{ current_user }}{% endblock %}
{% block content %}
<dl class="row">
<dt class="col-sm-2">Id</dt>
<dd class="col-sm-10">{{ message.id }}</dd>
<dt class="col-sm-2">From</dt>
<dd class="col-sm-10">{{ message.from }}</dd>
<dt class="col-sm-2">To</dt>
<dd class="col-sm-10">{{ message.to | join(', ') }}</dd>
<dt class="col-sm-2">Date</dt>
<dd class="col-sm-10">{{ message.sent_at[:10] }} {{ message.sent_at[11:19] }} UTC</dd>
<dt class="col-sm-2">Status</dt>
<dd class="col-sm-10">
{% if message.retrieved %}
<span class="text-success">Read</span>
{% else %}
<span class="text-warning">Unread</span>
{% endif %}
</dd>
{% if message.has_attachments %}
<dt class="col-sm-2">Attachments</dt>
<dd class="col-sm-10 text-info">Has {{ message.attachments | length }} attachment(s) (not shown yet)</dd>
{% endif %}
</dl>
<h3>Body</h3>
<pre class="bg-light p-3 border">{{ message.text }}</pre>
<p class="nav-links">
<a href="/dashboard">← Back to Dashboard</a>
</p>
{% endblock %}

View File

@@ -0,0 +1,33 @@
{% extends "base.html" %}
{% block title %}Messages - PacketServer{% endblock %}
{% block content %}
<h1>Messages</h1>
<div class="mb-3">
<a href="?msg_type=received" class="btn btn-sm {% if msg_type == 'received' %}btn-primary{% else %}btn-outline-primary{% endif %}">Received</a>
<a href="?msg_type=sent" class="btn btn-sm {% if msg_type == 'sent' %}btn-primary{% else %}btn-outline-primary{% endif %}">Sent</a>
<a href="?msg_type=all" class="btn btn-sm {% if msg_type == 'all' %}btn-primary{% else %}btn-outline-primary{% endif %}">All</a>
</div>
{% if messages %}
<ul class="message-list">
{% for msg in messages %}
<li>
<strong><a href="/dashboard/message/{{ msg.id }}">{{ msg.subject }}</a></strong>
<span class="meta">
From: {{ msg.from }} | To: {{ msg.to }} | {{ msg.sent_at[:10] }} {{ msg.sent_at[11:19] }}
{% if not msg.retrieved %}<span class="text-warning"> (Unread)</span>{% endif %}
{% if msg.has_attachments %}<span class="text-info"> (Attachments)</span>{% endif %}
</span>
<div class="preview">{{ msg.text_preview }}</div>
</li>
{% endfor %}
</ul>
{% else %}
<p>No messages found.</p>
{% endif %}
<p><a href="/dashboard">← Back to Dashboard</a></p>
{% endblock %}