Skip to content
Snippets Groups Projects
Commit decb6c19 authored by Jakub Drápalík's avatar Jakub Drápalík
Browse files

full authentication, delete x items, filter items

parent 8bc50911
No related branches found
No related tags found
1 merge request!1Merge master do mainu - autentifikace, mazání X dat, filtrování dat
......@@ -4,14 +4,8 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="dc430dd6-7651-4a7f-96c1-6718ff6c20b9" name="Changes" comment="1st commit">
<change afterPath="$PROJECT_DIR$/templates/base.html" afterDir="false" />
<change afterPath="$PROJECT_DIR$/templates/index.html" afterDir="false" />
<change afterPath="$PROJECT_DIR$/templates/login.html" afterDir="false" />
<change afterPath="$PROJECT_DIR$/templates/register.html" afterDir="false" />
<change afterPath="$PROJECT_DIR$/users.json" afterDir="false" />
<list default="true" id="dc430dd6-7651-4a7f-96c1-6718ff6c20b9" name="Changes" comment="skeleton for authentication, base template">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
......@@ -98,7 +92,7 @@
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1709806296443</updated>
<workItem from="1709806303486" duration="4461000" />
<workItem from="1709806303486" duration="6311000" />
</task>
<task id="LOCAL-00001" summary="1st commit">
<option name="closed" value="true" />
......@@ -108,7 +102,15 @@
<option name="project" value="LOCAL" />
<updated>1709806519109</updated>
</task>
<option name="localTasksCounter" value="2" />
<task id="LOCAL-00002" summary="skeleton for authentication, base template">
<option name="closed" value="true" />
<created>1709818813093</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1709818813093</updated>
</task>
<option name="localTasksCounter" value="3" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
......@@ -116,7 +118,8 @@
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="1st commit" />
<option name="LAST_COMMIT_MESSAGE" value="1st commit" />
<MESSAGE value="skeleton for authentication, base template" />
<option name="LAST_COMMIT_MESSAGE" value="skeleton for authentication, base template" />
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/drapajak_miniprojekt$drapajak_miniprojekt.coverage" NAME="drapajak-miniprojekt Coverage Results" MODIFIED="1709810579502" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
......
from datetime import datetime
import bcrypt
import json
import os
from flask import *
from flask_login import *
app = Flask(__name__)
app.secret_key = os.urandom(24)
USER_DATA_FILE = 'users.json'
DATA_FILE = 'data.json'
@app.route('/')
def hello_world():
return render_template('index.html'), 200
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
class User(UserMixin):
def __init__(self, username):
self.id = username
@login_manager.user_loader
def load_user(user_id):
users = load_users()
if user_id in users:
return User(user_id)
return None
def initialize_users_file():
if not os.path.exists(USER_DATA_FILE):
with open(USER_DATA_FILE, 'w') as f:
json.dump({}, f)
initialize_users_file()
def load_data():
if os.path.exists(DATA_FILE):
with open(DATA_FILE, 'r') as f:
return json.load(f)
else:
return {}
def load_users():
if os.path.exists(USER_DATA_FILE):
with open(USER_DATA_FILE, 'r') as f:
return json.load(f)
else:
return {}
def save_users(users):
with open(USER_DATA_FILE, 'w') as f:
json.dump(users, f)
def redirect_dest(home):
dest_url = request.args.get('next')
if not dest_url:
dest_url = url_for(home)
return redirect(dest_url)
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
if add_user(username, password):
flash('Registration successful')
return redirect(url_for('login'))
else:
flash('Username already exists')
username = request.form['username']
password = request.form['password']
users = load_users()
if username in users:
return jsonify({'message': 'Uživatelské jméno již existuje!'}), 400
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
users[username] = hashed_password
save_users(users)
return jsonify({'message': 'Registrace úspěšná!'}), 200
return render_template('register.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
if authenticate_user(username, password):
return redirect(url_for('home'))
username = request.form['username']
password = request.form['password']
users = load_users()
if username in users and bcrypt.checkpw(password.encode('utf-8'), users[username].encode('utf-8')):
user = User(username)
login_user(user)
return jsonify({'message': 'Přihlášení úspěšné!'}), 200
else:
flash('Incorrect username/password')
return jsonify({'message': 'Nesprávné jméno nebo heslo!'}), 400
return render_template('login.html')
def authenticate_user(username, password):
with open('users.json', 'r') as file:
data = json.load(file)
for user in data['users']:
if user['username'] == username and user['password'] == password:
return True
return False
def add_user(username, password):
with open('users.json', 'r') as file:
data = json.load(file)
for user in data['users']:
if user['username'] == username:
return False
data['users'].append({'username': username, 'password': password})
with open('users.json', 'w') as file:
json.dump(data, file, indent=4)
return True
@app.route('/filter-data', methods=['POST'])
@login_required
def update_items_count():
data = request.get_json()
items_count = int(data.get('itemsCount', 5))
all_data = load_data()
filtered_data = all_data[:items_count]
return jsonify(filtered_data)
@app.route('/delete-oldest-items', methods=['POST'])
@login_required
def delete_oldest_items():
data = request.get_json()
count = int(data.get('count', 1))
all_data = load_data()
all_data.sort(key=lambda x: datetime.strptime(x.get('timestamp', '0000-00-00-000000'), '%Y-%m-%d-%H%M%S'))
all_data = all_data[count:]
with open(DATA_FILE, 'w') as f:
json.dump(all_data, f)
new_total_items = len(all_data)
return jsonify({'message': f'Bylo smazáno {count} nejstarších položek.', 'total_items': new_total_items}), 200
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('login'))
@app.route('/', methods=['GET'])
@login_required
def index():
data = load_data()
total_items = len(data)
return render_template('index.html', items=data, total_items=total_items)
if __name__ == '__main__':
app.run(debug=True)
[{"humidity": 15, "temperature": 17, "timestamp": "2024-10-12-164507"},
{"humidity": 16, "temperature": 18, "timestamp": "2024-10-13-164507"},
{"humidity": 17, "temperature": 19, "timestamp": "2024-10-14-164507"},
{"humidity": 18, "temperature": 20, "timestamp": "2024-10-15-164507"},
{"humidity": 19, "temperature": 10, "timestamp": "2024-10-16-164507"},
{"humidity": 20, "temperature": 19, "timestamp": "2024-10-17-164507"},
{"humidity": 21, "temperature": 17, "timestamp": "2024-10-18-164507"},
{"humidity": 22, "temperature": 19, "timestamp": "2024-10-19-164507"},
{"humidity": 23, "temperature": 18, "timestamp": "2024-10-20-164507"},
{"humidity": 24, "temperature": 7, "timestamp": "2024-10-21-164507"},
{"humidity": 25, "temperature": 27, "timestamp": "2024-10-22-164507"},
{"humidity": 26, "temperature": 10, "timestamp": "2024-10-23-164507"}]
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<title>{% block title %}{% endblock title %} - NSI</title>
{% endblock head %}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}My Flask App{% endblock %}</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Bootstrap CSS -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
{% block navbar %}
<div class="navbar bg-body-primary p-5">
<div class="nav justify-content-space-between container-fluid">
<div class="nav-item">
<a class="navlink" href="/">Home</a>
</div>
<div class="nav-item">
<button class="btn">{% block buttonText %}{% endblock buttonText %}</button>
</div>
</div>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{{ url_for('index') }}">Dashboard</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
{% if current_user.is_anonymous %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('login') }}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('register') }}">Register</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav ml-auto">
{% if current_user.is_authenticated %}
<li class="nav-item">
<span class="nav-link font-weight-bold">{{ current_user.id }}</span>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
</li>
{% endif %}
</ul>
</div>
{% endblock navbar %}
</nav>
<div class="container mt-3">
{% block content %}{% endblock %}
</div>
<!-- Bootstrap JS -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>
\ No newline at end of file
</html>
{% extends 'base.html' %}
{% block title %}Home{% endblock title %}
<body>
{% block buttonText %}Register{% endblock buttonText %}
{% block card %}
<div class="container-fluid">
<div class="card" style="width: 10rem;">
<div class="card-body">
<h2 class="card-title">Poslední naměřená hodnota</h2>
<p class="card-text">Lorem Ipsum blab la</p>
{% extends "base.html" %}
{% block title %}Domů{% endblock %}
{% block content %}
<div class="container">
<h2>Data</h2>
<div class="row p-3">
<div class="col-md-6">
<label for="itemsCount">Počet zobrazovaných položek: <span id="sliderValue">{{ total_items }}</span></label>
<input type="range" class="custom-range" id="itemsCount" min="0" max="{{ total_items }}" value="5" oninput="updateSliderValue(this.value)">
</div>
<div class="col-md-6">
<div class="row p-3 md-1">
<div class="col-md-12">
<label for="deleteCount">Počet položek k smazání: <input type="number" id="deleteCount" min="1" max="{{ total_items }}" value="1"></label>
</div>
<div class="row p-3">
<div class="col-md-12">
<button class="btn btn-danger" onclick="deleteOldestItems()">Smazat položky</button>
</div>
</div>
</div>
</div>
</div>
<div class="row" id="itemsContainer">
{% for item in items %}
<div class="col-md-4">
<div class="card m-3">
<div class="card-body">
<h5 class="card-title">Teplota: {{ item.temperature }}°C, Humidita: {{ item.humidity }}%</h5>
<button type="button" class="btn btn-danger" onclick="deleteItem('{{ item.id }}')">Smazat</button> //TODO: implementovat mazání jednotlivých položek skrze id ? -> nutno přidat do jsonu
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock card %}
</body>
\ No newline at end of file
<script>
document.addEventListener('DOMContentLoaded', function() {
const slider = document.getElementById('itemsCount');
const totalItems = {{ total_items }};
slider.max = totalItems;
});
function updateSliderValue(value) {
document.getElementById('sliderValue').textContent = value;
fetch('/filter-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({itemsCount: value}),
})
.then(response => response.json())
.then(data => {
const itemsContainer = document.getElementById('itemsContainer');
itemsContainer.innerHTML = '';
data.forEach(item => {
const itemElement = document.createElement('div');
itemElement.className = 'col-md-4';
itemElement.innerHTML = `
<div class="card m-3">
<div class="card-body">
<h5 class="card-title">Teplota: ${item.temperature}°C, Humidita: ${item.humidity}%</h5>
<button type="button" class="btn btn-danger" onclick="deleteItem('${item.id}')">Smazat</button>
</div>
</div>
`;
itemsContainer.appendChild(itemElement);
});
})
.catch((error) => {
console.error('Chyba:', error);
});
}
function deleteOldestItems() {
const count = document.getElementById('deleteCount').value;
fetch('/delete-oldest-items', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({count: count}),
})
.then(response => response.json())
.then(data => {
const slider = document.getElementById('itemsCount');
const totalItems = data.total_items;
slider.max = totalItems;
document.getElementById('sliderValue').textContent = totalItems;
updateSliderValue(document.getElementById('itemsCount').value);
})
.catch((error) => {
console.error('Chyba:', error);
});
}
</script>
{% endblock %}
{% extends 'base.html' %}
{% block title %}Login{% endblock title %}
<body>
{% block buttonText %}Register{% endblock buttonText %}
</body>
\ No newline at end of file
{% extends "base.html" %}
{% block title %}Přihlášení{% endblock %}
{% block content %}
<div class="container">
<h2>Přihlášení</h2>
<form id="loginForm" action="{{ url_for('login') }}" method="post">
<div class="form-group">
<label for="username">Jméno:</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Heslo:</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Přihlásit</button>
</form>
<div id="errorMessage" class="alert alert-danger mt-3" style="display: none;"></div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
$('#loginForm').on('submit', function(e) {
e.preventDefault();
$.ajax({
url: $(this).attr('action'),
type: 'POST',
data: $(this).serialize(),
success: function(response) {
if (response.message === 'Přihlášení úspěšné!') {
window.location.href = '/';
} else {
$('#errorMessage').text(response.message).show();
}
},
error: function(xhr, status, error) {
$('#errorMessage').text(xhr.responseJSON.message).show();
}
});
});
});
</script>
{% endblock %}
{% extends 'base.html' %}
{% block title %}Register{% endblock title %}
<body>
{% block buttonText %}Login{% endblock buttonText %}
</body>
\ No newline at end of file
{% extends "base.html" %}
{% block title %}Registrace{% endblock %}
{% block content %}
<div class="container">
<h2>Registrace</h2>
<form id="registerForm" action="{{ url_for('register') }}" method="post">
<div class="form-group">
<label for="username">Jméno:</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Heslo:</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Registrovat</button>
</form>
<div id="errorMessage" class="alert alert-danger mt-3" style="display: none;"></div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
$('#registerForm').on('submit', function(e) {
e.preventDefault();
$.ajax({
url: $(this).attr('action'),
type: 'POST',
data: $(this).serialize(),
success: function(response) {
if (response.message === 'Registrace úspěšná!') {
window.location.href = '/';
} else {
$('#errorMessage').text(response.message).show();
}
},
error: function(xhr, status, error) {
$('#errorMessage').text(xhr.responseJSON.message).show();
}
});
});
});
</script>
{% endblock %}
{"adasd": "$2b$12$dnUywykk8u2tBp2e5flVJ.kMspi.Lbua9bWmFfIU2MoxfXmwhp8lG", "boris": "$2b$12$U8E7XJgYzCNe/XZIA1p4venRE1Q1RHrhuCPvEdno24nlKlrlJQ/TO", "borisa": "$2b$12$Gj8lINYG/YRCGqz/nhkg7erb7Un0lVLWGIKG5tgUWCFp6V2.Pl5Pm", "test": "$2b$12$qOnnRDFRO3jHodU7jMgZne3i53wp4DgZEO/aQQfAsj2j0NUmbJ8ai", "test12": "$2b$12$AI8bx2Sv.ah1B/gBb3H3r..CnoiE3CutsLcvHiDxke3Duv/XUGeDO"}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment