Compare commits
No commits in common. "469785ee33a7714cc4f9d03fba57fd1b2ffd5c63" and "d7a0167cd6148cfa4a46b4a562fd00112f74fbe2" have entirely different histories.
469785ee33
...
d7a0167cd6
29
README.md
29
README.md
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
### Sec:
|
### Sec:
|
||||||
|
|
||||||
- This repo is public. Mind cred slip-ups.
|
* This repo is public. Mind cred slip-ups.
|
||||||
- Please note changes to /etc/sshd/sshd_conf made by lll script. If different method is used, audit manually.
|
* Please note changes to /etc/sshd/sshd_conf made by lll script. If different method is used, audit manually.
|
||||||
- Note app Dockerfile debug console, found at /console. Werkzeug/flask is WILDLY insecure if left in dev/dbg.
|
* Note app Dockerfile debug console, found at /console. Werkzeug/flask is WILDLY insecure if left in dev/dbg.
|
||||||
- Avoid docker socks stuff.
|
* Avoid docker socks stuff.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Install:
|
### Install:
|
||||||
|
|
||||||
@ -47,28 +49,19 @@ set up cron job for script
|
|||||||
pmb-pf - git clone of my mail thing
|
pmb-pf - git clone of my mail thing
|
||||||
other - ref and non-sensitive files for dns
|
other - ref and non-sensitive files for dns
|
||||||
|
|
||||||
### Setup cheat:
|
### Timeline:
|
||||||
|
|
||||||
- set up certbot dns (prod)
|
set up certbot dns\
|
||||||
- see tar of cert dir with script (prod)
|
see tar of cert dir with script
|
||||||
- flask vs uwsgi in backend compose section (prod)
|
|
||||||
- build vs local image in pmb-pf compose section
|
|
||||||
- git clone pmb-pf
|
|
||||||
- copy example .env in root dir
|
|
||||||
- copy example .env in pmb-pf
|
|
||||||
- copy example conf in proxy
|
|
||||||
- do pmb-pf setup, and adjust root .env
|
|
||||||
- mind backend config db settings
|
|
||||||
|
|
||||||
### Notes:
|
### Notes:
|
||||||
This repo is minimally-sensitive. Falling outside the repo dir structure are reference awesome-compose files used as baseline -- nginx-flask-mysql -- and certs, containing letsencrypt script. Script may be backed up into repo carefully, sanitizing any tkens.
|
This repo is minimally-sensitive. Falling outside the repo dir structure are reference awesome-compose files used as baseline -- nginx-flask-mysql -- and certs, containing letsencrypt script. Script may be backed up into repo carefully, sanitizing any tkens.
|
||||||
|
|
||||||
|
TODO: gitea subdomain will require wildcard cert -- therefore "*.oily.dad" AND "oily.dad" DONE
|
||||||
|
|
||||||
### Changing gitea subdomain:
|
### Changing gitea subdomain:
|
||||||
|
|
||||||
Find in proxy/conf.\
|
Find in proxy/conf.\
|
||||||
Find in gitea conf.\
|
Find in gitea conf.\
|
||||||
Rebuild images.
|
Rebuild images.
|
||||||
|
|
||||||
### Todo:
|
|
||||||
- gitea subdomain will require wildcard cert -- therefore "*.oily.dad" AND "oily.dad" DONE
|
|
||||||
- move more stuff from backend config into root .env
|
|
@ -1,4 +1,3 @@
|
|||||||
venv
|
venv
|
||||||
migrations
|
migrations
|
||||||
zapp.db
|
|
||||||
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
# syntax=docker/dockerfile:1.4
|
# syntax=docker/dockerfile:1.4
|
||||||
FROM python:3-slim-bookworm AS builder
|
FROM python:3-slim-bookworm AS builder
|
||||||
|
|
||||||
# Second line optional/debug/qol
|
RUN apt update && apt install -y libmariadb-dev gcc
|
||||||
RUN apt update && apt install -y \
|
|
||||||
libmariadb-dev gcc \
|
|
||||||
mariadb-client
|
|
||||||
|
|
||||||
|
|
||||||
WORKDIR /code
|
WORKDIR /code
|
||||||
COPY requirements.txt /code
|
COPY requirements.txt /code
|
||||||
|
@ -18,14 +18,8 @@ pip install flask-migrate
|
|||||||
pip install flask-login
|
pip install flask-login
|
||||||
pip install email-validator
|
pip install email-validator
|
||||||
pip install pydenticon
|
pip install pydenticon
|
||||||
Prod only, require sys packages:
|
|
||||||
pip install mariadb
|
pip install mariadb
|
||||||
pip install uwsgi
|
|
||||||
...
|
...
|
||||||
```
|
|
||||||
|
|
||||||
Freeze/requirements.txt. Better to audit this inside python:3-bookworm-slim container.
|
|
||||||
```
|
|
||||||
pip freeze > requirements.txt
|
pip freeze > requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -42,11 +36,8 @@ flask db downgrade base
|
|||||||
flask db upgrade
|
flask db upgrade
|
||||||
```
|
```
|
||||||
|
|
||||||
Full reset or maria init:
|
Full reset:
|
||||||
```
|
```
|
||||||
sql:
|
|
||||||
drop table users;
|
|
||||||
drop table posts;
|
|
||||||
rm app.db
|
rm app.db
|
||||||
rm -r migrations
|
rm -r migrations
|
||||||
flask db init
|
flask db init
|
||||||
|
@ -26,12 +26,11 @@ if not app.debug:
|
|||||||
app.logger.addHandler(mail_handler)
|
app.logger.addHandler(mail_handler)
|
||||||
|
|
||||||
if app.config['DC_LOGGING']:
|
if app.config['DC_LOGGING']:
|
||||||
print('#################### TEST PRINT STDERR DEBUG', file=sys.stderr)
|
print('#################### DEBUGHERE', file=sys.stderr)
|
||||||
dclog = logging.StreamHandler(stream=sys.stderr)
|
dclog = logging.StreamHandler(stream=sys.stderr)
|
||||||
dclog.setLevel(logging.INFO)
|
dclog.setLevel(logging.INFO)
|
||||||
dclog.propagate = False
|
dclog.propagate = False
|
||||||
app.logger.addHandler(dclog)
|
app.logger.addHandler(dclog)
|
||||||
app.logger.info('@@@@@@@@@@@@@@@@@@@@@ TEST LOGGER INFO MESSAGE')
|
|
||||||
|
|
||||||
|
|
||||||
from app import routes, models, errors
|
from app import routes, models, errors
|
||||||
|
@ -9,13 +9,6 @@ import pydenticon, hashlib, base64
|
|||||||
from app import db, login
|
from app import db, login
|
||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
|
|
||||||
followers = sa.Table(
|
|
||||||
'followers',
|
|
||||||
db.metadata,
|
|
||||||
sa.Column('follower_id', sa.Integer, sa.ForeignKey('user.id'), primary_key=True),
|
|
||||||
sa.Column('followed_id', sa.Integer, sa.ForeignKey('user.id'), primary_key=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
class User(UserMixin, db.Model):
|
class User(UserMixin, db.Model):
|
||||||
id: so.Mapped[int] = so.mapped_column(primary_key=True)
|
id: so.Mapped[int] = so.mapped_column(primary_key=True)
|
||||||
username: so.Mapped[str] = so.mapped_column(sa.String(64), index=True, unique=True)
|
username: so.Mapped[str] = so.mapped_column(sa.String(64), index=True, unique=True)
|
||||||
@ -24,16 +17,6 @@ class User(UserMixin, db.Model):
|
|||||||
posts: so.WriteOnlyMapped['Post'] = so.relationship(back_populates='author')
|
posts: so.WriteOnlyMapped['Post'] = so.relationship(back_populates='author')
|
||||||
about_me: so.Mapped[Optional[str]] = so.mapped_column(sa.String(140))
|
about_me: so.Mapped[Optional[str]] = so.mapped_column(sa.String(140))
|
||||||
last_seen: so.Mapped[Optional[datetime]] = so.mapped_column(default=lambda: datetime.now(timezone.utc))
|
last_seen: so.Mapped[Optional[datetime]] = so.mapped_column(default=lambda: datetime.now(timezone.utc))
|
||||||
following: so.WriteOnlyMapped['User'] = so.relationship(
|
|
||||||
secondary=followers,
|
|
||||||
primaryjoin=(followers.c.follower_id == id),
|
|
||||||
secondaryjoin=(followers.c.followed_id == id),
|
|
||||||
back_populates='followers')
|
|
||||||
followers: so.WriteOnlyMapped['User'] = so.relationship(
|
|
||||||
secondary=followers,
|
|
||||||
primaryjoin=(followers.c.followed_id == id),
|
|
||||||
secondaryjoin=(followers.c.follower_id == id),
|
|
||||||
back_populates='following')
|
|
||||||
|
|
||||||
def set_password(self, password):
|
def set_password(self, password):
|
||||||
self.password_hash = generate_password_hash(password)
|
self.password_hash = generate_password_hash(password)
|
||||||
@ -66,36 +49,6 @@ class User(UserMixin, db.Model):
|
|||||||
pngloc = os.path.join(basedir, 'usercontent', 'identicon', digest + '.png')
|
pngloc = os.path.join(basedir, 'usercontent', 'identicon', digest + '.png')
|
||||||
return pngloc
|
return pngloc
|
||||||
|
|
||||||
def follow(self, user):
|
|
||||||
if not self.is_following(user):
|
|
||||||
self.following.add(user)
|
|
||||||
def unfollow(self, user):
|
|
||||||
if self.is_following(user):
|
|
||||||
self.following.remove(user)
|
|
||||||
def is_following(self, user):
|
|
||||||
query = self.following.select().where(User.id == user.id)
|
|
||||||
return db.session.scalar(query) is not None
|
|
||||||
def followers_count(self):
|
|
||||||
query = sa.select(sa.func.count()).select_from(self.followers.select().subquery())
|
|
||||||
return db.session.scalar(query)
|
|
||||||
def following_count(self):
|
|
||||||
query = sa.select(sa.func.count()).select_from(self.following.select().subquery())
|
|
||||||
return db.session.scalar(query)
|
|
||||||
def following_posts(self):
|
|
||||||
Author = so.aliased(User)
|
|
||||||
Follower = so.aliased(User)
|
|
||||||
return (
|
|
||||||
sa.select(Post)
|
|
||||||
.join(Post.author.of_type(Author))
|
|
||||||
.join(Author.followers.of_type(Follower), isouter=True)
|
|
||||||
.where(sa.or_(
|
|
||||||
Follower.id == self.id
|
|
||||||
Author.id == self.id
|
|
||||||
))
|
|
||||||
.group_by(Post)
|
|
||||||
.order_by(Post.timestamp.desc())
|
|
||||||
)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User {}>'.format(self.username)
|
return '<User {}>'.format(self.username)
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ basedir = os.path.abspath(os.path.dirname(__file__))
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
SECRET_KEY = os.environ.get('FLASK_SECRET_KEY') or 'flasksk'
|
SECRET_KEY = os.environ.get('FLASK_SECRET_KEY') or 'flasksk'
|
||||||
#SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'zapp.db')
|
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'zapp.db')
|
||||||
SQLALCHEMY_DATABASE_URI = 'mariadb+mariadbconnector://flasku:' + os.environ.get('MYSQL_PASSWORD') + '@db:3306/flask'
|
#SQLALCHEMY_DATABASE_URI = 'mariadb+mariadbconnector://flasku:' + os.environ.get('MYSQL_PASSWORD') + '@db:3306/flask'
|
||||||
|
|
||||||
#MAIL_SERVER = 'pmb'
|
#MAIL_SERVER = 'pmb'
|
||||||
MAIL_SERVER = ''
|
MAIL_SERVER = ''
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Okay to publish -- creds are local dev only
|
|
||||||
|
|
||||||
mariadb -hdb -uflasku -pflaskp flask
|
|
@ -21,9 +21,5 @@ SQLAlchemy==2.0.31
|
|||||||
typing_extensions==4.12.2
|
typing_extensions==4.12.2
|
||||||
Werkzeug==3.0.3
|
Werkzeug==3.0.3
|
||||||
WTForms==3.1.2
|
WTForms==3.1.2
|
||||||
mariadb==1.1.10
|
uwsgi
|
||||||
packaging==24.1
|
mariadb
|
||||||
setuptools==72.1.0
|
|
||||||
uWSGI==2.0.26
|
|
||||||
wheel==0.43.0
|
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import os
|
|
||||||
os.environ['DATABASE_URL'] = 'sqlite://'
|
|
||||||
|
|
||||||
from datetime import datetime, timezone, timedelta
|
|
||||||
|
|
@ -28,12 +28,9 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: backend
|
context: backend
|
||||||
target: builder
|
target: builder
|
||||||
# Next two are only debug, used without restart
|
restart: always
|
||||||
stdin_open: true
|
|
||||||
tty: true
|
|
||||||
#restart: always
|
|
||||||
# Comment following line to use flask (1worker, dev), uncomment to use uwsgi (wsgi)
|
# Comment following line to use flask (1worker, dev), uncomment to use uwsgi (wsgi)
|
||||||
#command: ["uwsgi", "--http", "0.0.0.0:8000", "--master", "-p", "4", "-w", "microblog:app"]
|
#command: ["uwsgi", "--http", "0.0.0.0:8000", "--master", "-p", "4", "-w", "app:server"]
|
||||||
environment:
|
environment:
|
||||||
- MYSQL_USER=flasku
|
- MYSQL_USER=flasku
|
||||||
#- MYSQL_PASSWORD=flaskp
|
#- MYSQL_PASSWORD=flaskp
|
||||||
|
6
dotenv
6
dotenv
@ -5,7 +5,7 @@ DOTENV_MYSQL_ROOT_PASSWORD=rootp
|
|||||||
DOTENV_MYSQL_GITEA_PASSWORD=giteap
|
DOTENV_MYSQL_GITEA_PASSWORD=giteap
|
||||||
DOTENV_MYSQL_FLASK_PASSWORD=flaskp
|
DOTENV_MYSQL_FLASK_PASSWORD=flaskp
|
||||||
|
|
||||||
GITEA_MAIL_FROM=gitea@gitea.changeme
|
GITEA_MAIL_FROM=
|
||||||
|
|
||||||
# Build ARG GPG_PP. May still need to be empty to avoid breakage.
|
# Build ARG GPG_PP. May still need to be empty to avoid breakage.
|
||||||
BUILD_GPG_PP=
|
BUILD_GPG_PP=
|
||||||
@ -18,7 +18,3 @@ DOTENV_TOKEN_I=dti
|
|||||||
|
|
||||||
# Consequential token: protect
|
# Consequential token: protect
|
||||||
DOTENV_TOKEN_C=dtc
|
DOTENV_TOKEN_C=dtc
|
||||||
|
|
||||||
# Destination address for handler mailer
|
|
||||||
ADMIN_EMAIL="email@email.changeme"
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user