log in and sign up pages, db schema
This commit is contained in:
cube
2026-03-16 01:34:55 +00:00
parent 887ccffe9a
commit d367d459d4
11 changed files with 538 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
.venv/
*.pyc
__pycache__/
instance/
.pytest_cache/
.coverage
htmlcov/
dist/
build/
*.egg-info/

39
myriad/__init__.py Normal file
View File

@@ -0,0 +1,39 @@
import os
from flask import Flask
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'database.sqlite'),
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
os.makedirs(app.instance_path, exist_ok=True)
# a simple page that says hello
@app.route('/hello')
def hello():
return 'Hello, World!'
from . import db
db.init_app(app)
from . import auth
app.register_blueprint(auth.bp)
from . import home
app.register_blueprint(home.bp)
app.add_url_rule('/', endpoint='index')
return app

92
myriad/auth.py Normal file
View File

@@ -0,0 +1,92 @@
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from myriad.db import get_db
bp = Blueprint('auth', __name__, url_prefix='/auth')
@bp.route('/register', methods=('GET', 'POST'))
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
if not username:
error = 'Username is required.'
elif not password:
error = 'Password is required.'
if error is None:
try:
db.execute(
"INSERT INTO user (username, password) VALUES (?, ?)",
(username, generate_password_hash(password)),
)
db.commit()
except db.IntegrityError:
error = f"User {username} is already registered."
else:
return redirect(url_for("auth.login"))
flash(error)
return render_template('auth/register.html')
@bp.route('/login', methods=('GET', 'POST'))
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
user = db.execute(
'SELECT * FROM user WHERE username = ?', (username,)
).fetchone()
if user is None:
error = 'Incorrect username.'
elif not check_password_hash(user['password'], password):
error = 'Incorrect password.'
if error is None:
session.clear()
session['user_id'] = user['id']
return redirect(url_for('index'))
flash(error)
return render_template('auth/login.html')
@bp.before_app_request
def load_logged_in_user():
user_id = session.get('user_id')
if user_id is None:
g.user = None
else:
g.user = get_db().execute(
'SELECT * FROM user WHERE id = ?', (user_id,)
).fetchone()
@bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
def login_required(view):
@functools.wraps(view)
def wrapped_view(**kwargs):
if g.user is None:
return redirect(url_for('auth.login'))
return view(**kwargs)
return wrapped_view

47
myriad/db.py Normal file
View File

@@ -0,0 +1,47 @@
import sqlite3
from datetime import datetime
import click
from flask import current_app, g
def get_db():
if 'db' not in g:
g.db = sqlite3.connect(
current_app.config['DATABASE'],
detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
db = g.pop('db', None)
if db is not None:
db.close()
def init_db():
db = get_db()
with current_app.open_resource('schema.sql') as f:
db.executescript(f.read().decode('utf8'))
def init_app(app):
app.teardown_appcontext(close_db)
app.cli.add_command(init_db_command)
@click.command('init-db')
def init_db_command():
"""Clear the existing data and create new tables."""
init_db()
click.echo('Initialized the database.')
sqlite3.register_converter(
"timestamp", lambda v: datetime.fromisoformat(v.decode())
)

19
myriad/home.py Normal file
View File

@@ -0,0 +1,19 @@
from flask import (
Blueprint, flash, g, redirect, render_template, request, url_for
)
from werkzeug.exceptions import abort
from myriad.auth import login_required
from myriad.db import get_db
bp = Blueprint('home', __name__)
@bp.route('/')
def index():
# db = get_db()
# posts = db.execute(
# 'SELECT p.id, title, body, created, author_id, username'
# ' FROM post p JOIN user u ON p.author_id = u.id'
# ' ORDER BY created DESC'
# ).fetchall()
return render_template('index.html')

61
myriad/schema.sql Normal file
View File

@@ -0,0 +1,61 @@
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS member;
DROP TABLE IF EXISTS icons;
DROP TABLE IF EXISTS groups;
DROP TABLE IF EXISTS group_members;
DROP TABLE IF EXISTS user_front;
DROP TABLE IF EXISTS pages;
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE member (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
member_name TEXT NOT NULL,
subtitle TEXT,
bio TEXT,
main_icon INTEGER,
homepage BOOLEAN NOT NULL DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES user (id),
FOREIGN KEY (main_icon) REFERENCES icons (id)
);
CREATE TABLE icons (
id INTEGER PRIMARY KEY AUTOINCREMENT,
member_id INTEGER NOT NULL,
icon_location TEXT NOT NULL,
FOREIGN KEY (member_id) REFERENCES member (id)
);
CREATE TABLE groups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_name TEXT NOT NULL
);
CREATE TABLE group_members (
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_id INTEGER NOT NULL,
member_id INTEGER NOT NULL,
FOREIGN KEY (group_id) REFERENCES groups (id),
FOREIGN KEY (member_id) REFERENCES member (id)
);
CREATE TABLE user_front (
id INTEGER PRIMARY KEY AUTOINCREMENT,
member_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
FOREIGN KEY (member_id) REFERENCES member (id),
FOREIGN KEY (user_id) REFERENCES user (id)
);
CREATE TABLE pages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
member_id INTEGER NOT NULL,
page_location TEXT NOT NULL,
FOREIGN KEY (member_id) REFERENCES member (id)
);

196
myriad/static/style.css Normal file
View File

@@ -0,0 +1,196 @@
@font-face {
font-family: 'daydream';
src: url('/fonts/daydream.otf');
}
body{
background: #00b7ff;
background: linear-gradient(90deg, rgba(0, 183, 255, 1) 0%, rgba(87, 199, 133, 1) 50%, rgba(237, 221, 83, 1) 100%);
font-family:monospace;
font-size:12px;
scrollbar-color:#008bcc #b3e7ff;
}
hr{
width:50%;
margin-top:20px;
margin-bottom:20px;
}
a{
text-decoration:none;
}
a:hover{
font-weight:bold;
}
form{
margin:20px;
}
label,input{
margin:10px;
}
#main{
background-color: rgb(255, 255, 255, 0);
border-radius: 5px;
width: 60%;
margin:auto;
padding:20px;
display:flex;
margin-top:20px;
}
#nav{
margin-right:20px;
flex:20%;
height:fit-content;
position:sticky;
position: -webkit-sticky;
}
.navitem{
display:block;
}
.container{
background-color:#e6f7ff;
border-color:#99dfff;
border-style:solid;
border-width:2px;
flex: 80%;
padding: 10px;
border-radius:5px;
}
.profile{
margin:15px;
border-style:solid;
border-width:1.2px;
border-color:#99dfff;
border-radius:5px;
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
}
.clear { clear: both; }
.dsgame{
position:relative;
float:right;
margin-right:10px;
margin-bottom:10px;
}
.heading{
background-color:#b3e7ff;
border-left:solid;
border-color:#008bcc;
padding: 4px 0px 8px 10px;
margin-bottom: 5px;
margin:15px;
border-width:5px;
}
.icon{
display:inline;
float:left;
width:120px;
height:auto;
border-radius:10px;
margin:12px;
border-style:solid;
border-width:2px;
border-color:#008bcc;
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
}
.bio{
display:inline-block;
vertical-align:middle;
padding:10px;
width:60%;
color:black;
}
.minis{
padding:20px;
}
.divider{
width:50%;
height:auto;
margin:auto;
display:block;
}
.title{
font-size:20px;
margin-top:20px;
margin-bottom:20px;
display:block;
}
.heading.big{
font-size:20px;
font-weight:bold;
}
.heading.links{
position:relative;
float:left;
padding-right:20px;
}
.maintext{
display:block;
}
#blog{
max-height:300px;
overflow-y:scroll;
}
.post .title{
font-size:20px;
margin:10px;
}
.post .timestamp{
font-style:italic;
font-size:10px;
color:rgb(80, 80, 80);
margin:10px;
}
.post .content{
margin:10px;
}
.post{
border-width:1px;
border-radius:5px;
border-style:solid;
margin:20px;
}
.imgbullet{
margin-right:10px;
}
.imgright{
display: block;
margin-left: auto;
}
#frontbanner{
font-size:20px;
}
#frontbanner sub{
font-size:10px;
}
@media (max-width: 1000px) {
#main{
width:90%;
}
#nav{
display:none;
}
}

View File

@@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% block header %}
<div class="title">{% block title %}Log In{% endblock %}</div>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required><br>
<label for="password">Password</label>
<input type="password" name="password" id="password" required><br>
<input type="submit" value="Log In">
</form>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% block header %}
<div class="title">{% block title %}Register{% endblock %}</div>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required><br>
<label for="password">Password</label>
<input type="password" name="password" id="password" required><br>
<input type="submit" value="Register">
</form>
{% endblock %}

View File

@@ -0,0 +1,29 @@
<!doctype html>
<title>{% block title %}{% endblock %} - Myriad</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<div id="main">
<div class="container" id="nav">
<div class="heading">myriad</div>
<div class="navitem">> <a href="{{ url_for('index') }}">Home</a></div>
{% if g.user %}
<div class="navitem">> <a href="{{ url_for('auth.logout') }}">Log out</a></div>
{% else %}
<div class="navitem">> <a href="{{ url_for('auth.register') }}">Register</a></div>
<div class="navitem">> <a href="{{ url_for('auth.login') }}">Log in</a></div>
{% endif %}
</div>
<div class="container">
{% block header %}{% endblock %}
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% block content %}{% endblock %}
</div>
</div>

View File

@@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block header %}
<div class="title">{% block title %}Welcome{% endblock %}</div>
{% endblock %}
{% block content %}
<div class="maintext">
homepage :)
</div>
{% endblock %}