diff --git a/CHANGELOG.md b/CHANGELOG.md index 24a2232..ff728bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,6 @@ + Codebase refactor (with breaking changes!) + Move ALL config to .env (config.py is NO MORE supported) + Config SITE_NAME replaced with APP_NAME -+ Add CSRF token and flask_WTF ## 0.9.0 diff --git a/README.md b/README.md index 3ee8c96..b96fffc 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,10 @@ This is the server. For the client, see [coriplusapp](https://github.com/sakurag * Add info to your profile * In-site notifications * Public API -* SQLite (or PostgreSQL)-based app +* SQLite-based app ## Requirements -* **Python 3.10+** with **pip**. -* **Flask** web framework. +* **Python 3** only. We don't want to support Python 2. +* **Flask** web framework (also required extension **Flask-Login**). * **Peewee** ORM. -* A \*nix-based OS. - -## Installation - -* Install dependencies: `pip install .` -* Set the `DATABASE_URL` (must be SQLite or PostgreSQL) -* Run the migrations: `sh ./genmig.sh @` -* i forgor - diff --git a/genmig.sh b/genmig.sh deleted file mode 100755 index a29c9fb..0000000 --- a/genmig.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/bash -# GENERATE MIGRATIONS - -source venv/bin/activate && \ -source .env && \ -case "$1" in - ("+") pw_migrate create --auto --auto-source=coriplus.models --directory=src/migrations --database="$DATABASE_URL" "${@:2}" ;; - ("@") pw_migrate migrate --directory=src/migrations --database="$DATABASE_URL" "${@:2}" ;; -esac \ No newline at end of file diff --git a/src/old_migrations/migrate_0_4_to_0_5.py b/migrate_0_4_to_0_5.py similarity index 100% rename from src/old_migrations/migrate_0_4_to_0_5.py rename to migrate_0_4_to_0_5.py diff --git a/src/old_migrations/migrate_0_6_to_0_7.py b/migrate_0_6_to_0_7.py similarity index 100% rename from src/old_migrations/migrate_0_6_to_0_7.py rename to migrate_0_6_to_0_7.py diff --git a/src/old_migrations/migrate_0_7_to_0_8.py b/migrate_0_7_to_0_8.py similarity index 100% rename from src/old_migrations/migrate_0_7_to_0_8.py rename to migrate_0_7_to_0_8.py diff --git a/pyproject.toml b/pyproject.toml index 76e1140..f4d5b53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,10 +8,7 @@ dependencies = [ "Python-Dotenv>=1.0.0", "Flask", "Flask-Login", - "Peewee", - "Flask-WTF", - "peewee-migrate", - "PsycoPG2" + "Peewee" ] requires-python = ">=3.10" classifiers = [ diff --git a/src/coriplus/__init__.py b/src/coriplus/__init__.py index 98b4d85..4f01715 100644 --- a/src/coriplus/__init__.py +++ b/src/coriplus/__init__.py @@ -20,7 +20,6 @@ from flask import ( send_from_directory, __version__ as flask_version) import os, sys from flask_login import LoginManager -from flask_wtf import CSRFProtect import dotenv import logging @@ -45,8 +44,6 @@ app.secret_key = os.environ['SECRET_KEY'] login_manager = LoginManager(app) -CSRFProtect(app) - from .models import * from .utils import * @@ -67,10 +64,7 @@ def before_request(): @app.after_request def after_request(response): - try: - g.db.close() - except Exception: - logger.error('database closed twice') + g.db.close() return response @app.context_processor @@ -85,10 +79,6 @@ def _inject_variables(): def _inject_user(userid): return User[userid] -@app.errorhandler(403) -def error_403(body): - return render_template('403.html'), 403 - @app.errorhandler(404) def error_404(body): return render_template('404.html'), 404 diff --git a/src/coriplus/admin.py b/src/coriplus/admin.py index a78cf1b..b8fb4b9 100644 --- a/src/coriplus/admin.py +++ b/src/coriplus/admin.py @@ -4,8 +4,7 @@ Management of reports and the entire site. New in 0.8. ''' -from flask import Blueprint, abort, redirect, render_template, request, url_for -from flask_login import current_user +from flask import Blueprint, redirect, render_template, request, url_for from .models import User, Message, Report, report_reasons, REPORT_STATUS_ACCEPTED, \ REPORT_MEDIA_USER, REPORT_MEDIA_MESSAGE from .utils import pwdhash, object_list @@ -13,18 +12,21 @@ from functools import wraps bp = Blueprint('admin', __name__, url_prefix='/admin') -def _check_auth(username, password) -> bool: +def check_auth(username, password): try: - return User.select().where((User.username == username) & (User.password == pwdhash(password)) & (User.is_admin) - ).exists() + return User.get((User.username == username) & (User.password == pwdhash(password)) + ).is_admin except User.DoesNotExist: return False def admin_required(f): @wraps(f) def wrapped_view(**kwargs): - if not _check_auth(current_user.username, current_user.password): - abort(403) + auth = request.authorization + if not (auth and check_auth(auth.username, auth.password)): + return ('Unauthorized', 401, { + 'WWW-Authenticate': 'Basic realm="Login Required"' + }) return f(**kwargs) return wrapped_view diff --git a/src/coriplus/models.py b/src/coriplus/models.py index 5a2ae50..4cdb2b5 100644 --- a/src/coriplus/models.py +++ b/src/coriplus/models.py @@ -13,14 +13,11 @@ The tables are: from flask import request from peewee import * -from playhouse.db_url import connect import os - -from . import BASEDIR # here should go `from .utils import get_current_user`, but it will cause # import errors. It's instead imported at function level. -database = connect(os.environ['DATABASE_URL']) +database = SqliteDatabase('coriplus.sqlite') class BaseModel(Model): class Meta: @@ -192,7 +189,7 @@ class Relationship(BaseModel): ) -UPLOAD_DIRECTORY = os.path.join(BASEDIR, 'uploads') +UPLOAD_DIRECTORY = os.path.join(os.path.split(os.path.dirname(__file__))[0], 'uploads') class Upload(BaseModel): # the extension of the media diff --git a/src/coriplus/static/style.css b/src/coriplus/static/style.css index c238285..87152a8 100644 --- a/src/coriplus/static/style.css +++ b/src/coriplus/static/style.css @@ -1,41 +1,19 @@ -:root { - --accent: #f0372e; - --link: #3399ff; -} -body, button, input, select, textarea { - font-family: Inter, Roboto, sans-serif; - line-height: 1.6; - background-color: #eeeeee; -} -#site-name, h1, h2, h3, h4, h5, h6 { - font-family: 'Funnel Sans', Roboto, sans-serif; - line-height: 1.2; -} +body,button,input,select,textarea{font-family:Roboto,'Segoe UI',Arial,Helvetica,sans-serif} body{margin:0} -main{margin:auto; max-width: 960px;} a{text-decoration:none} a:hover{text-decoration:underline} -.mobile-collapse {font-size: smaller;} @media (max-width:640px){ .mobile-collapse{display:none} } -.header{ - padding:12px;color:white; background-color:var(--accent); box-shadow:0 0 3px 3px #ccc; display: flex; position: relative; -} -.centered{ - text-align: center; -} +.header{padding:12px;color:white;background-color:#ff3018;box-shadow:0 0 3px 3px #ccc} .content{padding:12px} -.header a{color:white; } +.header a{color:white} .header a svg{fill:white} -.content a{color:var(--link)} -.content a svg{fill:var(--link)} -.content a.plus{color:var(--accent)} -.leftnav, .rightnav{list-style: none; display: flex; padding: 0; margin: 0;position: absolute;} -.leftnav {left: 0} -.rightnav {right: 0} -.card {background-color: #fafafa; border-radius: 12px; padding: 12px; margin-bottom: 12px; border-bottom: 2px solid #999999;} -#site-name {text-align: center;flex: 1} +.content a{color:#3399ff} +.content a svg{fill:#3399ff} +.content a.plus{color:#ff3018} +.metanav{float:right} +.metanav-divider{width:1px;background:white;display:inline-block} .header h1{margin:0;display:inline-block} .flash{background-color:#ff9;border:yellow 1px solid} .infobox{padding:12px;border:#ccc 1px solid} @@ -45,15 +23,15 @@ a:hover{text-decoration:underline} .weak{opacity:.5} .field_desc{display:block} ul.timeline{padding:0;margin:auto;max-width:960px} -ul.timeline > li{list-style:none;} +ul.timeline > li{list-style:none;border-bottom:#808080 1px solid} .message-visual img{max-width:100%;margin:auto} .message-options-showhide::before{content:'\2026'} .message-options{display:none} .create_text{width:100%;height:8em} .biography_text{height:4em} .before-toggle:not(:checked) + input{display:none} -.follow_button,input[type="submit"]{background-color:var(--accent);color:white;border-radius:3px;border:1px solid var(--accent)} -.follow_button.following{background-color:transparent;color:var(--accent);border-color:var(--accent)} +.follow_button,input[type="submit"]{background-color:#ff3018;color:white;border-radius:3px;border:1px solid #ff3018} +.follow_button.following{background-color:transparent;color:#ff3018;border-color:#ff3018} .copyright{font-size:smaller;text-align:center;color:#808080} .copyright a:link,.copyright a:visited{color:#31559e} .copyright ul{list-style:none;padding:0} diff --git a/src/coriplus/templates/403.html b/src/coriplus/templates/403.html deleted file mode 100644 index 5cdbf3b..0000000 --- a/src/coriplus/templates/403.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} - -{% block body %} -
Copyright © 2019, 2025 Sakuragasaki46.
+{{ site_name }} {{ version }} – Python {{ python_version }} – + Flask {{ flask_version }}
+Copyright © 2019 Sakuragasaki46.
-Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions:
-The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-Source code for this site: - https://github.com/sakuragasaki46/coriplus/ +
Source code for this site: + https://github.com/sakuragasaki46/coriplus/ -