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:
parent
7a2a2133cf
commit
c9f8df96c2
16 changed files with 662 additions and 763 deletions
|
|
@ -5,6 +5,8 @@ pyramid_debugtoolbar
|
|||
waitress
|
||||
pyramid_retry
|
||||
|
||||
pyramid_jwt
|
||||
|
||||
pyramid_tm
|
||||
zope.sqlalchemy
|
||||
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
18
templates/create_namespace.html.j2
Normal file
18
templates/create_namespace.html.j2
Normal 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 %}
|
||||
12
templates/display_agent_jwt.html.j2
Normal file
12
templates/display_agent_jwt.html.j2
Normal 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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
50
templates/manage_namespace.html.j2
Normal file
50
templates/manage_namespace.html.j2
Normal 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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
33
templates/view_namespace_logs.html.j2
Normal file
33
templates/view_namespace_logs.html.j2
Normal 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 %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue