mirror of
				https://github.com/tiyn/container-critique.git
				synced 2025-10-31 19:21:20 +01:00 
			
		
		
		
	src: login works
This commit is contained in:
		
							
								
								
									
										35
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								README.md
									
									
									
									
									
								
							| @@ -5,9 +5,15 @@ The blog is intended to be used to review and critique things. | ||||
|  | ||||
| ## Features/To-Dos | ||||
|  | ||||
| - [ ] Plain text support for blog entries | ||||
|   - [ ] HTML files (.html) | ||||
|   - [ ] Markdown Files (.md) | ||||
|  | ||||
| - [ ] Accounts | ||||
|   - [x] Login | ||||
|   - [x] Logout | ||||
|   - [ ] Register | ||||
| - [ ] Review blog entries | ||||
|   - [ ] Writing entries | ||||
|   - [ ] Editing entries | ||||
|   - [ ] Deleting entries | ||||
| - [ ] Infinite-scroll blog page | ||||
| - [ ] Archive page | ||||
|   - [ ] Months as headings | ||||
| @@ -16,14 +22,16 @@ The blog is intended to be used to review and critique things. | ||||
| - [ ] Standalone article page | ||||
|   - [ ] Links to scrolling blog page | ||||
| - [ ] RSS feed | ||||
| - [ ] Navigation | ||||
|   - [ ] Header | ||||
|   - [ ] Footer | ||||
| - [ ] Switchable CSS | ||||
|   - [ ] CSS dark-theme | ||||
|   - [ ] CSS light-theme | ||||
| - [ ] Config file | ||||
| - [ ] Docker installation | ||||
| - [ ] Eye candy | ||||
|   - [ ] Star rating | ||||
|   - [ ] Rich text editor | ||||
| - [x] Navigation | ||||
|   - [x] Header | ||||
|   - [x] Footer | ||||
| - [x] Switchable CSS | ||||
|   - [x] CSS dark-theme | ||||
|   - [x] CSS light-theme | ||||
| - [x] Docker installation | ||||
| - [ ] Logo | ||||
|  | ||||
| ## Usage | ||||
| @@ -49,11 +57,10 @@ The `config.py` can be found in the `src` folder. | ||||
| Set the following volumes with the -v tag. | ||||
|  | ||||
| | Volume-Name   | Container mount        | Description                        | | ||||
| | ------------- | --------------------------- | ------------------------------------------------------------ | | ||||
| | ------------- | ---------------------- | ---------------------------------- | | ||||
| | `config-file` | `/blog/src/config.py`  | Config file                        | | ||||
| | `entries`     | `/blog/src/templates/entry` | Directory for blog entries                                   | | ||||
| | `css`         | `/blog/src/static/css` | (optional) Directory for css files | | ||||
| | `html`        | `/blog/src/templates`       | (optional) Directory for templates (entry-volume not needed) | | ||||
| | `html`        | `/blog/src/templates`  | (optional) Directory for templates | | ||||
|  | ||||
| #### Ports | ||||
|  | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										76
									
								
								src/app.py
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								src/app.py
									
									
									
									
									
								
							| @@ -1,58 +1,24 @@ | ||||
| from flask import Flask, flash, make_response, render_template, request, redirect, abort, url_for | ||||
| from flask_login import current_user, login_user, LoginManager, logout_user | ||||
| from flask_wtf import CSRFProtect | ||||
|  | ||||
| import content as con_gen | ||||
| import config | ||||
|  | ||||
| from flask_wtf import FlaskForm | ||||
| from wtforms import StringField, PasswordField, SubmitField, BooleanField | ||||
| from wtforms.validators import DataRequired | ||||
|  | ||||
|  | ||||
| app = Flask(__name__) | ||||
| login = LoginManager(app) | ||||
| login.login_view = 'login' | ||||
| csrf = CSRFProtect() | ||||
| app.secret_key = "123534" | ||||
| csrf.init_app(app) | ||||
|  | ||||
| class LoginForm(FlaskForm): | ||||
|     username = StringField("Username", validators=[DataRequired()]) | ||||
|     password = PasswordField("Password", validators=[DataRequired()]) | ||||
|     remember_me = BooleanField("Remember Me") | ||||
|     submit = SubmitField("Sign In") | ||||
| login = LoginManager(app) | ||||
| login.login_view = "login" | ||||
|  | ||||
| TITLE = config.TITLE | ||||
| STYLE = config.STYLE | ||||
| DESCRIPTION = config.DESCRIPTION | ||||
| WEBSITE = config.WEBSITE | ||||
|  | ||||
| from werkzeug.security import generate_password_hash, check_password_hash | ||||
|  | ||||
| class User(): | ||||
|  | ||||
|     def __init__(self, username): | ||||
|         self.username = username | ||||
|         self.id = 1 | ||||
|         self.is_active = True | ||||
|         self.is_authenticated = False | ||||
|         self.is_anonymous = False | ||||
|  | ||||
|     def set_password(self, password): | ||||
|         self.password_hash = generate_password_hash(password) | ||||
|  | ||||
|     def check_password(self, password): | ||||
|         return check_password_hash(self.password_hash, password) | ||||
|  | ||||
|     def get_id(self): | ||||
|         return self.id | ||||
|  | ||||
| u = User("marten") | ||||
| u.set_password("test") | ||||
|  | ||||
| class Config(object): | ||||
|     SECRET_KEY = "123534" | ||||
|  | ||||
| app.config.from_object(Config) | ||||
|  | ||||
|  | ||||
| @app.errorhandler(404) | ||||
| def page_not_found(e): | ||||
|     return render_template("error.html", title=TITLE, errorcode="404", style=STYLE), 404 | ||||
| @@ -67,7 +33,7 @@ def index(): | ||||
|  | ||||
| @app.route("/archive") | ||||
| @app.route("/archive.html") | ||||
| def blog_archive(): | ||||
| def archive(): | ||||
|     content = con_gen.gen_arch_string() | ||||
|     return render_template("archive.html", title=TITLE, content_string=content, style=STYLE) | ||||
|  | ||||
| @@ -91,24 +57,36 @@ def feed(): | ||||
|     return response | ||||
|  | ||||
| @login.user_loader | ||||
| def load_user(id): | ||||
| def load_user(ident): | ||||
|     ## TODO: load user from db by id | ||||
|     return id | ||||
|     db_user = db.get_by_id(ident) | ||||
|     if db_user is not None: | ||||
|         return db.db_to_user(*db_user) | ||||
|     return None | ||||
|  | ||||
| from login import LoginForm, User | ||||
| from database import Database | ||||
|  | ||||
| db = Database() | ||||
|  | ||||
| @app.route("/login", methods=["GET", "POST"]) | ||||
| @app.route("/login.html", methods=["GET", "POST"]) | ||||
| def login(): | ||||
|     #if current_user.is_authenticated: | ||||
|     #    return redirect("/index") | ||||
|     if current_user.is_authenticated: | ||||
|         return redirect(url_for("index")) | ||||
|     form = LoginForm() | ||||
|     if form.validate_on_submit(): | ||||
|         user = u | ||||
|         #user = form.username.data | ||||
|         if user is None or not user.check_password(form.password.data): | ||||
|         db_user = db.get_by_name(form.username.data) | ||||
|         if db_user is None: | ||||
|             flash("Invalid username or password") | ||||
|             return redirect(url_for("login")) | ||||
|         user = db.db_to_user(*db_user) | ||||
|         if not user.check_password(form.password.data): | ||||
|             flash("Invalid username or password") | ||||
|             return redirect(url_for("login")) | ||||
|         login_user(user, remember=form.remember_me.data) | ||||
|         return redirect(url_for("index")) | ||||
|     return render_template("login.html", title="Sign In", form=form, style=STYLE) | ||||
|     return render_template("login.html", title=TITLE, form=form, style=STYLE) | ||||
|  | ||||
| @app.route('/logout') | ||||
| def logout(): | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| ENTRY_DIR = "templates/entry" | ||||
|  | ||||
| def gen_arch_string(): | ||||
|     """ | ||||
|     Creates and returns a archive string of every file in ENTRY_DIR. | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								src/data.db
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/data.db
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										66
									
								
								src/database.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/database.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| import os | ||||
| import sqlite3 | ||||
|  | ||||
| from login import User | ||||
|  | ||||
|  | ||||
| class Database: | ||||
|  | ||||
|     def __init__(self): | ||||
|         self.TABLE_FILE = 'USERS' | ||||
|         self.DB_DIR = os.path.dirname(".") | ||||
|         self.setup_db() | ||||
|  | ||||
|     def connect(self): | ||||
|         """Connect to an existing database instance based on the object | ||||
|         attributes. | ||||
|         """ | ||||
|         path = os.path.join(self.DB_DIR, "data.db") | ||||
|         return sqlite3.connect(path) | ||||
|  | ||||
|     def setup_db(self): | ||||
|         """Creates a database with tables.""" | ||||
|         db = self.connect() | ||||
|         crs = db.cursor() | ||||
|         query = "CREATE TABLE IF NOT EXISTS " + self.TABLE_FILE + \ | ||||
|             "(id INTEGER PRIMARY KEY AUTOINCREMENT," + \ | ||||
|             "name CHAR(32) NOT NULL UNIQUE," + \ | ||||
|             "password CHAR(32) NOT NULL)" | ||||
|         crs.execute(query) | ||||
|  | ||||
|     def insert_user(self, name, password): | ||||
|         """Insert a new user into the database. | ||||
|         """ | ||||
|         if self.check_name(name): | ||||
|             db = self.connect() | ||||
|             crs = db.cursor() | ||||
|             query = "INSERT INTO " + self.TABLE_FILE + "(`name`,`password`)" + \ | ||||
|                     "VALUES (?, ?) ON CONFLICT DO NOTHING" | ||||
|             crs.execute(query, (name, password)) | ||||
|             db.commit() | ||||
|             return True | ||||
|         return False | ||||
|  | ||||
|     def check_name(self, name): | ||||
|         if self.get_by_name(name) is None: | ||||
|             return True | ||||
|         return False | ||||
|  | ||||
|     def get_by_id(self, ident): | ||||
|         db = self.connect() | ||||
|         crs = db.cursor() | ||||
|         query = "SELECT * FROM " + self.TABLE_FILE + " WHERE id = ?" | ||||
|         crs.execute(query, (ident, )) | ||||
|         return crs.fetchone() | ||||
|  | ||||
|     def get_by_name(self, name): | ||||
|         db = self.connect() | ||||
|         crs = db.cursor() | ||||
|         query = "SELECT * FROM " + self.TABLE_FILE + " WHERE name = ?" | ||||
|         crs.execute(query, (name, )) | ||||
|         return crs.fetchone() | ||||
|  | ||||
|     def db_to_user(self, ident, name, pass_hash): | ||||
|         user = User(name, pass_hash) | ||||
|         user.set_id(ident) | ||||
|         return user | ||||
							
								
								
									
										34
									
								
								src/login.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/login.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| from flask_wtf import FlaskForm | ||||
| from wtforms import StringField, PasswordField, SubmitField, BooleanField | ||||
| from wtforms.validators import DataRequired | ||||
|  | ||||
| from werkzeug.security import generate_password_hash, check_password_hash | ||||
|  | ||||
|  | ||||
| class User(): | ||||
|  | ||||
|     def __init__(self, name, pass_hash=None): | ||||
|         self.name = name | ||||
|         self.id = 0 | ||||
|         self.is_active = True | ||||
|         self.is_authenticated = True | ||||
|         self.is_anonymous = False | ||||
|         self.pass_hash = pass_hash | ||||
|  | ||||
|     def set_password(self, password): | ||||
|         self.pass_hash = generate_password_hash(password) | ||||
|  | ||||
|     def set_id(self, ident): | ||||
|         self.id = ident | ||||
|  | ||||
|     def check_password(self, password): | ||||
|         return check_password_hash(self.pass_hash, password) | ||||
|  | ||||
|     def get_id(self): | ||||
|         return self.id | ||||
|  | ||||
| class LoginForm(FlaskForm): | ||||
|     username = StringField("Username", validators=[DataRequired()]) | ||||
|     password = PasswordField("Password", validators=[DataRequired()]) | ||||
|     remember_me = BooleanField("Remember Me") | ||||
|     submit = SubmitField("Sign In") | ||||
| @@ -1,4 +1,5 @@ | ||||
| Flask==2.1.2 | ||||
| flask_login==0.6.2 | ||||
| flask_wtf==1.0.1 | ||||
| WTForms==3.0.1 | ||||
| Flask_Login==0.6.2 | ||||
| Flask_WTF==0.14.3 | ||||
| Werkzeug==2.1.2 | ||||
| WTForms==2.2.1 | ||||
|   | ||||
| @@ -1,6 +1,9 @@ | ||||
| {% extends "template.html" %} | ||||
|  | ||||
| {% block content %} | ||||
|  | ||||
| <div class="container"> | ||||
|     <div class="logging"> | ||||
|         <h1>Sign In</h1> | ||||
|         <form action="" method="post" novalidate> | ||||
|             {{ form.hidden_tag() }} | ||||
| @@ -14,5 +17,10 @@ | ||||
|             </p> | ||||
|             <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p> | ||||
|             <p>{{ form.submit() }}</p> | ||||
|             {% for mesg in get_flashed_messages() %} | ||||
|             <p>{{ mesg }}</p> | ||||
|             {% endfor %} | ||||
|         </form> | ||||
|     </div> | ||||
| </div> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -13,13 +13,13 @@ | ||||
|         <input type="checkbox" id="main-menu-check"> | ||||
|         <label for="main-menu-check" class="show-menu">☰</label> | ||||
|         <div class="main-menu"> | ||||
|             <a href="/">Blog</a> | ||||
|             <a href="/archive">Archive</a> | ||||
|             <a href="{{ url_for('index') }}">Blog</a> | ||||
|             <a href="{{ url_for('archive') }}">Archive</a> | ||||
|             <label for="main-menu-check" class="hide-menu">X</label> | ||||
|             {% if current_user.is_anonymous %} | ||||
|             <a href="/login">Login</a> | ||||
|             <a href="{{ url_for('login') }}">Login</a> | ||||
|             {% else %} | ||||
|             <a href="/logout">Logout</a> | ||||
|             <a href="{{ url_for('logout') }}">Logout</a> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|     </div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user