post and get slates

This commit is contained in:
Marco Salazar 2022-08-07 20:15:02 +08:00
commit bb960d99fd
15 changed files with 632 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
*__pycache__*
settings.py
.cache
.env
htmlcov
.coverage
funding/static/qr/*
yourvirtualenviornment/
venv/
.idea/

9
LICENSE Normal file
View File

@ -0,0 +1,9 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2018 Wownero Inc., a Monero Enterprise Alliance partner company
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.

48
README.md Normal file
View File

@ -0,0 +1,48 @@
# Epic Post
A simple alternative to epic box.
### Install postgres
```
sudo apt install aptitude
sudo aptitude install postgresql postgresql-contrib
```
https://tecadmin.net/how-to-install-postgresql-in-ubuntu-20-04/
### Web application
Download application and configure.
```
sudo apt install libjpeg-dev libpng-dev python3 redis-server postgresql-server-dev-*
sudo apt install python3-virtualenv
sudo apt install python3-venv
git clone https://github.com/firoorg/fcs.git
cd epicpost
python3 -m venv yourvirtualenviornment
source yourvirtualenviornment/bin/activate
pip uninstall pillow
pip install wheel
pip install -r requirements.txt
CC="cc -mavx2" pip install -U --force-reinstall pillow-simd
cp settings.py_example settings.py
- change settings.py accordingly
```
eg change the psql_pass to your database password, psql_db to your database name
Run the application:
```bash
python run_dev.py
```
Beware `run_dev.py` is meant as a development server.
When running behind nginx/apache, inject `X-Forwarded-For`.
### License
© 2022 WTFPL Do What the Fuck You Want to Public License

0
funding/__init__.py Normal file
View File

0
funding/bin/__init__.py Normal file
View File

7
funding/bin/utils.py Normal file
View File

@ -0,0 +1,7 @@
from datetime import datetime, date
def json_encoder(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
raise TypeError("Type %s not serializable" % type(obj))

View File

@ -0,0 +1,28 @@
from flask import request
import settings
from funding.factory import app, db
@app.before_request
def before_request():
pass
@app.after_request
def after_request(res):
res.headers.add('Accept-Ranges', 'bytes')
if request.full_path.startswith('/api/'):
res.headers.add('Access-Control-Allow-Origin', '*')
if settings.DEBUG:
res.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
res.headers['Pragma'] = 'no-cache'
res.headers['Expires'] = '0'
res.headers['Cache-Control'] = 'public, max-age=0'
return res
@app.errorhandler(404)
def error(err):
return 'Error', 404

163
funding/bin/utils_time.py Normal file
View File

@ -0,0 +1,163 @@
from datetime import datetime, date
from dateutil import parser
import math
import calendar
class TimeMagic():
def __init__(self):
self.now = datetime.now()
self.weekdays_en = {
0: 'monday',
1: 'tuesday',
2: 'wednesday',
3: 'thursday',
4: 'friday',
5: 'saturday',
6: 'sunday'
}
self.months_en = {
0: 'january',
1: 'february',
2: 'march',
3: 'april',
4: 'may',
5: 'june',
6: 'july',
7: 'august',
8: 'september',
9: 'october',
10: 'november',
11: 'december'
}
def get_weekday_from_datetime(self, dt):
n = dt.today().weekday()
return n
def week_number_get(self):
now = datetime.now()
return int(now.strftime("%V"))
def week_number_verify(self, week_nr):
if week_nr > 0 or week_nr <= 53:
return True
def get_weeknr_from_date(self, date):
return date.strftime("%V")
def year_verify(self, year):
if isinstance(year, str):
try:
year = int(year)
except Exception as ex:
return False
if 2000 <= year <= 2030:
return True
def get_day_number(self):
dt = datetime.now()
return dt.today().weekday()
def get_month_nr(self):
return datetime.now().strftime("%m")
def get_daynr_from_weekday(self, weekday):
for k, v in self.weekdays_en.items():
if v == weekday:
return k
def get_day_from_daynr(self, nr):
return self.weekdays_en[nr]
def get_month_from_weeknr(self, nr):
nr = float(nr) / float(4)
if nr.is_integer():
nr -= 1
else:
nr = math.floor(nr)
if nr < 0:
nr = 0
return self.months_en[nr]
def get_month_nr_from_month(self, month):
for k, v in self.months_en.items():
if v == month:
return k
def get_year(self):
return date.today().year
def get_month(self):
return date.today().month
def get_amount_of_days_from_month_nr(self, month_nr):
try:
max_days = calendar.monthrange(self.get_year(), int(month_nr))[1]
return max_days
except Exception as e:
pass
def from_till(self):
m = self.get_month()
d = self.get_amount_of_days_from_month_nr(m)
y = self.get_year()
if len(str(d)) == 1:
d = '0' + str(d)
else:
d = str(d)
if len(str(m)) == 1:
m = '0' + str(m)
else:
m = str(m)
f = '%s/01/%s' % (m, y)
t = '%s/%s/%s' % (m, d, y)
return {'date_from': f, 'date_till': t}
def ago_dt(self, datetime):
return self.ago(datetime)
def ago_str(self, date_str):
date = parser.parse(date_str)
return self.ago(date)
def ago(self, datetime=None, epoch=None):
import math
if epoch:
td = int(epoch)
else:
if datetime:
td = (self.now - datetime).total_seconds()
else:
return None
if td < 60:
if td == 1:
return '%s second ago'
else:
return 'Just now'
elif 60 <= td < 3600:
if 60 <= td < 120:
return '1 minute ago'
else:
return '%s minutes ago' % str(int(math.floor(td / 60)))
elif 3600 <= td < 86400:
if 3600 <= td < 7200:
return '1 hour ago'
else:
return '%s hours ago' % str(int(math.floor(td / 60 / 60)))
elif td >= 86400:
if td <= 86400 < 172800:
return '1 day ago'
else:
x = int(math.floor(td / 24 / 60 / 60))
if x == 1:
return '1 day ago'
return '%s days ago' % str(x)

47
funding/cache.py Normal file
View File

@ -0,0 +1,47 @@
import json
import redis
from flask_session import RedisSessionInterface
import settings
from funding.bin.utils import json_encoder
def redis_args():
args = {
"host": settings.REDIS_HOST,
"port": settings.REDIS_PORT,
'socket_connect_timeout': 2,
'socket_timeout': 2,
'retry_on_timeout': True,
'decode_responses': True
}
if settings.REDIS_PASSWD:
args["password"] = settings.REDIS_PASSWD
return args
class JsonRedisSerializer:
@staticmethod
def loads(val):
try:
return json.loads(val).get("wow", {})
except ValueError:
return
@staticmethod
def dumps(val):
try:
return json.dumps({"wow": val})
except ValueError:
return
class JsonRedis(RedisSessionInterface):
serializer = JsonRedisSerializer
def __init__(self, key_prefix, use_signer=False, decode_responses=True):
super(JsonRedis, self).__init__(
redis=redis.Redis(**redis_args()),
key_prefix=key_prefix,
use_signer=use_signer)

81
funding/factory.py Normal file
View File

@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
import settings
from flask import Flask
from flask_caching import Cache
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
from werkzeug.middleware.proxy_fix import ProxyFix
import redis
app = None
cache = None
db = None
bcrypt = None
def _setup_cache(app: Flask):
global cache
cache_config = {
"CACHE_TYPE": "redis",
"CACHE_DEFAULT_TIMEOUT": 60,
"CACHE_KEY_PREFIX": "wow_cache_",
"CACHE_REDIS_PORT": settings.REDIS_PORT
}
if settings.REDIS_PASSWD:
cache_config["CACHE_REDIS_PASSWORD"] = settings.REDIS_PASSWD
app.config.from_mapping(cache_config)
cache = Cache(app)
def _setup_session(app: Flask):
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_COOKIE_NAME'] = 'bar'
app.config['SESSION_REDIS'] = redis.from_url(settings.REDIS_URI)
Session(app) # defaults to timedelta(days=31)
def _setup_db(app: Flask):
global db
uri = 'postgresql+psycopg2://{user}:{pw}@{url}/{db}'.format(
user=settings.PSQL_USER,
pw=settings.PSQL_PASS,
url=settings.PSQL_HOST,
db=settings.PSQL_DB)
app.config['SQLALCHEMY_DATABASE_URI'] = uri
db = SQLAlchemy(app)
import funding.orm
db.create_all()
def create_app():
global app
global db
global cache
global bcrypt
app = Flask(import_name=__name__,
static_folder='static',
template_folder='templates')
app.config.from_object(settings)
app.config['PERMANENT_SESSION_LIFETIME'] = 2678400
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 30
app.secret_key = settings.SECRET
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
_setup_cache(app)
_setup_session(app)
_setup_db(app)
# import routes
from funding import routes
from funding.bin import utils_request
app.app_context().push()
return app

114
funding/orm.py Normal file
View File

@ -0,0 +1,114 @@
from datetime import datetime
import string
import random
import requests
from sqlalchemy.orm import relationship, backref
import sqlalchemy as sa
from sqlalchemy.orm import scoped_session, sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.types import Float
from sqlalchemy_json import MutableJson
import hashlib
import json
import settings
from funding.factory import db, cache
base = declarative_base(name="Model")
def val_address(address):
if len(address) != 52 or address[0] != 'e' or address[1] != 's':
raise Exception("invalid epic address")
class Address(db.Model):
__tablename__ = "addresses"
id = db.Column('user_id', db.Integer, primary_key=True)
address = db.Column(db.String(52), unique=True)
@classmethod
def find_address(cls, address):
from funding.factory import db
q = cls.query
q = q.filter(Address.address == address)
result = q.first()
if not result:
return
return result
def __repr__(self):
return "<Address(address='%s')>" % (
self.address)
@classmethod
def add(cls, address):
from funding.factory import db
try:
previous = cls.query.filter(Address.address == address).first()
if previous is not None:
print("was inputted before")
return
# validate incoming username/email
val_address(address)
new_address = Address(address=address)
db.session.add(new_address)
db.session.commit()
db.session.flush()
return new_address
except Exception as ex:
db.session.rollback()
raise
class Slate(db.Model):
__tablename__ = "slates"
id = db.Column(db.Integer, primary_key=True)
slate = db.Column(db.String())
# TODO: clear slatse that have been in the database past a certain time
posted_time = db.Column(db.DateTime)
receivingAddress = db.Column(db.String(52), db.ForeignKey('addresses.address'), nullable=False)
def __init__(self, slate, receivingAddress):
from funding.factory import bcrypt
self.slate = slate
self.receivingAddress = receivingAddress
self.posted_time = datetime.utcnow()
def __repr__(self):
return "<Slate(slate='%s', receivingAddress='%s', posted_time='%s)>" % (
self.slate, self.receivingAddress, self.posted_time)
@classmethod
def find_slates(cls, address):
q = cls.query
q = q.filter(Slate.receivingAddress == address)
result = q.all()
print(f'{result}')
list_slates = []
for slate_db in result:
list_slates.append(slate_db.slate)
return list_slates
@classmethod
def add(cls, slate, receivingAddress):
from funding.factory import db
try:
previous = cls.query.filter(Slate.slate == slate).first()
if previous is not None:
print("was inputted before")
return
# put in new one
new_slate = Slate(slate=slate, receivingAddress=receivingAddress)
db.session.add(new_slate)
db.session.commit()
db.session.flush()
return new_slate
except Exception as ex:
db.session.rollback()
raise

64
funding/routes.py Normal file
View File

@ -0,0 +1,64 @@
import uuid
from datetime import datetime
import requests
from flask import request, redirect, render_template, url_for, flash, make_response, send_from_directory, jsonify
from flask_yoloapi import endpoint, parameter
import json
import settings
from funding.factory import app, db, cache
from funding.orm import Address, Slate
@app.route('/')
def index():
return redirect(url_for('about'))
@app.route('/about')
def about():
return ""
@app.route('/postSlate', methods=['POST'])
@endpoint.api(
parameter('receivingAddress', type=str, required=True),
parameter('slate', type=str, required=True),
)
def postSlate(receivingAddress, slate):
try:
if receivingAddress is None or slate is None:
return make_response(jsonify({'status': 'failure', 'error': str("missing correct arguments")}))
try:
Address.add(address=receivingAddress)
except Exception as ex:
print(f'{ex}')
try:
Slate.add(slate=slate, receivingAddress=receivingAddress)
except Exception as ex:
print(f'{ex}')
return make_response(jsonify({'status': 'success'}))
except Exception as ex:
print(f'{ex}')
return make_response(jsonify({'status': 'failure', 'error': str(ex)}))
@app.route('/getSlates', methods=['POST'])
@endpoint.api(
parameter('receivingAddress', type=str, required=True),
)
def getSlates(receivingAddress):
try:
if receivingAddress is None:
return make_response(jsonify({'status': 'failure', 'error': str("missing correct arguments")}))
slates = Slate.find_slates(address=receivingAddress)
return make_response(jsonify({'status': 'success', 'slates': slates}))
except Exception as ex:
print(f'{ex}')
return make_response(jsonify({'status': 'failure', 'error': str(ex)}))

17
requirements.txt Normal file
View File

@ -0,0 +1,17 @@
sqlalchemy==1.3.4
flask
flask-yoloapi==0.1.5
flask_session
flask-login
flask-bcrypt
redis
gunicorn
psycopg2
markdown2
requests
pyqrcode
pypng
pillow-simd
Flask-Caching
flask-sqlalchemy
sqlalchemy_json

8
run_dev.py Normal file
View File

@ -0,0 +1,8 @@
from funding.factory import create_app
import settings
if __name__ == '__main__':
app = create_app()
app.run(host=settings.BIND_HOST, port=settings.BIND_PORT,
debug=settings.DEBUG, use_reloader=False)

36
settings.py_example Normal file
View File

@ -0,0 +1,36 @@
import logging
import socket
import collections
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
SECRET = ''
DEBUG = True
COINCODE = ''
PSQL_HOST = "127.0.0.1:5432"
PSQL_DB = ''
PSQL_USER = 'postgres'
PSQL_PASS = ''
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI', 'postgresql://{user}:{pw}@localhost/{db}').format(user=PSQL_USER, pw=PSQL_PASS, db=PSQL_DB)
SESSION_COOKIE_NAME = os.environ.get('{coincode}_SESSION_COOKIE_NAME', '{coincode}_id').format(coincode=COINCODE.upper())
SESSION_PREFIX = os.environ.get('{coincode}_SESSION_PREFIX', 'session:').format(coincode=COINCODE.upper())
REDIS_HOST = os.environ.get('REDIS_HOST', '127.0.0.1')
REDIS_PORT = int(os.environ.get('REDIS_PORT', 6379))
REDIS_PASSWD = os.environ.get('REDIS_PASSWD', None)
REDIS_URI = "redis://"
if REDIS_PASSWD:
REDIS_URI += f":{REDIS_PASSWD}"
REDIS_URI += f"@{REDIS_HOST}:{REDIS_PORT}"
BIND_HOST = os.environ.get("BIND_HOST", "0.0.0.0")
if not BIND_HOST:
raise Exception("BIND_HOST missing")
BIND_PORT = os.environ.get("BIND_PORT", 5004)
if not BIND_PORT:
raise Exception("BIND_PORT missing")
HOSTNAME = os.environ.get("{coincode}_HOSTNAME", socket.gethostname()).format(coincode=COINCODE.upper())