diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d7ac82..6ba5f63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,12 @@ - Posts may now be deleted by author. If it has comments, comments are not spared - Moderators (and admins) have now access to mod tools + Allowed operations: change display name, description, restriction status, and exile (guild-local ban) members -- Implemented guild subscriptions + + Site administrators and guild owners can add moderators +- Administrators can claim ownership of abandoned guilds +- Implemented guild subscriptions (not as in $$$, yes as in the follow button) - Added ✨color themes✨ - Users can now set their display name, biography and color theme in `/settings` +- You can now add an impressum in .env, e.g. `IMPRESSUM='Acme Ltd.::1 Short Island::Old York, Divided States::Responsible: ::Donald Duck'` Lines are separated by two colons. Version before 0.4.0 CAN'T BE RUN in German-speaking countries as of 2025. ## 0.3.3 diff --git a/README.md b/README.md index 20c781e..1308d66 100644 --- a/README.md +++ b/README.md @@ -11,26 +11,73 @@ * Unix-like OS (Docker container, Linux or MacOS are all good). * **Python** >=3.10. Recommended to use a virtualenv (unless in Docker lol). * **PostgreSQL** at least 16. - * **Redis**/Valkey (as of 0.4.0 unused in codebase). + * **Redis**/Valkey (as of 0.4.0 unused in codebase -_-). + * **Docker** and **Docker Compose**. * A server machine with a public IP address and shell access (mandatory for production, optional for development/staging). - * A reverse proxy listening on ports 80 and 443. Reminder to set `APP_IS_BEHIND_PROXY=1` in `.env` !!! + * First time? I recommend a VPS. The cheapest one starts at €5/month, half a Spotify subscription. + * You must have **shell access**. FTP only is not enough. + * A domain (mandatory for production). + * You must have bought it beforehand. Don't have? `.xyz` are like $2 or $3 on Namecheap[^1] + * For development, tweaking `/etc/hosts` or plain running on `localhost:5000` is usually enough. + * A reverse proxy (i.e. Caddy or nginx) listening on ports 80 and 443. Reminder to set `APP_IS_BEHIND_PROXY=1` in `.env` !!! * Electricity. * Will to not give up. * Clone this repository. * Fill in `.env` with the necessary information. - * `DOMAIN_NAME` (you must own it. Don't have? `.xyz` are like $2 or $3 on Namecheap[^1]) + * `DOMAIN_NAME` (see above) * `APP_NAME` * `DATABASE_URL` (hint: `postgresql://username:password@localhost/dbname`) * `SECRET_KEY` (you can generate one with the command `cat /dev/random | tr -dc A-Za-z0-9_. | head -c 56`) * `PRIVATE_ASSETS` (you must provide the icon stylesheets here. Useful for custom CSS / scripts as well) * `APP_IS_BEHIND_PROXY` (mandatory if behind reverse proxy or NAT) -* ... + * `IMPRESSUM` (if you host or serve your site in Germany[^2]. Lines are separated by double colons `::`) +* Adjust `docker-compose.yml` to your liking. +* Run `docker compose build`. +* Create a systemd unit file looking like this: +```systemd +[Unit] +Description=Freak +## using Caddy? replace nginx.service with Caddy.service. Yes, twice +Wants=nginx.service docker.service +After=nginx.service docker.service + +[Service] +Type=simple +## REPLACE it with your path +WorkingDirectory=/path/to/repository/freak +ExecStart=/usr/bin/docker compose up +ExecReload=/usr/bin/docker compose run freak bash ./docker-run.sh r +ExecStop=/usr/bin/docker compose down + +[Install] +WantedBy=multi-user.target +``` +* Copy the file to `/usr/lib/systemd/system` (with root access) +* Run `sudo systemctl enable --now freak.service` +* Expect no red text or weird error gibberish. If there is, you did not follow the tutorial: read it from the start again. +* Congratulations! Your Freak instance is up and running + [^1]: Namecheap is an American company. Don't trust American companies. +[^2]: Not legal advice. ## FAQ -... +### Why another Reddit clone? + +I felt like it. + +### Will Freak be federated? + +It's on the roadmap. However, it probably won't be fully functional if not after at least twenty feature releases. Therefore, wait patiently. + +Freak is currently implementing the [SIS](https://yusur.moe/protocols/sis.html). + +### What is your legal contact / Impressum? + +You have to configure it yourself by setting `IMPRESSUM` in `.env`. + +I only write the code. I am not accountable for Your use (see [License](#license)). ## License diff --git a/docker-compose.yml.example b/docker-compose.yml.example new file mode 100644 index 0000000..cc8f2fe --- /dev/null +++ b/docker-compose.yml.example @@ -0,0 +1,14 @@ + +services: + freak: + build: + context: . + image: freak + ports: + - 5000:5000 + volumes: + - .:/opt/live-app:ro + extra_hosts: + - 'postgres.docker.internal:172.17.0.1' + restart: on-failure:3 + diff --git a/freak/__init__.py b/freak/__init__.py index 653388a..30df9de 100644 --- a/freak/__init__.py +++ b/freak/__init__.py @@ -22,6 +22,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix from suou.configparse import ConfigOptions, ConfigValue from .colors import color_themes, theme_classes +from .utils import twocolon_list __version__ = '0.4.0-dev28' @@ -38,6 +39,7 @@ class AppConfig(ConfigOptions): private_assets = ConfigValue(cast=ssv_list) jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js') app_is_behind_proxy = ConfigValue(cast=bool, default=False) + impressum = ConfigValue(cast=twocolon_list, default=None) app_config = AppConfig() @@ -97,7 +99,8 @@ def _inject_variables(): 'post_count': Post.count(), 'user_count': User.active_count(), 'colors': color_themes, - 'theme_classes': theme_classes + 'theme_classes': theme_classes, + 'impressum': '\n'.join(app_config.impressum).replace('_', ' ') } @login_manager.user_loader diff --git a/freak/models.py b/freak/models.py index 9349519..f2f4657 100644 --- a/freak/models.py +++ b/freak/models.py @@ -360,18 +360,21 @@ class Guild(Base): if self.owner: yield ModeratorInfo(self.owner, True) for mem in db.session.execute(select(Member).where(Member.guild_id == self.id, Member.is_moderator == True)).scalars(): - if mem.user != self.owner and not mem.user.is_banned: + if mem.user != self.owner and not mem.is_banned: yield ModeratorInfo(mem.user, False) def update_member(self, u: User | Member, /, **values): if isinstance(u, User): m = db.session.execute(select(Member).where(Member.user_id == u.id, Member.guild_id == self.id)).scalar() if m is None: - return db.session.execute(insert(Member).values( + m = db.session.execute(insert(Member).values( guild_id = self.id, user_id = u.id, **values ).returning(Member)).scalar() + if m is None: + raise RuntimeError + return m else: m = u if len(values): diff --git a/freak/templates/about.html b/freak/templates/about.html index b7be8b7..a7d0840 100644 --- a/freak/templates/about.html +++ b/freak/templates/about.html @@ -24,7 +24,13 @@
Source code is available at: https://github.com/sakuragasaki46/freak
+Source code is available at: https://github.com/yusurko/freak
+ + {% if impressum %} +{{ impressum }}
+ {% endif %}
+
{% endblock %}
diff --git a/freak/templates/guildsettings.html b/freak/templates/guildsettings.html
index 99e9e2e..f7cbecb 100644
--- a/freak/templates/guildsettings.html
+++ b/freak/templates/guildsettings.html
@@ -1,14 +1,30 @@
{% extends "base.html" %}
+{% from "macros/icon.html" import icon with context %}
{% from "macros/title.html" import title_tag with context %}
{% from "macros/create.html" import checked_if with context %}
{% block title %}{{ title_tag('Settings for ' + gu.handle()) }}{% endblock %}
{% block heading %}
-