working, we forked pyrafiles into pyralogs

modified:   app.py
	modified:   requirements.txt
	modified:   templates/base.html.j2
	new file:   templates/create_namespace.html.j2
	new file:   templates/display_agent_jwt.html.j2
	deleted:    templates/edit_media.html.j2
	modified:   templates/home.html.j2
	deleted:    templates/import_user_record.html.j2
	deleted:    templates/list_media.html.j2
	new file:   templates/manage_namespace.html.j2
	modified:   templates/profile.html.j2
	deleted:    templates/upload_media.html.j2
	deleted:    templates/user_media.html.j2
	deleted:    templates/view_media.html.j2
	deleted:    templates/view_media_details.html.j2
	new file:   templates/view_namespace_logs.html.j2
This commit is contained in:
Russell Ballestrini 2025-01-11 19:13:51 -05:00
parent 7a2a2133cf
commit c9f8df96c2
16 changed files with 662 additions and 763 deletions

1056
app.py

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,8 @@ pyramid_debugtoolbar
waitress
pyramid_retry
pyramid_jwt
pyramid_tm
zope.sqlalchemy

View file

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}Media Hosting App{% endblock %}</title>
<title>{% block title %}PyraLogs{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://www.unturf.com/css/pico.classless.min.css" rel="stylesheet">
<style>
@ -99,14 +99,14 @@
</head>
<body>
<nav>
<a href="{{ request.route_url('home') }}"><strong>upload.unturf.com</strong></a>
<a href="{{ request.route_url('home') }}"><strong>PyraLogs</strong></a>
<!-- Hamburger Menu Button (only visible on mobile) -->
<button id="mobile-menu-button" aria-label="Open Menu">☰</button>
<!-- Navigation Links -->
<ul>
<li><a href="{{ request.route_url('list_media') }}">Browse Media</a></li>
<li><a href="{{ request.route_url('home') }}">Home</a></li>
{% if request.user and request.user.is_verified %}
<li><a href="{{ request.route_url('upload_media') }}">Upload Media</a></li>
<li><a href="{{ request.route_url('create_namespace') }}">Create Namespace</a></li>
<li><a href="{{ request.route_url('profile') }}">Profile</a></li>
<li><a href="{{ request.route_url('logout') }}">Logout</a></li>
{% else %}
@ -121,9 +121,9 @@
<button aria-label="Close Menu" id="mobile-menu-close">✕</button>
<nav>
<ul>
<li><a href="{{ request.route_url('list_media') }}">Browse Media</a></li>
<li><a href="{{ request.route_url('home') }}">Home</a></li>
{% if request.user and request.user.is_verified %}
<li><a href="{{ request.route_url('upload_media') }}">Upload Media</a></li>
<li><a href="{{ request.route_url('create_namespace') }}">Create Namespace</a></li>
<li><a href="{{ request.route_url('profile') }}">Profile</a></li>
<li><a href="{{ request.route_url('logout') }}">Logout</a></li>
{% else %}

View file

@ -0,0 +1,18 @@
{% extends 'base.html.j2' %}
{% block title %}Create Namespace{% endblock %}
{% block content %}
<h1>Create a New Namespace</h1>
<form method="POST">
<label for="name">Namespace Name:</label>
<input type="text" name="name" required><br><br>
<label>
<input type="checkbox" name="is_public">
Make Namespace Public
</label><br><br>
<button type="submit">Create Namespace</button>
</form>
{% endblock %}

View file

@ -0,0 +1,12 @@
{% extends 'base.html.j2' %}
{% block title %}Agent JWT{% endblock %}
{% block content %}
<h1>JWT for Agent '{{ agent_name }}'</h1>
<p>Please store this JWT securely. It will not be shown again.</p>
<pre>{{ jwt_token }}</pre>
<p>You can use this JWT to authenticate your agent when sending logs to the namespace '{{ namespace.name }}'.</p>
<p><a href="{{ request.route_url('manage_namespace', namespace_short_id=namespace.short_id) }}">Back to Manage Namespace</a></p>
{% endblock %}

View file

@ -1,35 +0,0 @@
{% extends 'base.html.j2' %}
{% block title %}Edit Media{% endblock %}
{% block content %}
<h1>Edit Media</h1>
<form method="POST" enctype="multipart/form-data">
<label for="title">Title (optional):</label>
<input type="text" name="title" value="{{ media.title }}" placeholder="Enter a title for the media"><br><br>
<label for="media_file">Replace Media File (leave blank to keep current):</label>
<input type="file" name="media_file" accept="image/*,audio/*,video/*"><br><br>
<label>
<input type="checkbox" name="is_public" {% if media.is_public %}checked{% endif %}>
Make Media Public
</label><br><br>
<button type="submit">Update</button>
</form>
<p>
<!-- View and Download links -->
<a href="{{ request.route_url('view_media', user_short_id=request.user.short_id, media_short_id=media.short_id) }}">View</a> |
<a href="{{ request.route_url('view_media', user_short_id=request.user.short_id, media_short_id=media.short_id, _query={'download': 'true'}) }}">Download</a> |
<a href="{{ request.route_url('view_media_details', user_short_id=request.user.short_id, media_short_id=media.short_id) }}">Back to Media Details</a>
</p>
<br/>
<form action="{{ request.route_url('delete_media', user_short_id=request.user.short_id, media_short_id=media.short_id) }}" method="post">
<button type="submit" onclick="return confirm('Are you sure you want to delete this media?');" style="border: 0px; background-color: #AA0000; color: white;">Delete</button>
</form>
{% endblock %}

View file

@ -3,7 +3,7 @@
{% block title %}Home{% endblock %}
{% block content %}
<h1>Welcome to the Media Hosting App</h1>
<h1>Welcome to PyraLogs</h1>
<p>
{% if request.user and request.user.is_verified %}
Hello, {{ request.user.username }}!
@ -11,5 +11,39 @@
Welcome, Guest!
{% endif %}
</p>
<p>This application allows authenticated users to upload images, audio, and short videos. Guests can browse and download publicly available media.</p>
<p>This application allows environments and agents to centrally log events to this hub. You can create namespaces to organize logs and control access.</p>
{% if request.user and request.user.is_verified %}
<h2>Your Namespaces</h2>
{% if user_namespaces %}
<ul>
{% for ns in user_namespaces %}
<li>
<a href="{{ request.route_url('view_namespace_logs', namespace_short_id=ns.short_id) }}">{{ ns.name }}</a>
{% if ns.role == 'owner' %}
| <a href="{{ request.route_url('manage_namespace', namespace_short_id=ns.short_id) }}">Manage</a>
{% endif %}
<br>
Role: {{ ns.role|capitalize }}
</li>
{% endfor %}
</ul>
{% else %}
<p>You are not a member of any namespaces.</p>
{% endif %}
{% endif %}
<h2>Public Namespaces</h2>
{% if public_namespaces %}
<ul>
{% for ns in public_namespaces %}
<li>
<a href="{{ request.route_url('view_namespace_logs', namespace_short_id=ns.short_id) }}">{{ ns.name }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No public namespaces available.</p>
{% endif %}
{% endblock %}

View file

@ -1,12 +0,0 @@
{% extends 'base.html.j2' %}
{% block title %}Import User Record{% endblock %}
{% block content %}
<h1>Import User Record</h1>
<form method="POST" enctype="multipart/form-data">
<label for="user_record_file">Select User Record JSON File:</label>
<input type="file" name="user_record_file" accept="application/json" required><br><br>
<button type="submit">Import</button>
</form>
{% endblock %}

View file

@ -1,30 +0,0 @@
{% extends 'base.html.j2' %}
{% block title %}Media List{% endblock %}
{% block content %}
<h1>Media List</h1>
{% if media_list %}
<ul>
{% for media_item in media_list %}
{% set media = media_item.media %}
<li>
<strong>{{ media.title if media.title else media.filename }}</strong><br>
Uploaded by: <a href="{{ request.route_url('user_media', user_short_id=media_item.user_short_id) }}">{{ media_item.username }}</a><br>
<!-- View and Download links -->
<a href="{{ request.route_url('view_media', user_short_id=media_item.user_short_id, media_short_id=media.short_id) }}">View</a> |
<a href="{{ request.route_url('view_media', user_short_id=media_item.user_short_id, media_short_id=media.short_id, _query={'download': 'true'}) }}">Download</a> |
<!-- Link to media detail page -->
<a href="{{ request.route_url('view_media_details', user_short_id=media_item.user_short_id, media_short_id=media.short_id) }}">Details</a>
{% if request.user and request.user.id == media.user_id %}
| <a href="{{ request.route_url('edit_media', user_short_id=media_item.user_short_id, media_short_id=media.short_id) }}">Edit</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>No media found.</p>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,50 @@
{% extends 'base.html.j2' %}
{% block title %}Manage Namespace{% endblock %}
{% block content %}
<h1>Manage Namespace: {{ namespace.name }}</h1>
<form method="POST" action="{{ request.route_url('update_namespace', namespace_short_id=namespace.short_id) }}">
<label>
<input type="checkbox" name="is_public" {% if namespace.is_public %}checked{% endif %}>
Make Namespace Public
</label><br><br>
<button type="submit">Update Namespace</button>
</form>
<h2>Invite User</h2>
<form method="POST" action="{{ request.route_url('invite_user', namespace_short_id=namespace.short_id) }}">
<label for="email">User Email:</label>
<input type="email" name="email" required><br><br>
<label for="role">Role:</label>
<select name="role" required>
<option value="owner">Owner</option>
<option value="editor">Editor</option>
<option value="reader">Reader</option>
</select><br><br>
<button type="submit">Invite User</button>
</form>
<h2>Users in Namespace</h2>
{% if users %}
<ul>
{% for item in users %}
<li>{{ item.user.username }} - {{ item.role|capitalize }}</li>
{% endfor %}
</ul>
{% else %}
<p>No users in this namespace.</p>
{% endif %}
<h2>Generate JWT for Agent</h2>
<form method="POST" action="{{ request.route_url('generate_agent_jwt', namespace_short_id=namespace.short_id) }}">
<label for="agent_name">Agent Name:</label>
<input type="text" name="agent_name" required><br><br>
<button type="submit">Generate JWT</button>
</form>
{% endblock %}

View file

@ -23,15 +23,5 @@
<button type="submit">Update Profile</button>
</form>
<h2>Your Stats</h2>
<ul>
<li>Total Uploads: {{ total_uploads }}</li>
<li>Total Storage Used: {{ total_size | filesizeformat }}</li>
</ul>
<h2>Your Media</h2>
<p><a href="{{ request.route_url('user_media', user_short_id=user.short_id) }}">View Your Uploads</a></p>
<p><a href="{{ request.route_url('upload_media') }}">Upload New Media</a></p>
<p><a href="{{ request.route_url('download_database') }}">Download Your Database</a></p>
{% endblock %}

View file

@ -1,22 +0,0 @@
{% extends 'base.html.j2' %}
{% block title %}Upload Media{% endblock %}
{% block content %}
<h1>Upload Media</h1>
<form method="POST" enctype="multipart/form-data">
<label for="media_file">Select Media File (Max 30MB):</label>
<input type="file" name="media_file" accept="image/*,audio/*,video/*" required><br><br>
<label for="title">Title (optional):</label>
<input type="text" name="title" placeholder="Enter a title for the media"><br><br>
<!-- Optionally, allow users to set media as public or private -->
<label>
<input type="checkbox" name="is_public" checked>
Make Media Public
</label><br><br>
<button type="submit">Upload</button>
</form>
{% endblock %}

View file

@ -1,33 +0,0 @@
{% extends 'base.html.j2' %}
{% block title %}{{ user.username }}'s Media{% endblock %}
{% block content %}
<h1>{{ user.username }}'s Media</h1>
{% if media_items %}
<ul>
{% for media in media_items %}
<li>
<strong>{{ media.title if media.title else media.filename }}</strong><br>
{% if media.is_public %}
<span>Status: Public</span>
{% else %}
<span>Status: Private</span>
{% endif %}<br>
<!-- View and Download links -->
<a href="{{ request.route_url('view_media', user_short_id=user.short_id, media_short_id=media.short_id) }}">View</a> |
<a href="{{ request.route_url('view_media', user_short_id=user.short_id, media_short_id=media.short_id, _query={'download': 'true'}) }}">Download</a> |
<!-- Link to media detail page -->
<a href="{{ request.route_url('view_media_details', user_short_id=user.short_id, media_short_id=media.short_id) }}">Details</a>
{% if request.user and request.user.id == media.user_id %}
| <a href="{{ request.route_url('edit_media', user_short_id=user.short_id, media_short_id=media.short_id) }}">Edit</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>No media found.</p>
{% endif %}
{% endblock %}

View file

@ -1,38 +0,0 @@
{% extends 'base.html.j2' %}
{% block title %}View Media{% endblock %}
{% block content %}
<h1>{{ media.title if media.title else media.filename }}</h1>
<p>Uploaded by <a href="{{ request.route_url('user_media', user_short_id=user_short_id) }}">{{ username }}</a> on {{ media.upload_date.strftime('%Y-%m-%d %H:%M:%S') }}</p>
<p>Size: {{ media.size | filesizeformat }}</p>
<!-- Display media based on type -->
{% if media.media_type == 'image' %}
<img src="{{ request.route_url('view_media', user_short_id=user_short_id, media_short_id=media.short_id) }}" alt="{{ media.filename }}">
{% elif media.media_type == 'audio' %}
<audio controls>
<source src="{{ request.route_url('view_media', user_short_id=user_short_id, media_short_id=media.short_id) }}">
Your browser does not support the audio element.
</audio>
{% elif media.media_type == 'video' %}
<video controls width="600">
<source src="{{ request.route_url('view_media', user_short_id=user_short_id, media_short_id=media.short_id) }}">
Your browser does not support the video tag.
</video>
{% endif %}
<!-- View and Download links -->
<p>
<a href="{{ request.route_url('view_media', user_short_id=user_short_id, media_short_id=media.short_id) }}">View</a> |
<a href="{{ request.route_url('view_media', user_short_id=user_short_id, media_short_id=media.short_id, _query={'download': 'true'}) }}">Download</a>
</p>
{% if is_owner %}
<!-- Edit button for the owner -->
<p>
<a href="{{ request.route_url('edit_media', user_short_id=user_short_id, media_short_id=media.short_id) }}">Edit</a>
</p>
{% endif %}
{% endblock %}

View file

@ -1,24 +0,0 @@
{% extends 'base.html.j2' %}
{% block title %}Media Details{% endblock %}
{% block content %}
<h1>Media Details</h1>
<p><strong>Title:</strong> {{ media.title if media.title else 'Untitled' }}</p>
<p><strong>Filename:</strong> {{ media.filename }}</p>
<p><strong>Uploaded by:</strong> <a href="{{ request.route_url('user_media', user_short_id=user_short_id) }}">{{ username }}</a></p>
<p><strong>Upload Date:</strong> {{ media.upload_date.strftime('%Y-%m-%d %H:%M:%S') }}</p>
<p><strong>Status:</strong> {{ 'Public' if media.is_public else 'Private' }}</p>
<p><strong>Size:</strong> {{ media.size | filesizeformat }}</p>
<!-- View and Download links -->
<a href="{{ request.route_url('view_media', user_short_id=user_short_id, media_short_id=media.short_id) }}">View</a> |
<a href="{{ request.route_url('view_media', user_short_id=user_short_id, media_short_id=media.short_id, _query={'download': 'true'}) }}">Download</a>
{% if is_owner %}
<!-- Edit button for the owner -->
| <a href="{{ request.route_url('edit_media', user_short_id=user_short_id, media_short_id=media.short_id) }}">Edit</a>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,33 @@
{% extends 'base.html.j2' %}
{% block title %}Namespace Logs{% endblock %}
{% block content %}
<h1>Logs for Namespace: {{ namespace.name }}</h1>
{% if logs %}
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>Level</th>
<th>Message</th>
<th>Metadata</th>
</tr>
</thead>
<tbody>
{% for log in logs %}
<tr>
<td>{{ log.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</td>
<td>{{ log.level }}</td>
<td>{{ log.message }}</td>
<td>{{ log.log_metadata }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No logs available.</p>
{% endif %}
{% endblock %}