diff --git a/.gitignore b/.gitignore
index 19c746c..2a1cdfd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -116,4 +116,6 @@ dmypy.json
# Personal things!
-setup_env.sh
+web.env
+db.env
+dump.sql
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..ecc9d76
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,16 @@
+FROM python:alpine
+
+RUN mkdir /app
+COPY requirements.txt gunicorn.conf run.py /app/
+COPY www/ /app/www
+ENV FLASK_APP=www
+WORKDIR /app
+RUN apk add mariadb-connector-c-dev gcc musl-dev
+RUN pip install --no-cache-dir -r requirements.txt
+RUN apk del gcc musl-dev
+
+ENV GUNICORN_WORKERS 2
+ENV GUNICORN_BIND 0.0.0.0:8081
+
+EXPOSE 8081
+CMD ["gunicorn", "--config", "gunicorn.conf", "run:app"]
diff --git a/db.env.example b/db.env.example
new file mode 100644
index 0000000..ff92198
--- /dev/null
+++ b/db.env.example
@@ -0,0 +1,2 @@
+MYSQL_ROOT_PASSWORD=secret
+MYSQL_DATABASE=db
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..39b44c3
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,21 @@
+version: '3'
+services:
+ web:
+ build: .
+ ports:
+ - "8081:8081"
+ volumes:
+ - ./www:/app/www
+ depends_on:
+ - db
+ env_file:
+ - web.env
+ command: "gunicorn --config gunicorn.conf run:app"
+ db:
+ image: mariadb
+ volumes:
+ - dbdata:/var/lib/mysql
+ env_file:
+ - db.env
+volumes:
+ dbdata:
diff --git a/dump.sql b/dump.sql
new file mode 100644
index 0000000..b46fd90
--- /dev/null
+++ b/dump.sql
@@ -0,0 +1,27 @@
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE posts (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ title TEXT NOT NULL,
+ markdown TEXT NOT NULL,
+ html TEXT NOT NULL,
+ resume TEXT NOT NULL,
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+CREATE TABLE now (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ markdown TEXT NOT NULL,
+ html TEXT NOT NULL
+);
+INSERT INTO now VALUES(1,replace(replace('### Esta es mi pagina /now.\r\n\r\nTome esta idea del señor [Derek Sivers](https://sivers.org/now), con quien me encontré tras estar buscando varias paginas personales en posts de [dev.to](https://dev.to/) y [reddit](https://www.reddit.com/r/web_design/comments/6glc6i/best_ridiculously_simple_portfolio_sites/), resulta que varias de ellas tenían esta sección y me pareció lo suficientemente interesante como para añadirla aquí.\r\n\r\n__Actualizada en 14/02/2018__\r\n\r\n#### Programación:\r\n\r\n- Estoy haciendo esta pagina nuevamente, cometí un grave error al intentarlo hacer 100% html estático, fue un sufrimiento manejar los links y todo eso, así que me decante por hacerla con Flask y SQLite para mantener todo correcto y poder hacerla mínimamente dinámica en su interior, como por ejemplo, ahora hice un pequeño panel de administración en el cual puedo subir posts para el blog y actualizar el now utilizando Markdown, es una maravilla!, ahora le quiero agregar uno para añadir recomendaciones y poder agregar links a los proyectos que e hecho.\r\n- Estoy haciendo una aplicación para la contabilidad de las cajas diarias de nuestro restaurant familiar, es una pequeña aplicación en Java y SQLite nuevamente, tenia que ser una base de dato embebida dado que ya que en los computadores en que se utiliza hay un servidor MySQL, el cual es version 4.3 si no me equivoco, por lo que seria un problema que no quiero solucionar, y a costa de esto tuve que mas o menos hacerme un "ORM" para poder comunicarme con SQLite porque parece que Java no se lleva muy bien con esta base de datos e Hibernate no pareciera tener soporte.\r\n\r\n#### Aprendiendo:\r\n\r\n- Primeramente ahora estoy metido con java e intento hacer un software lo mas correcto posible por lo que estoy intentando aprender un poco de patrones y utilizarlos en la vida real.\r\n- Flask! Con el cual estoy haciendo esta pagina, me gusta bastante, es conciso y limpio cuando lo comparo con lo que hacia con PHP y Laravel, al menos a mi parecer, supongo que gracias a que es Python3.\r\n- Realice un cambio de universidad desde la UFRO a la Inacap por motivos varios. Por lo que ahora estoy en una universidad nueva y tristemente pareciera que quede un semestre mas atras de lo que me hubiese tocado, pero hay que salir adelante y aprovechar la oportunidad de repasar cosas que quizas estoy pasando por alto.\r\n\r\n#### Libros:\r\n\r\n__(Aviso! Soy un muy mal lector, por lo que estos libros probablemente se mantendrán aquí un largo tiempo antes que los termine)__\r\n\r\n- En Las Montañas de la Locura, H.P Lovecraft. Este esta en un compilado junto a "Los Sueños en la Casa de la Bruja" y "La Sombra Sobre Innsmouth" los cuales ya termine y debo admitir me encantaron.\r\n- IT, Stephen King. Este lo tengo en mi velador todas las noches, lo veo y no lo leo, no me doy el tiempo de tomarlo y continuarlo, llevo un tercio de el y aun no me e dado el tiempo de continuar.\r\n- Don Quijote De La Mancha, Miguel de Cervantes. Bien loco, me he reído bastante mientras e ido leyendo, aunque debo decir que lo tengo tirado y no lo e avanzado, debería.\r\n\r\n\r\n','\r',char(13)),'\n',char(10)),replace('
Esta es mi pagina /now.
\nTome esta idea del señor Derek Sivers, con quien me encontré tras estar buscando varias paginas personales en posts de dev.to y reddit, resulta que varias de ellas tenían esta sección y me pareció lo suficientemente interesante como para añadirla aquí.
\nActualizada en 14/02/2018
\nProgramación:
\n\n- Estoy haciendo esta pagina nuevamente, cometí un grave error al intentarlo hacer 100% html estático, fue un sufrimiento manejar los links y todo eso, así que me decante por hacerla con Flask y SQLite para mantener todo correcto y poder hacerla mínimamente dinámica en su interior, como por ejemplo, ahora hice un pequeño panel de administración en el cual puedo subir posts para el blog y actualizar el now utilizando Markdown, es una maravilla!, ahora le quiero agregar uno para añadir recomendaciones y poder agregar links a los proyectos que e hecho.
\n- Estoy haciendo una aplicación para la contabilidad de las cajas diarias de nuestro restaurant familiar, es una pequeña aplicación en Java y SQLite nuevamente, tenia que ser una base de dato embebida dado que ya que en los computadores en que se utiliza hay un servidor MySQL, el cual es version 4.3 si no me equivoco, por lo que seria un problema que no quiero solucionar, y a costa de esto tuve que mas o menos hacerme un "ORM" para poder comunicarme con SQLite porque parece que Java no se lleva muy bien con esta base de datos e Hibernate no pareciera tener soporte.
\n
\nAprendiendo:
\n\n- Primeramente ahora estoy metido con java e intento hacer un software lo mas correcto posible por lo que estoy intentando aprender un poco de patrones y utilizarlos en la vida real.
\n- Flask! Con el cual estoy haciendo esta pagina, me gusta bastante, es conciso y limpio cuando lo comparo con lo que hacia con PHP y Laravel, al menos a mi parecer, supongo que gracias a que es Python3.
\n- Realice un cambio de universidad desde la UFRO a la Inacap por motivos varios. Por lo que ahora estoy en una universidad nueva y tristemente pareciera que quede un semestre mas atras de lo que me hubiese tocado, pero hay que salir adelante y aprovechar la oportunidad de repasar cosas que quizas estoy pasando por alto.
\n
\nLibros:
\n(Aviso! Soy un muy mal lector, por lo que estos libros probablemente se mantendrán aquí un largo tiempo antes que los termine)
\n\n- En Las Montañas de la Locura, H.P Lovecraft. Este esta en un compilado junto a "Los Sueños en la Casa de la Bruja" y "La Sombra Sobre Innsmouth" los cuales ya termine y debo admitir me encantaron.
\n- IT, Stephen King. Este lo tengo en mi velador todas las noches, lo veo y no lo leo, no me doy el tiempo de tomarlo y continuarlo, llevo un tercio de el y aun no me e dado el tiempo de continuar.
\n- Don Quijote De La Mancha, Miguel de Cervantes. Bien loco, me he reído bastante mientras e ido leyendo, aunque debo decir que lo tengo tirado y no lo e avanzado, debería.
\n
','\n',char(10)));
+CREATE TABLE users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ username TEXT NOT NULL,
+ password TEXT NOT NULL
+);
+INSERT INTO users VALUES(1,'ryuuji','pbkdf2:sha256:50000$0fDBIsdx$b1866621bed6c2405d54c8cc617203f6fa51bc2a68b03c7cb57a49c28ebe1e9e');
+DELETE FROM sqlite_sequence;
+INSERT INTO sqlite_sequence VALUES('now',1);
+INSERT INTO sqlite_sequence VALUES('users',1);
+INSERT INTO sqlite_sequence VALUES('posts',1);
+COMMIT;
diff --git a/gunicorn.conf b/gunicorn.conf
new file mode 100644
index 0000000..b1bd733
--- /dev/null
+++ b/gunicorn.conf
@@ -0,0 +1,6 @@
+import os
+
+for k,v in os.environ.items():
+ if k.startswith("GUNICORN_"):
+ key = k.split('_', 1)[1].lower()
+ locals()[key] = v
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..d01b541
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,5 @@
+Flask
+Flask-SQLAlchemy
+mysqlclient
+gunicorn
+markdown
diff --git a/run.py b/run.py
new file mode 100644
index 0000000..3045cd9
--- /dev/null
+++ b/run.py
@@ -0,0 +1,6 @@
+from www import create_app
+
+app = create_app()
+
+if __name__ == '__main__':
+ app.run(host='0.0.0.0', debug=True)
diff --git a/web.env.example b/web.env.example
new file mode 100644
index 0000000..000a67d
--- /dev/null
+++ b/web.env.example
@@ -0,0 +1,7 @@
+DATABASE_URI=mysql://root:secret@db:3306/db
+TRACK_MODIFICATIONS=False
+
+USERNAME=admin
+PASSWORD=secret
+
+SECRET_KEY=1337
diff --git a/wsgi.py b/wsgi.py
deleted file mode 100644
index e9762e6..0000000
--- a/wsgi.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from www import create_app
-
-app = create_app()
-
-if __name__ == "__main__":
- app.run()
diff --git a/www/__init__.py b/www/__init__.py
index 34765d6..ad09396 100644
--- a/www/__init__.py
+++ b/www/__init__.py
@@ -5,43 +5,41 @@ from flask import redirect, url_for, render_template
def create_app():
app = Flask(__name__, instance_relative_config=True)
-
app.config.from_mapping(
- DATABASE=os.path.join(app.instance_path, 'www.sqlite'),
+ SQLALCHEMY_DATABASE_URI=os.environ.get("DATABASE_URI"),
+ SQLALCHEMY_TRACK_MODIFICATIONS=os.environ.get("TRACK_MODIFICATIONS"),
+ USERNAME=os.environ.get("USERNAME"),
+ PASSWORD=os.environ.get("PASSWORD"),
+ SECRET_KEY=os.environ.get("SECRET_KEY")
)
- app.config.from_pyfile('config.py', silent=True)
-
- try:
- os.makedirs(app.instance_path)
- except OSError:
- pass
-
@app.route('/')
def index():
- return redirect(url_for('blog.index'))
+ return redirect(url_for('blog.index'))
- from . import db
+ from www.models import db
db.init_app(app)
- from . import auth
+ from www import commands
+ commands.init_app(app)
+
+ from www import auth
app.register_blueprint(auth.bp)
- from . import admin
+ from www import admin
app.register_blueprint(admin.bp)
- from . import blog
+ from www import blog
app.register_blueprint(blog.bp)
- from . import now
+ from www import now
app.register_blueprint(now.bp)
- from . import recommended
+ from www import recommended
app.register_blueprint(recommended.bp)
- from . import projects
+ from www import projects
app.register_blueprint(projects.bp)
return app
-
diff --git a/www/admin.py b/www/admin.py
index 828624f..849431b 100644
--- a/www/admin.py
+++ b/www/admin.py
@@ -1,6 +1,5 @@
-from flask import Blueprint, flash, g, redirect, render_template, request, session, url_for
+from flask import Blueprint, render_template
-from www.db import get_db
from www.auth import admin_required
bp = Blueprint('admin', __name__, url_prefix='/admin')
diff --git a/www/auth.py b/www/auth.py
index a3631e4..a078d40 100644
--- a/www/auth.py
+++ b/www/auth.py
@@ -1,11 +1,12 @@
import functools
-from www.db import get_db
-
from werkzeug.exceptions import abort
from werkzeug.security import check_password_hash
-from flask import Blueprint, request, flash, render_template, session, g, redirect, url_for
+from flask import Blueprint, render_template, request, session
+from flask import url_for, flash, redirect, g
+
+from www.models import User
bp = Blueprint('auth', __name__, url_prefix='/auth')
@@ -16,21 +17,18 @@ def login():
username = request.form['username']
password = request.form['password']
- db = get_db()
error = None
- user = db.execute(
- 'SELECT * FROM users WHERE username = ?', (username,)
- ).fetchone()
+ user = User.query.filter_by(username=username).first()
if user is None:
error = 'Incorrect username.'
- elif not check_password_hash(user['password'], password):
+ elif not check_password_hash(user.password, password):
error = 'Incorrect password.'
if error is None:
session.clear()
- session['user_id'] = user['id']
+ session['user_id'] = user.id
return redirect(url_for('index'))
flash(error)
@@ -49,9 +47,7 @@ def load_logged_in_user():
if user_id is None:
g.user = None
else:
- g.user = get_db().execute(
- 'SELECT * FROM users WHERE id = ?', (user_id,)
- ).fetchone()
+ g.user = User.query.get(user_id)
def admin_required(view):
@functools.wraps(view)
diff --git a/www/blog.py b/www/blog.py
index be86d31..0a286b4 100644
--- a/www/blog.py
+++ b/www/blog.py
@@ -1,23 +1,16 @@
-from flask import (
- Blueprint, flash, g, redirect, render_template, request, url_for
-)
+from flask import Blueprint, flash, redirect, render_template, request, url_for
from werkzeug.exceptions import abort
import markdown as md
-from www.db import get_db
-from www.auth import admin_required
+from www.models import db, Post
+from www.auth import admin_required
bp = Blueprint('blog', __name__, url_prefix='/blog')
@bp.route('/')
def index():
- db = get_db()
- posts = db.execute(
- 'SELECT id, title, resume, created_at'
- ' FROM posts'
- ' ORDER BY created_at DESC'
- ).fetchall()
+ posts = Post.query.order_by(Post.created_at.desc()).all()
return render_template('blog/index.html', posts=posts)
@@ -38,13 +31,10 @@ def create():
if error is not None:
flash(error)
else:
- db = get_db()
- db.execute(
- 'INSERT INTO posts (title, markdown, html, resume)'
- ' VALUES (?, ?, ?, ?)',
- (title, markdown, html, resume)
- )
- db.commit()
+ post = Post(title, markdown, html, resume)
+ db.session.add(post)
+ db.session.commit()
+
return redirect(url_for('blog.index'))
return render_template('blog/create.html')
@@ -52,12 +42,7 @@ def create():
@bp.route('/update/', methods=('GET', 'POST'))
@admin_required
def update(id):
- post = get_db().execute(
- 'SELECT id, title, markdown, resume'
- ' FROM posts'
- ' WHERE id = ?',
- (id,)
- ).fetchone()
+ post = Post.query.get(id)
if post is None:
abort(404)
@@ -76,55 +61,37 @@ def update(id):
if error is not None:
flash(error)
else:
- db = get_db()
- db.execute(
- 'UPDATE posts'
- ' SET title = ?, markdown = ?, html = ?, resume = ?'
- ' WHERE id = ?',
- (title, markdown, html, resume, id)
- )
- db.commit()
+ post.title = title
+ post.markdown = markdown
+ post.html = html
+ post.resume = resume
+ db.session.commit()
return redirect(url_for('blog.index'))
return render_template('blog/update.html', post=post)
@bp.route('/')
def view(id):
- post = get_db().execute(
- 'SELECT id, title, html'
- ' FROM posts'
- ' WHERE id = ?',
- (id,)
- ).fetchone()
-
+ post = Post.query.get(id)
+
if post is None:
abort(404)
else:
return render_template('blog/view.html', post=post)
-@bp.route('/delete/', methods=('GET', 'POST',))
+@bp.route('/delete/', methods=['POST'])
@admin_required
def delete(id):
- if request.method == 'GET':
- abort(404)
+ post = Post.query.get(id)
- db = get_db()
- post = db.execute(
- 'SELECT id'
- ' FROM posts'
- ' WHERE id = ?',
- (id,)
- ).fetchone()
- print(post)
-
if post is None:
abort(404)
else:
- db.execute('DELETE FROM posts WHERE id = ?', (id,))
- db.commit()
+ db.session.delete(post)
+ db.session.commit()
return redirect(url_for('blog.index'))
def parse_markdown(markdown):
- html = md.markdown(markdown)
+ html = md.markdown(markdown)
return html
diff --git a/www/commands.py b/www/commands.py
new file mode 100644
index 0000000..11aa410
--- /dev/null
+++ b/www/commands.py
@@ -0,0 +1,46 @@
+import click
+
+from flask import current_app
+from flask.cli import with_appcontext
+
+from werkzeug.security import generate_password_hash
+
+from www.models import db, User, Now
+
+
+def init_db():
+ db.create_all()
+
+
+def generate_admin():
+ username = current_app.config['USERNAME']
+
+ if User.query.filter_by(username=username).first() is None:
+ password = current_app.config['PASSWORD']
+
+ user = User(username, generate_password_hash(password))
+ db.session.add(user)
+ db.session.commit()
+
+def generate_base_now():
+ now = Now.query.first()
+ if now is None:
+ now = Now('', '')
+ db.session.add(now)
+ db.session.commit()
+
+
+@click.command('init-db')
+@with_appcontext
+def init_db_command():
+ """
+ Creates and initializes the db with the necesary data
+ If the db existed previously, it keeps it and his data
+ """
+ init_db()
+ generate_admin()
+ generate_base_now()
+
+
+def init_app(app):
+ app.cli.add_command(init_db_command)
diff --git a/www/db.py b/www/db.py
deleted file mode 100644
index e6eae5e..0000000
--- a/www/db.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import sqlite3
-import click
-
-from flask import current_app, g
-from flask.cli import with_appcontext
-from werkzeug.security import generate_password_hash
-
-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 fill_with_dummy():
- db = get_db()
-
- with current_app.open_resource('dummy.sql') as f:
- db.executescript(f.read().decode('utf8'))
-
-def generate_admin():
- db = get_db()
- username = current_app.config['USERNAME']
- password = current_app.config['PASSWORD']
-
- db.execute(
- 'INSERT INTO users (username, password) VALUES (?, ?)',
- (username, generate_password_hash(password))
- )
- db.commit()
-
-@click.command('init-db')
-@with_appcontext
-def init_db_command():
- """Initializes the schema of the database"""
- init_db()
- click.echo('Initialized the database')
-
-@click.command('fill-with-dummy')
-@with_appcontext
-def fill_with_dummy_command():
- """Adds dummy data to the existing database"""
- fill_with_dummy()
- click.echo('Database filled with dummy data')
-
-@click.command('generate-admin')
-@with_appcontext
-def generate_admin_command():
- """Creates the admin account to be used"""
- generate_admin()
- click.echo('Created admin')
-
-
-def init_app(app):
- app.teardown_appcontext(close_db)
- app.cli.add_command(init_db_command)
- app.cli.add_command(fill_with_dummy_command)
- app.cli.add_command(generate_admin_command)
-
diff --git a/www/dummy.sql b/www/dummy.sql
deleted file mode 100644
index 6ca3bb3..0000000
--- a/www/dummy.sql
+++ /dev/null
@@ -1,25 +0,0 @@
-INSERT INTO posts (title, markdown, html, resume) values
-(
- "Excepturi et debitis explicabo occaecati",
- "Sit necessitatibus quibusdam autem facere tempore quibusdam assumenda. Quo sit asperiores earum beatae unde reiciendis perspiciatis. Blanditiis natus ex similique possimus optio veritatis.",
- "Sit necessitatibus quibusdam autem facere tempore quibusdam assumenda. Quo sit asperiores earum beatae unde reiciendis perspiciatis. Blanditiis natus ex similique possimus optio veritatis.",
- "Enim unde ut eaque vero saepe ut. Iure culpa modi ipsam maxime aliquid sed officiis. Consequatur minima quaerat molestias laudantium aut. Quia voluptas sint accusantium architecto."
-),
-(
- "Excepturi et debitis explicabo occaecati",
- "Sit necessitatibus quibusdam autem facere tempore quibusdam assumenda. Quo sit asperiores earum beatae unde reiciendis perspiciatis. Blanditiis natus ex similique possimus optio veritatis.",
- "Sit necessitatibus quibusdam autem facere tempore quibusdam assumenda. Quo sit asperiores earum beatae unde reiciendis perspiciatis. Blanditiis natus ex similique possimus optio veritatis.",
- "Enim unde ut eaque vero saepe ut. Iure culpa modi ipsam maxime aliquid sed officiis. Consequatur minima quaerat molestias laudantium aut. Quia voluptas sint accusantium architecto."
-),
-(
- "Excepturi et debitis explicabo occaecati",
- "Sit necessitatibus quibusdam autem facere tempore quibusdam assumenda. Quo sit asperiores earum beatae unde reiciendis perspiciatis. Blanditiis natus ex similique possimus optio veritatis.",
- "Sit necessitatibus quibusdam autem facere tempore quibusdam assumenda. Quo sit asperiores earum beatae unde reiciendis perspiciatis. Blanditiis natus ex similique possimus optio veritatis.",
- "Enim unde ut eaque vero saepe ut. Iure culpa modi ipsam maxime aliquid sed officiis. Consequatur minima quaerat molestias laudantium aut. Quia voluptas sint accusantium architecto."
-),
-(
- "Excepturi et debitis explicabo occaecati",
- "Sit necessitatibus quibusdam autem facere tempore quibusdam assumenda. Quo sit asperiores earum beatae unde reiciendis perspiciatis. Blanditiis natus ex similique possimus optio veritatis.",
- "Sit necessitatibus quibusdam autem facere tempore quibusdam assumenda. Quo sit asperiores earum beatae unde reiciendis perspiciatis. Blanditiis natus ex similique possimus optio veritatis.",
- "Enim unde ut eaque vero saepe ut. Iure culpa modi ipsam maxime aliquid sed officiis. Consequatur minima quaerat molestias laudantium aut. Quia voluptas sint accusantium architecto."
-);
diff --git a/www/models.py b/www/models.py
new file mode 100644
index 0000000..7d73c48
--- /dev/null
+++ b/www/models.py
@@ -0,0 +1,53 @@
+from datetime import datetime
+from flask_sqlalchemy import SQLAlchemy
+
+db = SQLAlchemy()
+
+class User(db.Model):
+ __tablename__ = 'users'
+ id = db.Column(db.Integer, primary_key=True)
+ username = db.Column(db.String(255), unique=True, nullable=False)
+ password = db.Column(db.String(255), nullable=False)
+
+ def __init__(self, username=None, password=None):
+ self.username = username
+ self.password = password
+
+ def __repr__(self):
+ return f'User {self.username}>'
+
+
+class Post(db.Model):
+ __tablename__ = 'posts'
+ id = db.Column(db.Integer, primary_key=True)
+ title = db.Column(db.Text, nullable=False)
+ markdown = db.Column(db.Text, nullable=False)
+ html = db.Column(db.Text, nullable=False)
+ resume = db.Column(db.Text, nullable=False)
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
+
+ def __init__(self, title=None, markdown=None, html=None, resume=None):
+ self.title = title
+ self.markdown = markdown
+ self.html = html
+ self.resume = resume
+
+
+ def __repr__(self):
+ return f''
+
+
+class Now(db.Model):
+ __tablename__ = 'nows'
+ id = db.Column(db.Integer, primary_key=True)
+ markdown = db.Column(db.Text, nullable=False)
+ html = db.Column(db.Text, nullable=False)
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
+
+ def __init__(self, markdown=None, html=None):
+ self.markdown = markdown
+ self.html = html
+
+
+ def __repr__(self):
+ return f''
diff --git a/www/now.py b/www/now.py
index 4c2a4ad..b16875d 100644
--- a/www/now.py
+++ b/www/now.py
@@ -1,25 +1,21 @@
-from flask import (
- Blueprint, flash, g, redirect, render_template, request, url_for
-)
-
-from werkzeug.exceptions import abort
import markdown as md
-from www.db import get_db
-from www.auth import admin_required
+from flask import Blueprint, redirect, render_template, request, url_for
+
+from www.auth import admin_required
+from www.models import db, Now
+
bp = Blueprint('now', __name__, url_prefix='/now')
+
@bp.route('/')
def index():
- db = get_db()
- now = db.execute(
- 'SELECT html'
- ' FROM now'
- ).fetchone()
+ now = Now.query.order_by(Now.created_at.desc()).first()
return render_template('now/now.html', now=now)
+
@bp.route('/update', methods=('GET', 'POST'))
@admin_required
def update():
@@ -27,21 +23,14 @@ def update():
markdown = request.form['markdown']
html = parse_markdown(markdown)
- db = get_db()
- db.execute(
- 'UPDATE now'
- ' SET markdown = ?, html = ?',
- (markdown, html,)
- )
- db.commit()
+ now = Now(markdown, html)
+ db.session.add(now)
+ db.session.commit()
return redirect(url_for('now.index'))
- now = get_db().execute(
- 'SELECT id, markdown'
- ' FROM now'
- ).fetchone()
+ now = Now.query.order_by(Now.created_at.desc()).first()
return render_template('now/update.html', now=now)
def parse_markdown(markdown):
- html = md.markdown(markdown)
+ html = md.markdown(markdown)
return html
diff --git a/www/projects.py b/www/projects.py
index 5af750f..8301b3f 100644
--- a/www/projects.py
+++ b/www/projects.py
@@ -1,17 +1,7 @@
-from flask import (
- Blueprint, flash, g, redirect, render_template, request, url_for
-)
-
-from werkzeug.exceptions import abort
-import markdown as md
-
-from www.db import get_db
-from www.auth import admin_required
+from flask import Blueprint, render_template
bp = Blueprint('projects', __name__, url_prefix='/projects')
@bp.route('/')
def index():
return render_template('empty.html')
-
-
diff --git a/www/recommended.py b/www/recommended.py
index 59b73ac..0c3647c 100644
--- a/www/recommended.py
+++ b/www/recommended.py
@@ -1,17 +1,8 @@
-from flask import (
- Blueprint, flash, g, redirect, render_template, request, url_for
-)
+from flask import Blueprint, render_template
-from werkzeug.exceptions import abort
-import markdown as md
-
-from www.db import get_db
-from www.auth import admin_required
bp = Blueprint('recommended', __name__, url_prefix='/recommended')
@bp.route('/')
def index():
return render_template('empty.html')
-
-
diff --git a/www/schema.sql b/www/schema.sql
deleted file mode 100644
index 21cf560..0000000
--- a/www/schema.sql
+++ /dev/null
@@ -1,28 +0,0 @@
-DROP TABLE IF EXISTS posts;
-DROP TABLE IF EXISTS now;
-DROP TABLE IF EXISTS users;
-
-CREATE TABLE posts (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- title TEXT NOT NULL,
- markdown TEXT NOT NULL,
- html TEXT NOT NULL,
- resume TEXT NOT NULL,
- created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
-);
-
-CREATE TABLE now (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- markdown TEXT NOT NULL,
- html TEXT NOT NULL
-);
-
-CREATE TABLE users (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- username TEXT NOT NULL,
- password TEXT NOT NULL
-);
-
-INSERT INTO now (markdown, html) VALUES (
- "",""
-);