Compare commits

..

3 Commits

Author SHA1 Message Date
8ee07b57eb allow admin access without valid link 2025-09-07 19:20:42 +00:00
038f014f9c add renew function 2025-09-07 18:53:36 +00:00
17ff666965 improve setting page 2025-09-07 18:43:15 +00:00
7 changed files with 79 additions and 57 deletions

View File

@ -2,12 +2,9 @@ import sqlite3
from flask import render_template, request, session
from datetime import datetime, timedelta, timezone
import geoip2.database
from auth import require_secret
from collections import defaultdict
import pandas as pd
from typing import Optional, List, Tuple
import json
import os
import auth
import helperfunctions as hf
@ -314,8 +311,6 @@ def songs_dashboard():
)
@require_secret
def connections():
title_short = app_config.get('TITLE_SHORT', 'Default Title')
title_long = app_config.get('TITLE_LONG' , 'Default Title')
@ -324,7 +319,7 @@ def connections():
title_short=title_short,
title_long=title_long)
@require_secret
def dashboard():
if 'filetype' not in session:
session['filetype'] = 'audio'
@ -603,7 +598,6 @@ def dashboard():
)
@require_secret
def file_access():
if 'timeframe' not in session:
session['timeframe'] = 'last24hours'

12
app.py
View File

@ -43,16 +43,16 @@ if os.environ.get('FLASK_ENV') == 'production':
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
app.config['SESSION_COOKIE_SECURE'] = True
app.add_url_rule('/dashboard', view_func=a.dashboard)
app.add_url_rule('/file_access', view_func=a.file_access)
app.add_url_rule('/connections', view_func=a.connections)
app.add_url_rule('/mylinks', view_func=auth.mylinks)
app.add_url_rule('/dashboard', view_func=auth.require_admin(a.dashboard))
app.add_url_rule('/file_access', view_func=auth.require_admin(a.file_access))
app.add_url_rule('/connections', view_func=auth.require_admin(a.connections))
app.add_url_rule('/mylinks', view_func=auth.require_secret(auth.mylinks))
app.add_url_rule('/songs_dashboard', view_func=auth.require_admin(a.songs_dashboard))
app.add_url_rule('/remove_secret', view_func=auth.remove_secret, methods=['POST'])
app.add_url_rule('/remove_token', view_func=auth.remove_token, methods=['POST'])
app.add_url_rule('/searchcommand', view_func=search.searchcommand, methods=['POST'])
app.add_url_rule('/songs_dashboard', view_func=a.songs_dashboard)
app.add_url_rule('/admin/folder_secret_config_editor', view_func=auth.require_admin(fsce.folder_secret_config_editor), methods=['GET', 'POST'])
app.add_url_rule('/admin/folder_secret_config_editor/data', view_func=auth.require_admin(auth.load_folder_config))
app.add_url_rule('/admin/folder_secret_config_editor/action', view_func=auth.require_admin(fsce.folder_secret_config_action), methods=['POST'])

View File

@ -181,7 +181,6 @@ def require_secret(f):
def require_admin(f):
@wraps(f)
@require_secret
def decorated_function(*args, **kwargs):
if is_admin():
return f(*args, **kwargs)
@ -197,7 +196,7 @@ def save_folder_config(data):
json.dump(folder_config, file, indent=4)
return folder_config
@require_secret
def mylinks():
scheme = request.scheme # current scheme (http or https)
valid_secrets = session.get('valid_secrets', [])

View File

@ -1,8 +1,5 @@
from flask import Flask, request, jsonify, render_template
import json
import os
from datetime import datetime
import secrets
import string
import auth
@ -12,7 +9,6 @@ app_config = auth.return_app_config()
ALPHABET = string.ascii_letters + string.digits
@auth.require_admin
def folder_secret_config_editor():
title_short = app_config.get('TITLE_SHORT', 'Default Title')
title_long = app_config.get('TITLE_LONG' , 'Default Title')
@ -22,7 +18,7 @@ def folder_secret_config_editor():
title_short=title_short,
title_long=title_long)
@auth.require_admin
def folder_secret_config_action():
p = request.get_json()
data = auth.return_folder_config()

View File

@ -7,14 +7,6 @@
<title>{% block title %}Meine Links{% endblock %}</title>
<!-- Android Theme Color -->
<meta name="theme-color" content="#000">

View File

@ -147,7 +147,7 @@
const remBtn = document.createElement('button');
remBtn.className = 'btn btn-outline-danger';
remBtn.type = 'button';
remBtn.textContent = 'Remove';
remBtn.textContent = 'entfernen';
remBtn.addEventListener('click', () => removeFolder(key, i));
nameGroup.appendChild(remBtn);
}
@ -170,7 +170,7 @@
const addFld = document.createElement('button');
addFld.className = 'btn btn-sm btn-primary mb-2';
addFld.type = 'button';
addFld.textContent = 'Add Folder';
addFld.textContent = 'Ordner hinzufügen';
addFld.addEventListener('click', () => addFolder(key));
body.appendChild(addFld);
}
@ -186,32 +186,49 @@
actions.appendChild(openButton);
}
if (!isEdit) {
const openButton = document.createElement('button');
openButton.className = 'btn btn-secondary btn-sm me-2';
openButton.onclick = () => toClipboard(`${window.location.origin}/?secret=${rec.secret}`);
openButton.textContent = 'Link kopieren';
actions.appendChild(openButton);
}
const delBtn = document.createElement('button');
delBtn.className = 'btn btn-danger btn-sm me-2 delete-btn';
delBtn.type = 'button';
delBtn.textContent = 'Delete';
delBtn.textContent = 'löschen';
delBtn.dataset.secret = key;
actions.appendChild(delBtn);
const cloneBtn = document.createElement('button');
cloneBtn.className = 'btn btn-secondary btn-sm me-2';
cloneBtn.type = 'button';
cloneBtn.textContent = 'Clone';
cloneBtn.textContent = 'clonen';
cloneBtn.addEventListener('click', () => cloneRec(key));
actions.appendChild(cloneBtn);
if (isEdit || expired) {
const renewBtn = document.createElement('button');
renewBtn.className = 'btn btn-info btn-sm me-2';
renewBtn.type = 'button';
renewBtn.textContent = 'erneuern';
renewBtn.addEventListener('click', () => renewRec(key));
actions.appendChild(renewBtn);
}
if (isEdit) {
const saveBtn = document.createElement('button');
saveBtn.className = 'btn btn-success btn-sm';
saveBtn.type = 'button';
saveBtn.textContent = 'Save';
saveBtn.textContent = 'speichern';
saveBtn.addEventListener('click', () => saveRec(key));
actions.appendChild(saveBtn);
} else {
const editBtn = document.createElement('button');
editBtn.className = 'btn btn-warning btn-sm';
editBtn.type = 'button';
editBtn.textContent = 'Edit';
editBtn.textContent = 'bearbeiten';
editBtn.addEventListener('click', () => editRec(key));
actions.appendChild(editBtn);
}
@ -241,10 +258,49 @@
const rec = JSON.parse(JSON.stringify(data[idx]));
const existing = data.map(r => r.secret);
rec.secret = generateSecret(existing);
const futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 35);
// Format as DD.MM.YYYY for validity input
const dd = String(futureDate.getDate()).padStart(2, '0');
const mm = String(futureDate.getMonth() + 1).padStart(2, '0');
const yyyy = futureDate.getFullYear();
rec.validity = `${dd}.${mm}.${yyyy}`;
data.splice(idx+1, 0, rec);
editing.add(rec.secret);
render();
}
async function renewRec(secret) {
// find current record
const rec = data.find(r => r.secret === secret);
if (!rec) return;
// generate a fresh unique secret
const existing = data.map(r => r.secret);
const newSecret = generateSecret(existing);
// validity = today + 35 days, formatted as YYYY-MM-DD
const future = new Date();
future.setDate(future.getDate() + 35);
const yyyy = future.getFullYear();
const mm = String(future.getMonth() + 1).padStart(2, '0');
const dd = String(future.getDate()).padStart(2, '0');
const validity = `${yyyy}-${mm}-${dd}`;
// keep folders unchanged
const folders = rec.folders.map(f => ({
foldername: f.foldername,
folderpath: f.folderpath
}));
// persist via existing endpoint
await sendAction({
action: 'update',
oldSecret: secret,
newSecret,
validity,
folders
});
}
function addFolder(secret) {
data.find(r => r.secret === secret).folders.push({foldername:'', folderpath:''});
render();

View File

@ -1,27 +1,12 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
{# templates/file_access.html #}
{% extends 'base.html' %}
<meta property="og:title" content="{{ title_long }}" />
<meta property="og:description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft." />
<meta property="og:image" content="/icon/logo-300x300.png" />
<title>{{ title_long }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="{{ url_for('static', filename='theme.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='app.css') }}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<header class="site-header">
<img src="/custom_logo/logoW.png" alt="Logo" class="logo">
<h1>{{ title_long }}</h1>
</header>
{# page title #}
{% block title %}Dateizugriffe{% endblock %}
{# page content #}
{% block content %}
<div class="container">
<div class="alert alert-warning">Du hast keine Links die noch gültig sind.<br>Bitte den Freigabelink erneut anklicken.</div>
<div class="alert alert-warning">Du hast keine gültige Freigaben.<br>Bitte Ordner mit einem Freigabelink freischalten.</div>
</div>
</body>
</html>
{% endblock %}