document and improve repo a bit

pull/4/head
lza_menace 3 years ago
parent 8a2c66d3b6
commit d0ff90dd5c

2
.gitignore vendored

@ -6,4 +6,4 @@ __pycache__
*tar.gz *tar.gz
*sql *sql
flask_session flask_session
config.py .env

@ -0,0 +1,15 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

@ -1,12 +0,0 @@
setup:
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
shell:
./bin/cmd shell
dev:
./bin/dev
prod:
./bin/prod

@ -1,40 +1,99 @@
# SuchWow! # SuchWow!
TBD Yo. This is a goofy little CRUD app that interacts with wownero-wallet-rpc to allow people to post memes and get tipped in WOW. It uses [Wownero's SSO & Identity service](https://login.wownero.com/developer/docs/backend) as an authentication backend.
It's lacking a lot of advanced functionality and features....but is pretty rock solid in operation. I rarely have to interact w/ my VPS, it just goes...
It was created haphazardly and drunkenly (at varying times). It (hopefully) stays true to the core ethos of Wownero (I feel) which is, "fuck it, lgtm, ship it". Rough around the edges but overall works great. Was fun to whip together and a blast to see it grow with a community behind it! Thanks all!
## Design
Using the "account" mechanism in the wallets (not subaddresses). Every post gets it's own Wownero account within that running wallet RPC process and is mapped to it. Funds go into the account when tipped. The payout script just checks the balances of each account on an interval and sends funds to the user associated with the artwork associated with said account.
## Setup ## Setup
There are quite a few prerequisites to run the web service:
* [register](https://login.wownero.com/developer/register) your app on [Wownero SSO](https://login.wownero.com/developer/docs/backend)
* install Wownero binaries/software
* setup secrets and config
* initialize new Wownero wallet and retain seed
* run the wownero-wallet-rpc process
* install local system Python dependencies
* initialize new sqlite db
* setup scheduled tasks to run payouts
I like to ship with Docker, adjust how you'd prefer.
``` ```
# initialize new wallet and retain seed # setup secrets in env file outside of git
docker run --rm -it --name suchwow-wallet-init \ cp env-example .env
-v $(pwd)/data:/root \
lalanza808/wownero \ # register on wownero sso
wownero-wallet-cli \ # https://login.wownero.com/developer/register
--daemon-address https://node.suchwow.xyz:443 \
--generate-new-wallet /root/wow \ # inject generated keys into suchwow config
--password zzzzzz \ # create new secrets for new wallet, flask server, data, etc
vim .env
# setup rpc process
docker run --rm -d --name suchwow-wallet \ # install docker
-v $(pwd)/data:/root \ sudo apt-get install docker.io docker-compose -y
-p 8888:8888 \ usermod -aG docker ubuntu
lalanza808/wownero \ sudo -u ubuntu bash # login w/ new group perms
wownero-wallet-rpc \
--daemon-address https://node.suchwow.xyz:443 \ # run wownero wallets via docker - store seed, ensure rpc running `docker ps`
--wallet-file /root/wow \ ./run_wallets.sh
--password zzzzzz \
--rpc-bind-port 8888 \ # install python dependencies locally
--rpc-bind-ip 0.0.0.0 \ sudo apt-get install python3-venv -y
--confirm-external-bind \
--rpc-login xxxx:yyyy \
--log-file /root/rpc.log
# install python dependencies
python3 -m venv .venv python3 -m venv .venv
source .venv/bin/activate source .venv/bin/activate
pip install -r requirements.txt pip install -r requirements.txt
# setup secrets in config file outside of git # initialize a new sqlite db
cp suchwow/config.example.py suchwow/config.py ./manage.sh init
vim !$
# setup recurring payouts
# see crontab.txt for cron syntax example
crontab -e
# run a dev server
./manage.sh run # access on localhost:5000 - flask local dev, not for prod
# run a prod server
./manage.sh prod # run gunicorn on loopback - pass to nginx reverse proxy
# kill prod server
pkill -e -f gunicorn
``` ```
# Operational Tasks
You'll want to promote a user as a moderator to review posts:
```
./manage.sh add_admin lza_menace
```
or remove them:
```
./manage.sh remove_admin lza_menace
```
Wallets bug out fairly often. Sometimes you might need to punch in a new daemon or restart a wallet:
```
docker stop suchwow-wallet-rpc
docker rm suchwow-wallet-rpc
vim .env
# modify DAEMON_URI
./run_wallets.sh
```
Manually run a payout:
```
./manage.py payout_users
```
There's other shit, but this is the most common. You'll get the hang of it.

@ -1,7 +0,0 @@
#!/bin/bash
source .venv/bin/activate
export FLASK_APP=suchwow/app.py
export FLASK_SECRETS=config.py
export FLASK_DEBUG=1
flask $@

@ -1,7 +0,0 @@
#!/bin/bash
source .venv/bin/activate
export FLASK_APP=suchwow/app.py
export FLASK_SECRETS=config.py
export FLASK_DEBUG=1
flask run

@ -1,24 +0,0 @@
#!/bin/bash
BASE=data/gunicorn
source .venv/bin/activate
export FLASK_APP=suchwow/app.py
export FLASK_SECRETS=config.py
export FLASK_DEBUG=0
export FLASK_ENV=production
mkdir -p $BASE
pgrep -F $BASE/gunicorn.pid
if [[ $? != 0 ]]; then
gunicorn \
--bind 127.0.0.1:4000 "suchwow.app:app" \
--daemon \
--log-file $BASE/gunicorn.log \
--pid $BASE/gunicorn.pid \
--reload
sleep 1
echo "Starting gunicorn with pid $(cat $BASE/gunicorn.pid)"
fi

@ -0,0 +1,25 @@
OIDC_URL=https://login.wownero.com/auth/realms/master/protocol/openid-connect
OIDC_CLIENT_ID=suchwow-dev
OIDC_CLIENT_SECRET=yyy-yyyyy-yyyyy-yy
OIDC_REDIRECT_URL=http://localhost:5000/auth
SECRET_KEY=ssssssssssss
DATA_FOLDER=/absolute/path/to/the/place/you/store/images
SERVER_NAME=localhost:5000
WALLET_PATH=/absolute/path/to/the/place/you/store/wallet
WALLET_PASS=mytopsecretpass
WALLET_HOST=localhost
WALLET_PORT=8888
WALLET_PROTO=http
WALLET_RPC_USER=suchwow
WALLET_RPC_PASS=again
DAEMON_URI=http://node.suchwow.xyz:34568
PRAW_CLIENT_SECRET=xxxx
PRAW_CLIENT_ID=xxxxx
PRAW_USER_AGENT=xxxxx
PRAW_USERNAME=xxxxx
PRAW_PASSWORD=xxxx
DISCORD_URL=https://xxxx
MM_ICON=https://xxxx
MM_CHANNEL=xxxx
MM_USERNAME=xxxx
MM_ENDPOINT=https://xxxx

@ -0,0 +1,29 @@
#!/bin/bash
source .venv/bin/activate
export FLASK_APP=suchwow/app.py
export FLASK_SECRETS=config.py
export FLASK_DEBUG=0
export FLASK_ENV=production
# override
export $(cat .env)
if [[ ${1} == "prod" ]];
then
export BASE=./data/gunicorn
mkdir -p $BASE
pgrep -F $BASE/gunicorn.pid
if [[ $? != 0 ]]; then
gunicorn \
--bind 127.0.0.1:4000 "suchwow.app:app" \
--daemon \
--log-file $BASE/gunicorn.log \
--pid $BASE/gunicorn.pid \
--reload
sleep 2
echo "Started gunicorn on 127.0.0.1:4000 with pid $(cat $BASE/gunicorn.pid)"
fi
else
flask $@
fi

@ -8,3 +8,4 @@ praw
qrcode qrcode
Pillow Pillow
arrow arrow
python-dotenv

@ -3,29 +3,26 @@
set -e set -e
set +x set +x
# these are only used for local development # mainnet wownero
WALLET_PATH="$(pwd)/data/suchwow-wallet"
WALLET_PASS="sdfj209rFLJDF29ruafj2)__!a@" export $(cat .env)
WALLET_RPC_USER="suchwow"
WALLET_RPC_PASS="y8YzL3cIW6Yeifa23s7Yng=="
DAEMON_URI="http://node.suchwow.xyz:34568"
if [ ! -d "$WALLET_PATH" ]; then if [ ! -d "$WALLET_PATH" ]; then
# initialize new wallet and retain seed # initialize new wallet and retain seed
docker run --rm -it --name suchwow-wallet-init \ docker run --rm -it --name suchwow-wallet-init \
-v $WALLET_PATH:/root \ -v $WALLET_PATH:/root \
lalanza808/wownero \ lalanza808/wownero:latest \
wownero-wallet-cli \ wownero-wallet-cli \
--daemon-address $DAEMON_URI \ --daemon-address $DAEMON_URI \
--generate-new-wallet /root/wow \ --generate-new-wallet /root/wow \
--password $WALLET_PASS --password $WALLET_PASS
fi fi
# setup rpc process # run rpc process
docker run --rm -d --name suchwow-wallet-rpc \ docker run --restart=always -d --name suchwow-wallet-rpc \
-v $WALLET_PATH:/root \ -v $WALLET_PATH:/root \
-p 8888:8888 \ -p 8888:8888 \
lalanza808/wownero \ lalanza808/wownero:latest \
wownero-wallet-rpc \ wownero-wallet-rpc \
--daemon-address $DAEMON_URI \ --daemon-address $DAEMON_URI \
--wallet-file /root/wow \ --wallet-file /root/wow \

@ -78,11 +78,6 @@ def mod_queue():
def about(): def about():
return render_template("about.html") return render_template("about.html")
@app.errorhandler(404)
def not_found(error):
flash("nothing there, brah")
return redirect(url_for("index"))
@app.cli.command("init") @app.cli.command("init")
def init(): def init():
# create subdirs # create subdirs

@ -1,30 +0,0 @@
from os import getenv
OIDC_URL = 'https://login.wownero.com/auth/realms/master/protocol/openid-connect',
OIDC_CLIENT_ID = 'suchwowxxx',
OIDC_CLIENT_SECRET = 'xxxxxxxxxx',
OIDC_REDIRECT_URL = 'http://localhost:5000/auth'
SECRET_KEY = 'yyyyyyyyyyyyy',
SESSION_TYPE = 'filesystem'
DATA_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
MAX_CONTENT_LENGTH = 16 * 1024 * 1024
WALLET_HOST = 'localhost'
WALLET_PORT = 8888
WALLET_PROTO = 'http'
WALLET_USER = 'suchwow'
WALLET_PASS = 'zzzzzzzzzzzzzzz'
PRAW_CLIENT_SECRET = 'xxxxxxxx'
PRAW_CLIENT_ID = 'xxxxxxxx'
PRAW_USER_AGENT = 'suchwow-yyyy-python'
PRAW_USERNAME = 'xxxxxxxx'
PRAW_PASSWORD = 'xxxxxxxx'
SERVER_NAME = 'localhost'
DISCORD_URL = 'xxxxxxx'
BANNED_USERS = {'username': 'reason for the ban'}
MM_ICON = getenv('MM_ICON', 'https://funding.wownero.com/static/wowdoge-a.jpg')
MM_CHANNEL = getenv('MM_CHANNEL', 'suchwow')
MM_USERNAME = getenv('MM_USERNAME', 'SuchWow!')
MM_ENDPOINT = getenv('MM_ENDPOINT', 'ppppppppppppppppppppppppp')

@ -0,0 +1,46 @@
from os import getenv
from dotenv import load_dotenv
load_dotenv()
# generated from https://login.wownero.com/developer/register
OIDC_URL = getenv('OIDC_URL', 'https://login.wownero.com/auth/realms/master/protocol/openid-connect')
OIDC_CLIENT_ID = getenv('OIDC_CLIENT_ID', 'suchwow-dev')
OIDC_CLIENT_SECRET = getenv('OIDC_CLIENT_SECRET', '')
OIDC_REDIRECT_URL = getenv('OIDC_REDIRECT_URL', 'http://localhost:5000/auth')
# you specify something
SECRET_KEY = getenv('SECRET_KEY', 'yyyyyyyyyyyyy') # whatever you want it to be
DATA_FOLDER = getenv('DATA_FOLDER', '/path/to/uploads') # some stable storage path
SERVER_NAME = getenv('SERVER_NAME', 'localhost') # name of your DNS resolvable site (.com)
WALLET_HOST = getenv('WALLET_HOST', 'localhost') #
WALLET_PORT = int(getenv('WALLET_PORT', 8888)) #
WALLET_PROTO = getenv('WALLET_PROTO', 'http') #
WALLET_RPC_USER = getenv('WALLET_RPC_USER', 'suchwow') #
WALLET_RPC_PASS = getenv('WALLET_RPC_PASS', 'suchwow') #
WALLET_PASS = getenv('WALLET_PASS', 'zzzzzzz') # You specify all these wallet details in .env
# Optional for banning users who post crazy shit (they do)
BANNED_USERS = {'username': 'reason for the ban'}
# Optional for posting to Reddit
PRAW_CLIENT_SECRET = getenv('PRAW_CLIENT_SECRET', None)
PRAW_CLIENT_ID = getenv('PRAW_CLIENT_ID', None)
PRAW_USER_AGENT = getenv('PRAW_USER_AGENT', None)
PRAW_USERNAME = getenv('PRAW_USERNAME', None)
PRAW_PASSWORD = getenv('PRAW_PASSWORD', None)
# Optional for posting to Discord
DISCORD_URL = getenv('DISCORD_URL', None)
# Optional for posting to Mattermost
MM_ICON = getenv('MM_ICON', 'https://funding.wownero.com/static/wowdoge-a.jpg')
MM_CHANNEL = getenv('MM_CHANNEL', 'suchwow')
MM_USERNAME = getenv('MM_USERNAME', 'SuchWow!')
MM_ENDPOINT = getenv('MM_ENDPOINT', 'ppppppppppppppppppppppppp')
# defaults
SESSION_TYPE = 'filesystem'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
MAX_CONTENT_LENGTH = 16 * 1024 * 1024

@ -6,7 +6,7 @@ from suchwow import wownero
from suchwow import config from suchwow import config
db = SqliteDatabase(f"{config.DATA_FOLDER}/db/sqlite.db") db = SqliteDatabase(f"{config.DATA_FOLDER}/sqlite.db")
class Post(Model): class Post(Model):
id = AutoField() id = AutoField()

@ -13,8 +13,8 @@ class Wallet(object):
self.host = config.WALLET_HOST self.host = config.WALLET_HOST
self.port = config.WALLET_PORT self.port = config.WALLET_PORT
self.proto = config.WALLET_PROTO self.proto = config.WALLET_PROTO
self.username = config.WALLET_USER self.username = config.WALLET_RPC_USER
self.password = config.WALLET_PASS self.password = config.WALLET_RPC_PASS
self.endpoint = '{}://{}:{}/json_rpc'.format( self.endpoint = '{}://{}:{}/json_rpc'.format(
self.proto, self.host, self.port self.proto, self.host, self.port
) )

Loading…
Cancel
Save