Walter Mantovani
commited on
Commit
·
f1d2cf1
1
Parent(s):
be4de6a
First test
Browse files- .gitignore +25 -0
- Dockerfile +29 -0
- app.py +115 -0
- requirements.txt +2 -0
- templates/guestbook.html +71 -0
- templates/home.html +29 -0
- templates/includes/flash.html +8 -0
- templates/login.html +26 -0
- templates/signup.html +28 -0
.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# macos
|
2 |
+
.DS_Store
|
3 |
+
|
4 |
+
# jupyter notebook server
|
5 |
+
.ipynb_checkpoints/
|
6 |
+
|
7 |
+
# various virtualenvs
|
8 |
+
.venv/
|
9 |
+
env/
|
10 |
+
|
11 |
+
# compiled python files
|
12 |
+
__pycache__/
|
13 |
+
*.py[co]
|
14 |
+
|
15 |
+
# backup files
|
16 |
+
# *~
|
17 |
+
|
18 |
+
# vs code
|
19 |
+
*.code-workspace
|
20 |
+
settings.json
|
21 |
+
|
22 |
+
# custom folders
|
23 |
+
_tmp/
|
24 |
+
_soluzioni/
|
25 |
+
.assets/
|
Dockerfile
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use the official Python 3.9 image
|
2 |
+
FROM python:3.11
|
3 |
+
|
4 |
+
# Set the working directory to /code
|
5 |
+
WORKDIR /code
|
6 |
+
|
7 |
+
# Copy the current directory contents into the container at /code
|
8 |
+
COPY ./requirements.txt /code/requirements.txt
|
9 |
+
|
10 |
+
# Install requirements.txt
|
11 |
+
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
12 |
+
|
13 |
+
# Set up a new user named "user" with user ID 1000
|
14 |
+
RUN useradd -m -u 1000 user
|
15 |
+
# Switch to the "user" user
|
16 |
+
USER user
|
17 |
+
# Set home to the user's home directory
|
18 |
+
ENV HOME=/home/user \
|
19 |
+
PATH=/home/user/.local/bin:$PATH
|
20 |
+
|
21 |
+
# Set the working directory to the user's home directory
|
22 |
+
WORKDIR $HOME/app
|
23 |
+
|
24 |
+
# Copy the current directory contents into the container at $HOME/app setting the owner to the user
|
25 |
+
COPY --chown=user . $HOME/app
|
26 |
+
|
27 |
+
# CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
|
28 |
+
CMD ["flask", "run", "--host=0.0.0.0"]
|
29 |
+
# CMD ["python", "-m", "flask", "run", "--host=0.0.0.0"]
|
app.py
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from flask import Flask, render_template, request, session, redirect, flash, url_for, jsonify
|
3 |
+
from flask_sqlalchemy import SQLAlchemy
|
4 |
+
from markupsafe import escape
|
5 |
+
|
6 |
+
BASE_DIR_PATH = os.path.abspath(os.path.dirname(__file__))
|
7 |
+
DATABASE_PATH = os.path.join(BASE_DIR_PATH, 'db.sqlite3')
|
8 |
+
|
9 |
+
app = Flask(__name__)
|
10 |
+
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'+DATABASE_PATH
|
11 |
+
app.config['SECRET_KEY'] = 'mysecretkey'
|
12 |
+
db = SQLAlchemy(app)
|
13 |
+
|
14 |
+
class Utente(db.Model):
|
15 |
+
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
16 |
+
nickname = db.Column(db.String(80), unique=True, nullable=False)
|
17 |
+
username = db.Column(db.String(80), unique=True, nullable=False)
|
18 |
+
password = db.Column(db.String(30), nullable=False)
|
19 |
+
# messaggi = db.relationship('Messaggio', backref=db.backref('user', lazy='dynamic'))
|
20 |
+
# --- OR ---
|
21 |
+
messaggi = db.relationship('Messaggio', back_populates='user', lazy='dynamic')
|
22 |
+
|
23 |
+
class Messaggio(db.Model):
|
24 |
+
id = db.Column(db.Integer, primary_key=True)
|
25 |
+
user_id = db.Column(db.Integer, db.ForeignKey('utente.id'), nullable=False)
|
26 |
+
messaggio = db.Column(db.Text, nullable=False)
|
27 |
+
timestamp = db.Column(db.DateTime, default=db.func.now(), nullable=False)
|
28 |
+
# user = db.relationship('Utente', backref=db.backref('messaggi', lazy='dynamic'))
|
29 |
+
# --- OR ---
|
30 |
+
user = db.relationship('Utente', back_populates='messaggi')
|
31 |
+
|
32 |
+
|
33 |
+
@app.route('/')
|
34 |
+
def home():
|
35 |
+
if 'user_id' in session:
|
36 |
+
user = db.session.query(Utente).get(session['user_id'])
|
37 |
+
return render_template('home.html', user=user)
|
38 |
+
return render_template('home.html')
|
39 |
+
|
40 |
+
|
41 |
+
@app.route('/guestbook')
|
42 |
+
def guestbook():
|
43 |
+
if 'user_id' not in session:
|
44 |
+
return redirect(url_for('login'))
|
45 |
+
return render_template('guestbook.html')
|
46 |
+
|
47 |
+
@app.route('/api/guestbook', methods=['GET', 'POST'])
|
48 |
+
def api_guestbook():
|
49 |
+
if 'user_id' not in session:
|
50 |
+
return jsonify({'error': 'Accesso non autorizzato.'}), 401
|
51 |
+
|
52 |
+
if request.method == 'POST':
|
53 |
+
messaggio = request.json.get('messaggio')
|
54 |
+
if not messaggio:
|
55 |
+
return jsonify({'error': 'Il messaggio non può essere vuoto!'}), 400
|
56 |
+
new_message = Messaggio(user_id=session['user_id'], messaggio=escape(messaggio))
|
57 |
+
db.session.add(new_message)
|
58 |
+
db.session.commit()
|
59 |
+
return jsonify({'success': True}), 201
|
60 |
+
|
61 |
+
messages = Messaggio.query.order_by(Messaggio.timestamp.desc()).all()
|
62 |
+
response = [
|
63 |
+
{'nickname': message.user.nickname, 'messaggio': message.messaggio}
|
64 |
+
for message in messages
|
65 |
+
]
|
66 |
+
return jsonify(response), 200
|
67 |
+
|
68 |
+
|
69 |
+
|
70 |
+
@app.route('/signup', methods=['GET', 'POST'])
|
71 |
+
def signup():
|
72 |
+
if request.method == 'POST':
|
73 |
+
nickname = request.form.get('nickname')
|
74 |
+
username = request.form.get('username')
|
75 |
+
password = request.form.get('password')
|
76 |
+
if not nickname or not username or not password:
|
77 |
+
flash('Tutti i campi sono obbligatori!')
|
78 |
+
return redirect(url_for('signup'))
|
79 |
+
if Utente.query.filter_by(username=username).first() or Utente.query.filter_by(nickname=nickname).first():
|
80 |
+
flash("Il nickname o l'username sono già in uso!")
|
81 |
+
return redirect(url_for('signup'))
|
82 |
+
new_user = Utente(nickname=nickname, username=username, password=password)
|
83 |
+
db.session.add(new_user)
|
84 |
+
db.session.commit()
|
85 |
+
flash('Registrazione effettuata con successo!')
|
86 |
+
return redirect(url_for('login'))
|
87 |
+
return render_template('signup.html')
|
88 |
+
|
89 |
+
|
90 |
+
@app.route('/login', methods=['GET', 'POST'])
|
91 |
+
def login():
|
92 |
+
if request.method == 'POST':
|
93 |
+
username = request.form['username']
|
94 |
+
password = request.form['password']
|
95 |
+
user = Utente.query.filter_by(username=username, password=password).first()
|
96 |
+
if user:
|
97 |
+
session['user_id'] = user.id
|
98 |
+
flash('Login riuscito!')
|
99 |
+
return redirect(url_for('guestbook'))
|
100 |
+
else:
|
101 |
+
flash('Credenziali non valide!')
|
102 |
+
return redirect(url_for('login'))
|
103 |
+
return render_template('login.html')
|
104 |
+
|
105 |
+
@app.route('/logout')
|
106 |
+
def logout():
|
107 |
+
session.pop('user_id', None)
|
108 |
+
flash('Logout effettuato con successo!')
|
109 |
+
return redirect(url_for('home'))
|
110 |
+
|
111 |
+
|
112 |
+
if __name__ == '__main__':
|
113 |
+
with app.app_context():
|
114 |
+
db.create_all()
|
115 |
+
app.run(debug=True)
|
requirements.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
Flask
|
2 |
+
Flask-SQLAlchemy
|
templates/guestbook.html
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title>Guestbook</title>
|
8 |
+
</head>
|
9 |
+
|
10 |
+
<body>
|
11 |
+
<h2>Guestbook</h2>
|
12 |
+
|
13 |
+
<!-- Mostra i messaggi flash -->
|
14 |
+
{% include 'includes/flash.html' %}
|
15 |
+
|
16 |
+
<form id="message-form">
|
17 |
+
<textarea id="message" name="message" required></textarea><br>
|
18 |
+
<button type="submit">Submit</button>
|
19 |
+
</form>
|
20 |
+
|
21 |
+
<h3>Messages:</h3>
|
22 |
+
|
23 |
+
<ul id="messages-list"></ul>
|
24 |
+
|
25 |
+
<script>
|
26 |
+
// Azioni da eseguire quando il DOM è pronto
|
27 |
+
document.addEventListener('DOMContentLoaded', () => {
|
28 |
+
// Legge e mostra i messaggi al caricamento della pagina
|
29 |
+
loadMessages();
|
30 |
+
// Aggiunge un event listener al pulsante di invio del messaggio
|
31 |
+
document.getElementById('message-form').addEventListener('submit', sendMessage);
|
32 |
+
});
|
33 |
+
|
34 |
+
// Funzione per inviare un messaggio al guestbook
|
35 |
+
async function sendMessage(event) {
|
36 |
+
event.preventDefault();
|
37 |
+
let messageTextarea = document.getElementById('message')
|
38 |
+
const message = messageTextarea.value;
|
39 |
+
const response = await fetch('/api/guestbook', {
|
40 |
+
method: 'POST',
|
41 |
+
headers: { 'Content-Type': 'application/json' },
|
42 |
+
body: JSON.stringify({messaggio: message})
|
43 |
+
});
|
44 |
+
const result = await response.json();
|
45 |
+
if (result.success) {
|
46 |
+
loadMessages();
|
47 |
+
messageTextarea.value = '';
|
48 |
+
// document.getElementById('message-form').reset();
|
49 |
+
} else if (result.error){
|
50 |
+
alert(result.error);
|
51 |
+
} else {
|
52 |
+
alert("Errore sconosciuto durante l'invio del messaggio.");
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
// Funzione per leggere i messaggi dal guestbook
|
57 |
+
async function loadMessages() {
|
58 |
+
const response = await fetch('/api/guestbook');
|
59 |
+
const messages = await response.json();
|
60 |
+
const messagesList = document.getElementById('messages-list');
|
61 |
+
messagesList.innerHTML = '';
|
62 |
+
messages.forEach(message => {
|
63 |
+
const li = document.createElement('li');
|
64 |
+
li.textContent = `${message.nickname}: ${message.messaggio}`;
|
65 |
+
messagesList.appendChild(li);
|
66 |
+
});
|
67 |
+
}
|
68 |
+
</script>
|
69 |
+
</body>
|
70 |
+
|
71 |
+
</html>
|
templates/home.html
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title>Home</title>
|
8 |
+
</head>
|
9 |
+
|
10 |
+
<body>
|
11 |
+
<h2>Home</h2>
|
12 |
+
|
13 |
+
<!-- Mostra i messaggi flash -->
|
14 |
+
{% include 'includes/flash.html' %}
|
15 |
+
|
16 |
+
{% if 'user_id' in session %}
|
17 |
+
<p>Benvenuto, {{ user.username }} detto {{ user.nickname }}!</p>
|
18 |
+
<p><a href="{{ url_for('guestbook') }}">Vai al Guestbook</a>.</p>
|
19 |
+
<p>Oppure <a href="{{ url_for('logout') }}">fai il Logout</a>.</p>
|
20 |
+
{% else %}
|
21 |
+
<p>
|
22 |
+
Per accedere fai il <a href="{{ url_for('login') }}">Login</a>
|
23 |
+
oppure, se non hai un account, fai il <a href="{{ url_for('signup') }}">Sign Up</a>
|
24 |
+
</p>
|
25 |
+
{% endif %}
|
26 |
+
|
27 |
+
</body>
|
28 |
+
|
29 |
+
</html>
|
templates/includes/flash.html
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- Mostra i messaggi flash -->
|
2 |
+
{% with messages = get_flashed_messages() %}
|
3 |
+
{% if messages %}
|
4 |
+
{% for message in messages %}
|
5 |
+
<p>{{ message }}</p>
|
6 |
+
{% endfor %}
|
7 |
+
{% endif %}
|
8 |
+
{% endwith %}
|
templates/login.html
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title>Login</title>
|
8 |
+
</head>
|
9 |
+
|
10 |
+
<body>
|
11 |
+
<h2>Login</h2>
|
12 |
+
|
13 |
+
<form method="POST" action="/login">
|
14 |
+
<label for="username">Username:</label>
|
15 |
+
<input type="text" id="username" name="username" required><br>
|
16 |
+
<label for="password">Password:</label>
|
17 |
+
<input type="password" id="password" name="password" required><br>
|
18 |
+
<button type="submit">Login</button>
|
19 |
+
</form>
|
20 |
+
|
21 |
+
<!-- Mostra i messaggi flash -->
|
22 |
+
{% include 'includes/flash.html' %}
|
23 |
+
|
24 |
+
</body>
|
25 |
+
|
26 |
+
</html>
|
templates/signup.html
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title>Sign Up</title>
|
8 |
+
</head>
|
9 |
+
|
10 |
+
<body>
|
11 |
+
<h2>Sign Up</h2>
|
12 |
+
|
13 |
+
<form method="POST" action="/signup">
|
14 |
+
<label for="nickname">Nickname:</label>
|
15 |
+
<input type="text" id="nickname" name="nickname" required><br>
|
16 |
+
<label for="username">Username:</label>
|
17 |
+
<input type="text" id="username" name="username" required><br>
|
18 |
+
<label for="password">Password:</label>
|
19 |
+
<input type="password" id="password" name="password" required><br>
|
20 |
+
<button type="submit">Sign Up</button>
|
21 |
+
</form>
|
22 |
+
|
23 |
+
<!-- Mostra i messaggi flash -->
|
24 |
+
{% include 'includes/flash.html' %}
|
25 |
+
|
26 |
+
</body>
|
27 |
+
|
28 |
+
</html>
|