mirror of
				https://github.com/tiyn/container-critique.git
				synced 2025-10-31 11:11:21 +01:00 
			
		
		
		
	src: login works
This commit is contained in:
		
							
								
								
									
										41
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								README.md
									
									
									
									
									
								
							| @@ -5,9 +5,15 @@ The blog is intended to be used to review and critique things. | |||||||
|  |  | ||||||
| ## Features/To-Dos | ## Features/To-Dos | ||||||
|  |  | ||||||
| - [ ] Plain text support for blog entries |  | ||||||
|   - [ ] HTML files (.html) | - [ ] Accounts | ||||||
|   - [ ] Markdown Files (.md) |   - [x] Login | ||||||
|  |   - [x] Logout | ||||||
|  |   - [ ] Register | ||||||
|  | - [ ] Review blog entries | ||||||
|  |   - [ ] Writing entries | ||||||
|  |   - [ ] Editing entries | ||||||
|  |   - [ ] Deleting entries | ||||||
| - [ ] Infinite-scroll blog page | - [ ] Infinite-scroll blog page | ||||||
| - [ ] Archive page | - [ ] Archive page | ||||||
|   - [ ] Months as headings |   - [ ] Months as headings | ||||||
| @@ -16,14 +22,16 @@ The blog is intended to be used to review and critique things. | |||||||
| - [ ] Standalone article page | - [ ] Standalone article page | ||||||
|   - [ ] Links to scrolling blog page |   - [ ] Links to scrolling blog page | ||||||
| - [ ] RSS feed | - [ ] RSS feed | ||||||
| - [ ] Navigation | - [ ] Eye candy | ||||||
|   - [ ] Header |   - [ ] Star rating | ||||||
|   - [ ] Footer |   - [ ] Rich text editor | ||||||
| - [ ] Switchable CSS | - [x] Navigation | ||||||
|   - [ ] CSS dark-theme |   - [x] Header | ||||||
|   - [ ] CSS light-theme |   - [x] Footer | ||||||
| - [ ] Config file | - [x] Switchable CSS | ||||||
| - [ ] Docker installation |   - [x] CSS dark-theme | ||||||
|  |   - [x] CSS light-theme | ||||||
|  | - [x] Docker installation | ||||||
| - [ ] Logo | - [ ] Logo | ||||||
|  |  | ||||||
| ## Usage | ## Usage | ||||||
| @@ -48,12 +56,11 @@ The `config.py` can be found in the `src` folder. | |||||||
|  |  | ||||||
| Set the following volumes with the -v tag. | Set the following volumes with the -v tag. | ||||||
|  |  | ||||||
| | Volume-Name   | Container mount             | Description                                                  | | | Volume-Name   | Container mount        | Description                        | | ||||||
| | ------------- | --------------------------- | ------------------------------------------------------------ | | | ------------- | ---------------------- | ---------------------------------- | | ||||||
| | `config-file` | `/blog/src/config.py`       | Config file                                                  | | | `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 | | ||||||
| | `css`         | `/blog/src/static/css`      | (optional) Directory for css files                           | | | `html`        | `/blog/src/templates`  | (optional) Directory for templates | | ||||||
| | `html`        | `/blog/src/templates`       | (optional) Directory for templates (entry-volume not needed) | |  | ||||||
|  |  | ||||||
| #### Ports | #### 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 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_login import current_user, login_user, LoginManager, logout_user | ||||||
|  | from flask_wtf import CSRFProtect | ||||||
|  |  | ||||||
| import content as con_gen | import content as con_gen | ||||||
| import config | import config | ||||||
|  |  | ||||||
| from flask_wtf import FlaskForm |  | ||||||
| from wtforms import StringField, PasswordField, SubmitField, BooleanField |  | ||||||
| from wtforms.validators import DataRequired |  | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Flask(__name__) | app = Flask(__name__) | ||||||
| login = LoginManager(app) | csrf = CSRFProtect() | ||||||
| login.login_view = 'login' | app.secret_key = "123534" | ||||||
|  | csrf.init_app(app) | ||||||
|  |  | ||||||
| class LoginForm(FlaskForm): | login = LoginManager(app) | ||||||
|     username = StringField("Username", validators=[DataRequired()]) | login.login_view = "login" | ||||||
|     password = PasswordField("Password", validators=[DataRequired()]) |  | ||||||
|     remember_me = BooleanField("Remember Me") |  | ||||||
|     submit = SubmitField("Sign In") |  | ||||||
|  |  | ||||||
| TITLE = config.TITLE | TITLE = config.TITLE | ||||||
| STYLE = config.STYLE | STYLE = config.STYLE | ||||||
| DESCRIPTION = config.DESCRIPTION | DESCRIPTION = config.DESCRIPTION | ||||||
| WEBSITE = config.WEBSITE | 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) | @app.errorhandler(404) | ||||||
| def page_not_found(e): | def page_not_found(e): | ||||||
|     return render_template("error.html", title=TITLE, errorcode="404", style=STYLE), 404 |     return render_template("error.html", title=TITLE, errorcode="404", style=STYLE), 404 | ||||||
| @@ -67,7 +33,7 @@ def index(): | |||||||
|  |  | ||||||
| @app.route("/archive") | @app.route("/archive") | ||||||
| @app.route("/archive.html") | @app.route("/archive.html") | ||||||
| def blog_archive(): | def archive(): | ||||||
|     content = con_gen.gen_arch_string() |     content = con_gen.gen_arch_string() | ||||||
|     return render_template("archive.html", title=TITLE, content_string=content, style=STYLE) |     return render_template("archive.html", title=TITLE, content_string=content, style=STYLE) | ||||||
|  |  | ||||||
| @@ -91,24 +57,36 @@ def feed(): | |||||||
|     return response |     return response | ||||||
|  |  | ||||||
| @login.user_loader | @login.user_loader | ||||||
| def load_user(id): | def load_user(ident): | ||||||
|     ## TODO: load user from db by id |     ## 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", methods=["GET", "POST"]) | ||||||
|  | @app.route("/login.html", methods=["GET", "POST"]) | ||||||
| def login(): | def login(): | ||||||
|     #if current_user.is_authenticated: |     if current_user.is_authenticated: | ||||||
|     #    return redirect("/index") |         return redirect(url_for("index")) | ||||||
|     form = LoginForm() |     form = LoginForm() | ||||||
|     if form.validate_on_submit(): |     if form.validate_on_submit(): | ||||||
|         user = u |         db_user = db.get_by_name(form.username.data) | ||||||
|         #user = form.username.data |         if db_user is None: | ||||||
|         if user is None or not user.check_password(form.password.data): |             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") |             flash("Invalid username or password") | ||||||
|             return redirect(url_for("login")) |             return redirect(url_for("login")) | ||||||
|         login_user(user, remember=form.remember_me.data) |         login_user(user, remember=form.remember_me.data) | ||||||
|         return redirect(url_for("index")) |         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') | @app.route('/logout') | ||||||
| def logout(): | def logout(): | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| ENTRY_DIR = "templates/entry" |  | ||||||
|  |  | ||||||
| def gen_arch_string(): | def gen_arch_string(): | ||||||
|     """ |     """ | ||||||
|     Creates and returns a archive string of every file in ENTRY_DIR. |     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==2.1.2 | ||||||
| flask_login==0.6.2 | Flask_Login==0.6.2 | ||||||
| flask_wtf==1.0.1 | Flask_WTF==0.14.3 | ||||||
| WTForms==3.0.1 | Werkzeug==2.1.2 | ||||||
|  | WTForms==2.2.1 | ||||||
|   | |||||||
| @@ -1,18 +1,26 @@ | |||||||
| {% extends "template.html" %} | {% extends "template.html" %} | ||||||
|  |  | ||||||
| {% block content %} | {% block content %} | ||||||
|     <h1>Sign In</h1> |  | ||||||
|     <form action="" method="post" novalidate> | <div class="container"> | ||||||
|         {{ form.hidden_tag() }} |     <div class="logging"> | ||||||
|         <p> |         <h1>Sign In</h1> | ||||||
|             {{ form.username.label }}<br> |         <form action="" method="post" novalidate> | ||||||
|             {{ form.username(size=32) }} |             {{ form.hidden_tag() }} | ||||||
|         </p> |             <p> | ||||||
|         <p> |                 {{ form.username.label }}<br> | ||||||
|             {{ form.password.label }}<br> |                 {{ form.username(size=32) }} | ||||||
|             {{ form.password(size=32) }} |             </p> | ||||||
|         </p> |             <p> | ||||||
|         <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p> |                 {{ form.password.label }}<br> | ||||||
|         <p>{{ form.submit() }}</p> |                 {{ form.password(size=32) }} | ||||||
|     </form> |             </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 %} | {% endblock %} | ||||||
|   | |||||||
| @@ -13,13 +13,13 @@ | |||||||
|         <input type="checkbox" id="main-menu-check"> |         <input type="checkbox" id="main-menu-check"> | ||||||
|         <label for="main-menu-check" class="show-menu">☰</label> |         <label for="main-menu-check" class="show-menu">☰</label> | ||||||
|         <div class="main-menu"> |         <div class="main-menu"> | ||||||
|             <a href="/">Blog</a> |             <a href="{{ url_for('index') }}">Blog</a> | ||||||
|             <a href="/archive">Archive</a> |             <a href="{{ url_for('archive') }}">Archive</a> | ||||||
|             <label for="main-menu-check" class="hide-menu">X</label> |             <label for="main-menu-check" class="hide-menu">X</label> | ||||||
|             {% if current_user.is_anonymous %} |             {% if current_user.is_anonymous %} | ||||||
|             <a href="/login">Login</a> |             <a href="{{ url_for('login') }}">Login</a> | ||||||
|             {% else %} |             {% else %} | ||||||
|             <a href="/logout">Logout</a> |             <a href="{{ url_for('logout') }}">Logout</a> | ||||||
|             {% endif %} |             {% endif %} | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user