Compare commits
No commits in common. "8ee07b57eb3d05d2b51b4e9b950eb22dc0d9657d" and "dbe938af1cca4483bb7d37f32290acf4791945b4" have entirely different histories.
8ee07b57eb
...
dbe938af1c
@ -2,9 +2,12 @@ import sqlite3
|
|||||||
from flask import render_template, request, session
|
from flask import render_template, request, session
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
import geoip2.database
|
import geoip2.database
|
||||||
|
from auth import require_secret
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from typing import Optional, List, Tuple
|
from typing import Optional, List, Tuple
|
||||||
|
import json
|
||||||
|
import os
|
||||||
import auth
|
import auth
|
||||||
import helperfunctions as hf
|
import helperfunctions as hf
|
||||||
|
|
||||||
@ -311,6 +314,8 @@ def songs_dashboard():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@require_secret
|
||||||
def connections():
|
def connections():
|
||||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
||||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
||||||
@ -319,7 +324,7 @@ def connections():
|
|||||||
title_short=title_short,
|
title_short=title_short,
|
||||||
title_long=title_long)
|
title_long=title_long)
|
||||||
|
|
||||||
|
@require_secret
|
||||||
def dashboard():
|
def dashboard():
|
||||||
if 'filetype' not in session:
|
if 'filetype' not in session:
|
||||||
session['filetype'] = 'audio'
|
session['filetype'] = 'audio'
|
||||||
@ -598,6 +603,7 @@ def dashboard():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@require_secret
|
||||||
def file_access():
|
def file_access():
|
||||||
if 'timeframe' not in session:
|
if 'timeframe' not in session:
|
||||||
session['timeframe'] = 'last24hours'
|
session['timeframe'] = 'last24hours'
|
||||||
|
|||||||
12
app.py
12
app.py
@ -43,16 +43,16 @@ if os.environ.get('FLASK_ENV') == 'production':
|
|||||||
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
|
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
|
||||||
app.config['SESSION_COOKIE_SECURE'] = True
|
app.config['SESSION_COOKIE_SECURE'] = True
|
||||||
|
|
||||||
app.add_url_rule('/dashboard', view_func=auth.require_admin(a.dashboard))
|
app.add_url_rule('/dashboard', view_func=a.dashboard)
|
||||||
app.add_url_rule('/file_access', view_func=auth.require_admin(a.file_access))
|
app.add_url_rule('/file_access', view_func=a.file_access)
|
||||||
app.add_url_rule('/connections', view_func=auth.require_admin(a.connections))
|
app.add_url_rule('/connections', view_func=a.connections)
|
||||||
app.add_url_rule('/mylinks', view_func=auth.require_secret(auth.mylinks))
|
app.add_url_rule('/mylinks', view_func=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_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('/remove_token', view_func=auth.remove_token, methods=['POST'])
|
||||||
app.add_url_rule('/searchcommand', view_func=search.searchcommand, 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', 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/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'])
|
app.add_url_rule('/admin/folder_secret_config_editor/action', view_func=auth.require_admin(fsce.folder_secret_config_action), methods=['POST'])
|
||||||
|
|||||||
3
auth.py
3
auth.py
@ -181,6 +181,7 @@ def require_secret(f):
|
|||||||
|
|
||||||
def require_admin(f):
|
def require_admin(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
@require_secret
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if is_admin():
|
if is_admin():
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
@ -196,7 +197,7 @@ def save_folder_config(data):
|
|||||||
json.dump(folder_config, file, indent=4)
|
json.dump(folder_config, file, indent=4)
|
||||||
return folder_config
|
return folder_config
|
||||||
|
|
||||||
|
@require_secret
|
||||||
def mylinks():
|
def mylinks():
|
||||||
scheme = request.scheme # current scheme (http or https)
|
scheme = request.scheme # current scheme (http or https)
|
||||||
valid_secrets = session.get('valid_secrets', [])
|
valid_secrets = session.get('valid_secrets', [])
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
from flask import Flask, request, jsonify, render_template
|
from flask import Flask, request, jsonify, render_template
|
||||||
|
import json
|
||||||
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import secrets
|
||||||
import string
|
import string
|
||||||
import auth
|
import auth
|
||||||
|
|
||||||
@ -9,6 +12,7 @@ app_config = auth.return_app_config()
|
|||||||
ALPHABET = string.ascii_letters + string.digits
|
ALPHABET = string.ascii_letters + string.digits
|
||||||
|
|
||||||
|
|
||||||
|
@auth.require_admin
|
||||||
def folder_secret_config_editor():
|
def folder_secret_config_editor():
|
||||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
||||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
||||||
@ -18,7 +22,7 @@ def folder_secret_config_editor():
|
|||||||
title_short=title_short,
|
title_short=title_short,
|
||||||
title_long=title_long)
|
title_long=title_long)
|
||||||
|
|
||||||
|
@auth.require_admin
|
||||||
def folder_secret_config_action():
|
def folder_secret_config_action():
|
||||||
p = request.get_json()
|
p = request.get_json()
|
||||||
data = auth.return_folder_config()
|
data = auth.return_folder_config()
|
||||||
|
|||||||
@ -7,6 +7,14 @@
|
|||||||
<title>{% block title %}Meine Links{% endblock %}</title>
|
<title>{% block title %}Meine Links{% endblock %}</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Android Theme Color -->
|
<!-- Android Theme Color -->
|
||||||
<meta name="theme-color" content="#000">
|
<meta name="theme-color" content="#000">
|
||||||
|
|
||||||
|
|||||||
@ -147,7 +147,7 @@
|
|||||||
const remBtn = document.createElement('button');
|
const remBtn = document.createElement('button');
|
||||||
remBtn.className = 'btn btn-outline-danger';
|
remBtn.className = 'btn btn-outline-danger';
|
||||||
remBtn.type = 'button';
|
remBtn.type = 'button';
|
||||||
remBtn.textContent = 'entfernen';
|
remBtn.textContent = 'Remove';
|
||||||
remBtn.addEventListener('click', () => removeFolder(key, i));
|
remBtn.addEventListener('click', () => removeFolder(key, i));
|
||||||
nameGroup.appendChild(remBtn);
|
nameGroup.appendChild(remBtn);
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@
|
|||||||
const addFld = document.createElement('button');
|
const addFld = document.createElement('button');
|
||||||
addFld.className = 'btn btn-sm btn-primary mb-2';
|
addFld.className = 'btn btn-sm btn-primary mb-2';
|
||||||
addFld.type = 'button';
|
addFld.type = 'button';
|
||||||
addFld.textContent = 'Ordner hinzufügen';
|
addFld.textContent = 'Add Folder';
|
||||||
addFld.addEventListener('click', () => addFolder(key));
|
addFld.addEventListener('click', () => addFolder(key));
|
||||||
body.appendChild(addFld);
|
body.appendChild(addFld);
|
||||||
}
|
}
|
||||||
@ -186,49 +186,32 @@
|
|||||||
actions.appendChild(openButton);
|
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');
|
const delBtn = document.createElement('button');
|
||||||
delBtn.className = 'btn btn-danger btn-sm me-2 delete-btn';
|
delBtn.className = 'btn btn-danger btn-sm me-2 delete-btn';
|
||||||
delBtn.type = 'button';
|
delBtn.type = 'button';
|
||||||
delBtn.textContent = 'löschen';
|
delBtn.textContent = 'Delete';
|
||||||
delBtn.dataset.secret = key;
|
delBtn.dataset.secret = key;
|
||||||
actions.appendChild(delBtn);
|
actions.appendChild(delBtn);
|
||||||
|
|
||||||
const cloneBtn = document.createElement('button');
|
const cloneBtn = document.createElement('button');
|
||||||
cloneBtn.className = 'btn btn-secondary btn-sm me-2';
|
cloneBtn.className = 'btn btn-secondary btn-sm me-2';
|
||||||
cloneBtn.type = 'button';
|
cloneBtn.type = 'button';
|
||||||
cloneBtn.textContent = 'clonen';
|
cloneBtn.textContent = 'Clone';
|
||||||
cloneBtn.addEventListener('click', () => cloneRec(key));
|
cloneBtn.addEventListener('click', () => cloneRec(key));
|
||||||
actions.appendChild(cloneBtn);
|
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) {
|
if (isEdit) {
|
||||||
const saveBtn = document.createElement('button');
|
const saveBtn = document.createElement('button');
|
||||||
saveBtn.className = 'btn btn-success btn-sm';
|
saveBtn.className = 'btn btn-success btn-sm';
|
||||||
saveBtn.type = 'button';
|
saveBtn.type = 'button';
|
||||||
saveBtn.textContent = 'speichern';
|
saveBtn.textContent = 'Save';
|
||||||
saveBtn.addEventListener('click', () => saveRec(key));
|
saveBtn.addEventListener('click', () => saveRec(key));
|
||||||
actions.appendChild(saveBtn);
|
actions.appendChild(saveBtn);
|
||||||
} else {
|
} else {
|
||||||
const editBtn = document.createElement('button');
|
const editBtn = document.createElement('button');
|
||||||
editBtn.className = 'btn btn-warning btn-sm';
|
editBtn.className = 'btn btn-warning btn-sm';
|
||||||
editBtn.type = 'button';
|
editBtn.type = 'button';
|
||||||
editBtn.textContent = 'bearbeiten';
|
editBtn.textContent = 'Edit';
|
||||||
editBtn.addEventListener('click', () => editRec(key));
|
editBtn.addEventListener('click', () => editRec(key));
|
||||||
actions.appendChild(editBtn);
|
actions.appendChild(editBtn);
|
||||||
}
|
}
|
||||||
@ -258,49 +241,10 @@
|
|||||||
const rec = JSON.parse(JSON.stringify(data[idx]));
|
const rec = JSON.parse(JSON.stringify(data[idx]));
|
||||||
const existing = data.map(r => r.secret);
|
const existing = data.map(r => r.secret);
|
||||||
rec.secret = generateSecret(existing);
|
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);
|
data.splice(idx+1, 0, rec);
|
||||||
editing.add(rec.secret);
|
editing.add(rec.secret);
|
||||||
render();
|
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) {
|
function addFolder(secret) {
|
||||||
data.find(r => r.secret === secret).folders.push({foldername:'', folderpath:''});
|
data.find(r => r.secret === secret).folders.push({foldername:'', folderpath:''});
|
||||||
render();
|
render();
|
||||||
|
|||||||
@ -1,12 +1,27 @@
|
|||||||
{# templates/file_access.html #}
|
<!doctype html>
|
||||||
{% extends 'base.html' %}
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
{# page title #}
|
<meta property="og:title" content="{{ title_long }}" />
|
||||||
{% block title %}Dateizugriffe{% endblock %}
|
<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 content #}
|
|
||||||
{% block content %}
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="alert alert-warning">Du hast keine gültige Freigaben.<br>Bitte Ordner mit einem Freigabelink freischalten.</div>
|
<div class="alert alert-warning">Du hast keine Links die noch gültig sind.<br>Bitte den Freigabelink erneut anklicken.</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user