Compare commits
6 Commits
7aa3a9382e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
40219f2b54
|
|||
|
4a45b62521
|
|||
|
fc55aaf87a
|
|||
|
db68ef2c3d
|
|||
|
a808137e5b
|
|||
|
a93a89f0df
|
73
README.md
73
README.md
@@ -1,18 +1,70 @@
|
||||
# Pyrom
|
||||
python/flask port of [porom](https://git.poto.cafe/yagich/porom)
|
||||
pyrom is a playful home-grown forum software for the indie web borne out of frustration with social media and modern forums imitating it.
|
||||
|
||||
this is now the canonical implementation of porom. it's compatible with the database of porom.
|
||||
the aim is not to recreate the feeling of forums from any time period. rather, it aims to serve as a lightweight alternative to other forum software packages. pyrom is lean and "fire-and-forget"; there is little necessary configuration, making it a great fit for smaller communities (though nothing prevents it from being used in larger ones.)
|
||||
|
||||
# License
|
||||
Released under [CNPLv7+](https://thufie.lain.haus/NPL.html).
|
||||
Please read the [full terms](./LICENSE.md) for proper wording.
|
||||
a live example can be seen in action over at [Porom](https://forum.poto.cafe/).
|
||||
|
||||
## stack & structure
|
||||
on the server side, pyrom is built in Python using the Flask framework. content is rendered mostly server-side with Jinja templates. the database used is SQLite.
|
||||
|
||||
on the client side, JS with only one library ([Bitty](https://bitty-js.com)) is used. for CSS, pyrom uses Sass.
|
||||
|
||||
below is an explanation of the folder structure:
|
||||
|
||||
- `/`
|
||||
- `app/`
|
||||
- `lib/` - utility libraries
|
||||
- `routes/` - each `.py` file represents a "sub-app", usually the first part of the URL
|
||||
- `templates/` - Jinja templates used by the routes. each subfolder corresponds to the "sub-app" that uses that template.
|
||||
- `__init__.py` - creates the app
|
||||
- `auth.py` - authentication helper
|
||||
- `constants.py` - constant values used throughout the forum
|
||||
- `db.py` - database abstraction layer and ORM library
|
||||
- `migrations.py` - database migrations
|
||||
- `models.py` - ORM model definitions
|
||||
- `run.py` - runner script for development
|
||||
- `schema.py` - database schema definition
|
||||
- `config/` - configuration for the forum
|
||||
- `data/`
|
||||
- `_cached/` - cached versions of certain endpoints are stored here
|
||||
- `db/` - the SQLite database is stored here
|
||||
- `static/` - static files
|
||||
- `avatars/` - user avatar uploads
|
||||
- `badges/` - user badge uploads
|
||||
- `css/` - CSS files generated from Sass sources
|
||||
- `emoji/` - emoji images used on the forum
|
||||
- `fonts/`
|
||||
- `js/`
|
||||
- `sass/`
|
||||
- `_default.scss` - the default theme. Sass variables that other themes modify are defined here, along with the default styles. other files define the available themes.
|
||||
- `build-themes.sh` - script for building Sass files into CSS
|
||||
- `nginx.conf` - nginx config (production only)
|
||||
- `uwsgi.ini` - uwsgi config (production only)
|
||||
|
||||
# license
|
||||
released under [CNPLv7+](https://thufie.lain.haus/NPL.html).
|
||||
please read the [full terms](./LICENSE.md) for proper wording.
|
||||
|
||||
# acknowledgments
|
||||
|
||||
pyrom uses many open-source and otherwise free-culture components. see the [THIRDPARTY](./THIRDPARTY.md) file for full credit.
|
||||
|
||||
# installing & first time setup
|
||||
## docker (production)
|
||||
create `config/secrets.prod.env` according to `config/secrets.prod.env.example`
|
||||
1. clone the repo
|
||||
2. create `config/secrets.prod.env` according to `config/secrets.prod.env.example`
|
||||
3. create `config/pyrom_config.toml` according to `config/pyrom_config.toml.example` and modify as needed
|
||||
4. make sure the `data/` folder is writable by the app:
|
||||
|
||||
```bash
|
||||
$ docker compose up
|
||||
$ chmod -R 777 data/
|
||||
```
|
||||
|
||||
5. bring up the container:
|
||||
|
||||
```bash
|
||||
$ docker compose up --build
|
||||
```
|
||||
|
||||
- opens port 8080
|
||||
@@ -20,10 +72,10 @@ $ docker compose up
|
||||
|
||||
make sure to run it in an interactive session the first time, because it will spit out the password to the auto-created admin account.
|
||||
|
||||
alternatively, if you already had porom running before, put the db file (`db.prod.sqlite`) in `data/db` and it will Just Work.
|
||||
6. point your favorite proxy at `localhost:8080`
|
||||
|
||||
## manual (development)
|
||||
1. install python >= 3.11, sqlite3, libargon2, and imagemagick & clone repo
|
||||
1. install python >= 3.13, sqlite3, libargon2, and imagemagick & clone repo
|
||||
2. create a venv:
|
||||
|
||||
```bash
|
||||
@@ -59,6 +111,3 @@ $ source .venv/bin/activate
|
||||
$ python -m app.run
|
||||
```
|
||||
|
||||
# acknowledgments
|
||||
|
||||
pyrom uses many open-source and otherwise free-culture components. see the [THIRDPARTY](./THIRDPARTY.md) file for full credit.
|
||||
|
||||
@@ -85,3 +85,18 @@ URL: https://bitty-js.com/
|
||||
License: CC0 1.0
|
||||
Author: alan w smith https://www.alanwsmith.com/
|
||||
Repo: https://github.com/alanwsmith/bitty
|
||||
|
||||
## Flask-Caching
|
||||
|
||||
URL: https://flask-caching.readthedocs.io/
|
||||
Copyright:
|
||||
|
||||
```
|
||||
Copyright (c) 2010 by Thadeus Burgess.
|
||||
Copyright (c) 2016 by Peter Justin.
|
||||
|
||||
Some rights reserved.
|
||||
```
|
||||
|
||||
License: BSD-3-Clause ([see more](https://github.com/pallets-eco/flask-caching/blob/e59bc040cd47cd2b43e501d636d43d442c50b3ff/LICENSE))
|
||||
Repo: https://github.com/pallets-eco/flask-caching
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from flask import Flask, session, request, render_template
|
||||
from dotenv import load_dotenv
|
||||
from .models import Avatars, Users, PostHistory, Posts, MOTD, BadgeUploads
|
||||
from .models import Avatars, Users, PostHistory, Posts, MOTD, BadgeUploads, Sessions
|
||||
from .auth import digest
|
||||
from .routes.users import is_logged_in, get_active_user, get_prefers_theme
|
||||
from .constants import (
|
||||
@@ -138,6 +138,16 @@ def bind_default_badges(path):
|
||||
'uploaded_at': int(os.path.getmtime(real_path)),
|
||||
})
|
||||
|
||||
def clear_stale_sessions():
|
||||
from .db import db
|
||||
with db.transaction():
|
||||
now = int(time.time())
|
||||
stale_sessions = Sessions.findall([
|
||||
('expires_at', '<', now)
|
||||
])
|
||||
for sess in stale_sessions:
|
||||
sess.delete()
|
||||
|
||||
|
||||
cache = Cache()
|
||||
|
||||
@@ -226,6 +236,8 @@ def create_app():
|
||||
create_admin()
|
||||
create_deleted_user()
|
||||
|
||||
clear_stale_sessions()
|
||||
|
||||
reparse_babycode()
|
||||
|
||||
bind_default_badges(app.config['BADGES_PATH'])
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from flask import Blueprint, redirect, url_for, render_template
|
||||
from app import cache
|
||||
from datetime import datetime
|
||||
|
||||
bp = Blueprint("app", __name__, url_prefix = "/")
|
||||
@@ -7,12 +6,3 @@ bp = Blueprint("app", __name__, url_prefix = "/")
|
||||
@bp.route("/")
|
||||
def index():
|
||||
return redirect(url_for("topics.all_topics"))
|
||||
|
||||
@bp.route("/cache-test")
|
||||
def cache_test():
|
||||
test_value = cache.get('test')
|
||||
if test_value is None:
|
||||
test_value = 'cached_value_' + str(datetime.now())
|
||||
cache.set('test', test_value, timeout=10)
|
||||
return f"set cache: {test_value}"
|
||||
return f"cached: {test_value}"
|
||||
|
||||
@@ -74,7 +74,17 @@ def validate_and_create_badge(input_image, filename):
|
||||
return False
|
||||
|
||||
def is_logged_in():
|
||||
return "pyrom_session_key" in session
|
||||
if "pyrom_session_key" not in session:
|
||||
return False
|
||||
sess = Sessions.find({"key": session["pyrom_session_key"]})
|
||||
if not sess:
|
||||
return False
|
||||
if sess.expires_at < int(time.time()):
|
||||
session.clear()
|
||||
sess.delete()
|
||||
flash('Your session expired.;Please log in again.', InfoboxKind.INFO)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_active_user():
|
||||
@@ -83,6 +93,8 @@ def get_active_user():
|
||||
sess = Sessions.find({"key": session["pyrom_session_key"]})
|
||||
if not sess:
|
||||
return None
|
||||
if sess.expires_at < int(time.time()):
|
||||
return None
|
||||
return Users.find({"id": sess.user_id})
|
||||
|
||||
|
||||
@@ -884,6 +896,10 @@ def delete_page_confirm(username):
|
||||
flash('Incorrect password.', InfoboxKind.ERROR)
|
||||
return redirect(url_for('.delete_page', username=username))
|
||||
|
||||
if target_user.is_admin():
|
||||
flash('You cannot delete the admin account.', InfoboxKind.ERROR)
|
||||
return redirect(url_for('.delete_page', username=username))
|
||||
|
||||
anonymize_user(target_user.id)
|
||||
sessions = Sessions.findall({'user_id': int(target_user.id)})
|
||||
for session_obj in sessions:
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
### REQUIRED CONFIGURATION
|
||||
## the following settings are required.
|
||||
## the app will not work if they are missing.
|
||||
|
||||
# the domain name you will be serving Pyrom from, without the scheme, including the subdomain(s).
|
||||
# this is overridden by the app in development.
|
||||
# used for generating URLs.
|
||||
# the app will not start if this field is missing.
|
||||
SERVER_NAME = "forum.your.domain"
|
||||
|
||||
### OPTIONAL CONFIGURATION
|
||||
## the following settings are set to their default values.
|
||||
## you can override any of them.
|
||||
|
||||
# your forum's name, shown on the header.
|
||||
SITE_NAME = "Pyrom"
|
||||
DISABLE_SIGNUP = false # if true, no one can sign up.
|
||||
|
||||
# if true, users can not sign up manually. see the following two settings.
|
||||
DISABLE_SIGNUP = false
|
||||
|
||||
# if neither of the following two options is true,
|
||||
# no one can sign up. this may be useful later when/if LDAP is implemented.
|
||||
|
||||
MODS_CAN_INVITE = true # if true, allows moderators to create invite links. useless unless DISABLE_SIGNUP to be true.
|
||||
USERS_CAN_INVITE = false # if true, allows users to create invite links. useless unless DISABLE_SIGNUP to be true.
|
||||
# if true, allows moderators to create invite links. useless unless DISABLE_SIGNUP is true.
|
||||
MODS_CAN_INVITE = true
|
||||
|
||||
# if true, allows users to create invite links. useless unless DISABLE_SIGNUP is true.
|
||||
USERS_CAN_INVITE = false
|
||||
|
||||
# contact information, will be shown in /guides/contact
|
||||
# some babycodes allowed
|
||||
|
||||
Reference in New Issue
Block a user