Initial commit
This commit is contained in:
commit
1059a4d62d
|
@ -0,0 +1,14 @@
|
|||
*.pyc
|
||||
bin/
|
||||
config.ini
|
||||
alembic.ini
|
||||
include/
|
||||
local/
|
||||
lib/
|
||||
static/
|
||||
*.swp
|
||||
*.rdb
|
||||
storage/
|
||||
pip-selfcheck.json
|
||||
.sass-cache/
|
||||
overrides/
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2015 Drew DeVault
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,51 @@
|
|||
# Builds static assets
|
||||
# Depends on:
|
||||
# - scss
|
||||
# - coffeescript
|
||||
# - inotify-tools
|
||||
# Run `make` to compile static assets
|
||||
# Run `make watch` to recompile whenever a change is made
|
||||
|
||||
.PHONY: all static watch clean
|
||||
|
||||
STYLES:=$(patsubst styles/%.scss,static/%.css,$(wildcard styles/*.scss))
|
||||
STYLES+=$(patsubst styles/%.css,static/%.css,$(wildcard styles/*.css))
|
||||
SCRIPTS:=$(patsubst scripts/%.coffee,static/%.js,$(wildcard scripts/*.coffee))
|
||||
SCRIPTS+=$(patsubst scripts/%.js,static/%.js,$(wildcard scripts/*.js))
|
||||
_STATIC:=$(patsubst _static/%,static/%,$(wildcard _static/*))
|
||||
|
||||
static/%: _static/%
|
||||
@mkdir -p static/
|
||||
cp $< $@
|
||||
|
||||
static/%.css: styles/%.css
|
||||
@mkdir -p static/
|
||||
cp $< $@
|
||||
|
||||
static/%.css: styles/%.scss
|
||||
@mkdir -p static/
|
||||
scss -I styles/ $< $@
|
||||
|
||||
static/%.js: scripts/%.js
|
||||
@mkdir -p static/
|
||||
cp $< $@
|
||||
|
||||
static/%.js: scripts/%.coffee
|
||||
@mkdir -p static/
|
||||
coffee -m -o static/ -c $<
|
||||
|
||||
static: $(STYLES) $(SCRIPTS) $(_STATIC)
|
||||
|
||||
all: static
|
||||
echo $(STYLES)
|
||||
echo $(SCRIPTS)
|
||||
|
||||
clean:
|
||||
rm -rf static
|
||||
|
||||
watch:
|
||||
while inotifywait \
|
||||
-e close_write scripts/ \
|
||||
-e close_write styles/ \
|
||||
-e close_write _static/; \
|
||||
do make; done
|
|
@ -0,0 +1,107 @@
|
|||
# fosspay
|
||||
|
||||
Helps you get paid for your open source work.
|
||||
|
||||
[](https://drewdevault.com/donate)
|
||||
|
||||
## Rationale
|
||||
|
||||
I write a ton of open source software, but almost none of it is on the scale
|
||||
that I can expect reliable income from donations, or the sorts of projects that
|
||||
a business would be likely to fund. It's very unlikely that I'd receive enough
|
||||
donations from random folks to support full time open source work, but full time
|
||||
is the best way to make serious progress on your projects.
|
||||
|
||||
So - here's how this works: supporters give you one-time or recurring donations,
|
||||
and after a while you get enough to take a week off from work to spend on open
|
||||
source work. Since I have several projects, I also ask supporters to tell me
|
||||
what project they're donating towards, and I distribute the load based on which
|
||||
projects receive the most support.
|
||||
|
||||
## Before you start
|
||||
|
||||
Talk to your employer. The way that this is designed to work is that you
|
||||
continue working full-time at your job, and collect donations. After a while,
|
||||
you should have enough donations to take some period of unpaid leave - a week, a
|
||||
month, or whatever works.
|
||||
|
||||
* You keep your current job and job security
|
||||
* You get paid to work on FOSS even with flaky or inconsistent donations
|
||||
* Everyone wins
|
||||
|
||||
There are a few things you need to talk about with your employer:
|
||||
|
||||
1. Make sure you own the IP for the things you write during your open source
|
||||
sprints.
|
||||
1. Make sure that you have a job to come back to afterwards.
|
||||
1. Research the tax implications of accepting these donations.
|
||||
|
||||
### Stripe
|
||||
|
||||
Payments are taken through Stripe, which is pretty headache-free for you to use.
|
||||
You need to set up an approved Stripe account, which you can get from here:
|
||||
|
||||
https://stripe.com/
|
||||
|
||||
### Mandrill
|
||||
|
||||
You will need a mail server of some sort. If you don't want to go through the
|
||||
trouble of setting one up, you can use Mandrill:
|
||||
|
||||
http://mandrill.com/
|
||||
|
||||
You can probably also use your existing mail server, which is what I do, which
|
||||
makes it easy for people to email you questions and such.
|
||||
|
||||
### SSL
|
||||
|
||||
You will need an SSL certificate for your website (you also need a domain name).
|
||||
You can get a free SSL certificate from [StartSSL](http://www.startssl.com/),
|
||||
but they've always felt pretty... bad to me. You can pay for one instead at
|
||||
[RapidSSL](https://www.rapidssl.com/), which is what I use personally. You can
|
||||
also get one for free from [Let's Encrypt](https://letsencrypt.org/) if that
|
||||
ever happens.
|
||||
|
||||
If you need a domain, you can use my referral link for
|
||||
[Namecheap](http://www.namecheap.com/?aff=84838) and that'd be super nice of
|
||||
you. Here's a link to Namecheap without the referral link:
|
||||
[Namecheap](http://www.namecheap.com).
|
||||
|
||||
## Installation
|
||||
|
||||
Install these things (Arch Linux packages in parenthesis):
|
||||
|
||||
* Python 3 (python)
|
||||
* PostgreSQL (postgresql)
|
||||
* scss (ruby-sass)
|
||||
* Flask (python-flask)
|
||||
* SQLAlchemy (python-sqlalchemy)
|
||||
* Flask-Login (python-flask-login)
|
||||
* psycopg2 (python-psycopg2)
|
||||
* bcrypt (python-bcrypt)
|
||||
|
||||
You'll have to configure PostgreSQL yourself and get a connection string that
|
||||
fosspay can use. Then you can clone this repository to wherever you want to run
|
||||
it from (I suggest making an unprivledged user account on the server you want to
|
||||
host this on).
|
||||
|
||||
### Configuration
|
||||
|
||||
Copy config.ini.example to config.ini and edit it to your liking. Then, you can
|
||||
run this command to try the site in development mode:
|
||||
|
||||
python app.py
|
||||
|
||||
[Click here](http://localhost:5000) to visit your donation site and further
|
||||
instructions will be provided there.
|
||||
|
||||
### Production Deployment
|
||||
|
||||
To deploy this to production, copy the systemd unit from `contrib/` to your
|
||||
server at `/etc/systemd/system/` (or whatever's appropriate for your distro).
|
||||
Use `sytsemctl enable fosspay` and `systemctl start fosspay` to run the site on
|
||||
`127.0.0.1:8000` (you can change this port by editing the unit file). You should
|
||||
configure nginx to proxy through to fosspay from whatever other website you
|
||||
already have. My nginx config is provided in `contrib/` for you to take a look
|
||||
at - it proxies most requests to Github pages (my blog), and `/donate` to
|
||||
fosspay.
|
|
@ -0,0 +1,11 @@
|
|||
from fosspay.app import app
|
||||
from fosspay.config import _cfg, _cfgi
|
||||
|
||||
import os
|
||||
|
||||
app.static_folder = os.path.join(os.getcwd(), "static")
|
||||
|
||||
import os
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host=_cfg("debug-host"), port=_cfgi('debug-port'), debug=True)
|
|
@ -0,0 +1,24 @@
|
|||
[dev]
|
||||
# Change this to the actual location of your site
|
||||
protocol=http
|
||||
domain=localhost:5000
|
||||
# Change this value to something random and secret
|
||||
secret-key=hello world
|
||||
|
||||
# On the debug server, this lets you choose what to bind to
|
||||
debug-host=0.0.0.0
|
||||
debug-port=5000
|
||||
|
||||
# Fill out these details with your mail server
|
||||
smtp-host=mail.you.com
|
||||
smtp-port=587
|
||||
smtp-user=you
|
||||
smtp-password=password
|
||||
smtp-from=donate@you.com
|
||||
|
||||
# Your information
|
||||
your_name=Joe Bloe
|
||||
your_email=joe@bloe.com
|
||||
|
||||
# SQL connection string
|
||||
connection-string=postgresql://postgres@localhost/fosspay
|
|
@ -0,0 +1,74 @@
|
|||
from flask import Flask, render_template, request, g, Response, redirect, url_for
|
||||
from flask.ext.login import LoginManager, current_user
|
||||
from jinja2 import FileSystemLoader, ChoiceLoader
|
||||
|
||||
import sys
|
||||
import os
|
||||
import locale
|
||||
|
||||
from fosspay.config import _cfg, _cfgi
|
||||
from fosspay.database import db, init_db
|
||||
from fosspay.objects import User
|
||||
from fosspay.common import *
|
||||
from fosspay.network import *
|
||||
|
||||
from fosspay.blueprints.html import html
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = _cfg("secret-key")
|
||||
app.jinja_env.cache = None
|
||||
init_db()
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
|
||||
app.jinja_loader = ChoiceLoader([
|
||||
FileSystemLoader("overrides"),
|
||||
FileSystemLoader("templates"),
|
||||
])
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(email):
|
||||
return User.query.filter(User.email == email).first()
|
||||
|
||||
login_manager.anonymous_user = lambda: None
|
||||
|
||||
app.register_blueprint(html)
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, 'en_US')
|
||||
except:
|
||||
pass
|
||||
|
||||
if not app.debug:
|
||||
@app.errorhandler(500)
|
||||
def handle_500(e):
|
||||
# shit
|
||||
try:
|
||||
db.rollback()
|
||||
db.close()
|
||||
except:
|
||||
# shit shit
|
||||
print("We're very borked, letting init system kick us back up")
|
||||
sys.exit(1)
|
||||
return render_template("internal_error.html"), 500
|
||||
|
||||
@app.errorhandler(404)
|
||||
def handle_404(e):
|
||||
return render_template("not_found.html"), 404
|
||||
|
||||
@app.context_processor
|
||||
def inject():
|
||||
return {
|
||||
'root': _cfg("protocol") + "://" + _cfg("domain"),
|
||||
'domain': _cfg("domain"),
|
||||
'protocol': _cfg("protocol"),
|
||||
'len': len,
|
||||
'any': any,
|
||||
'request': request,
|
||||
'locale': locale,
|
||||
'url_for': url_for,
|
||||
'file_link': file_link,
|
||||
'user': current_user,
|
||||
'_cfg': _cfg,
|
||||
'debug': app.debug
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
from flask import Blueprint, render_template, abort, request, redirect, session, url_for, send_file, Response
|
||||
from flask.ext.login import current_user, login_user, logout_user
|
||||
from fosspay.objects import *
|
||||
from fosspay.database import db
|
||||
from fosspay.common import *
|
||||
from fosspay.config import _cfg, load_config
|
||||
|
||||
import locale
|
||||
|
||||
encoding = locale.getdefaultlocale()[1]
|
||||
html = Blueprint('html', __name__, template_folder='../../templates')
|
||||
|
||||
@html.route("/")
|
||||
def index():
|
||||
if User.query.count() == 0:
|
||||
load_config()
|
||||
return render_template("setup.html")
|
||||
return render_template("index.html")
|
||||
|
||||
@html.route("/setup", methods=["POST"])
|
||||
def setup():
|
||||
if not User.query.count() == 0:
|
||||
abort(400)
|
||||
email = request.form.get("email")
|
||||
password = request.form.get("password")
|
||||
if not email or not password:
|
||||
return redirect("/") # TODO: Tell them what they did wrong (i.e. being stupid)
|
||||
user = User(email, password)
|
||||
user.admin = True
|
||||
db.add(user)
|
||||
db.commit()
|
||||
login_user(user)
|
||||
return redirect("/admin?first-run=1")
|
||||
|
||||
@html.route("/admin")
|
||||
@adminrequired
|
||||
def admin():
|
||||
first=bool(request.args.get("first-run"))
|
||||
return render_template("admin.html", first=first)
|
|
@ -0,0 +1,104 @@
|
|||
from flask import session, jsonify, redirect, request, Response, abort
|
||||
from flask.ext.login import current_user
|
||||
from werkzeug.utils import secure_filename
|
||||
from functools import wraps
|
||||
from fosspay.objects import User
|
||||
from fosspay.database import db, Base
|
||||
from fosspay.config import _cfg
|
||||
|
||||
import json
|
||||
import urllib
|
||||
import requests
|
||||
import xml.etree.ElementTree as ET
|
||||
import hashlib
|
||||
|
||||
def firstparagraph(text):
|
||||
try:
|
||||
para = text.index("\n\n")
|
||||
return text[:para + 2]
|
||||
except:
|
||||
try:
|
||||
para = text.index("\r\n\r\n")
|
||||
return text[:para + 4]
|
||||
except:
|
||||
return text
|
||||
|
||||
def with_session(f):
|
||||
@wraps(f)
|
||||
def go(*args, **kw):
|
||||
try:
|
||||
ret = f(*args, **kw)
|
||||
db.commit()
|
||||
return ret
|
||||
except:
|
||||
db.rollback()
|
||||
db.close()
|
||||
raise
|
||||
return go
|
||||
|
||||
def loginrequired(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
if not current_user:
|
||||
return redirect("/login?return_to=" + urllib.parse.quote_plus(request.url))
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
def adminrequired(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
if not current_user:
|
||||
return redirect("/login?return_to=" + urllib.parse.quote_plus(request.url))
|
||||
else:
|
||||
if not current_user.admin:
|
||||
abort(401)
|
||||
return f(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
def json_output(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
def jsonify_wrap(obj):
|
||||
jsonification = json.dumps(obj)
|
||||
return Response(jsonification, mimetype='application/json')
|
||||
|
||||
result = f(*args, **kwargs)
|
||||
if isinstance(result, tuple):
|
||||
return jsonify_wrap(result[0]), result[1]
|
||||
if isinstance(result, dict):
|
||||
return jsonify_wrap(result)
|
||||
if isinstance(result, list):
|
||||
return jsonify_wrap(result)
|
||||
|
||||
# This is a fully fleshed out response, return it immediately
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
def cors(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
res = f(*args, **kwargs)
|
||||
if request.headers.get('x-cors-status', False):
|
||||
if isinstance(res, tuple):
|
||||
json_text = res[0].data
|
||||
code = res[1]
|
||||
else:
|
||||
json_text = res.data
|
||||
code = 200
|
||||
|
||||
o = json.loads(json_text)
|
||||
o['x-status'] = code
|
||||
|
||||
return jsonify(o)
|
||||
|
||||
return res
|
||||
|
||||
return wrapper
|
||||
|
||||
def file_link(path):
|
||||
return _cfg("protocol") + "://" + _cfg("domain") + "/" + path
|
||||
|
||||
def disown_link(path):
|
||||
return _cfg("protocol") + "://" + _cfg("domain") + "/disown?filename=" + path
|
|
@ -0,0 +1,33 @@
|
|||
import logging
|
||||
|
||||
try:
|
||||
from configparser import ConfigParser
|
||||
except ImportError:
|
||||
# Python 2 support
|
||||
from ConfigParser import ConfigParser
|
||||
|
||||
logger = logging.getLogger("fosspay")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
sh = logging.StreamHandler()
|
||||
sh.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||
sh.setFormatter(formatter)
|
||||
|
||||
logger.addHandler(sh)
|
||||
|
||||
# scss logger
|
||||
logging.getLogger("scss").addHandler(sh)
|
||||
|
||||
env = 'dev'
|
||||
config = None
|
||||
|
||||
def load_config():
|
||||
global config
|
||||
config = ConfigParser()
|
||||
config.readfp(open('config.ini'))
|
||||
|
||||
load_config()
|
||||
|
||||
_cfg = lambda k: config.get(env, k)
|
||||
_cfgi = lambda k: int(_cfg(k))
|
|
@ -0,0 +1,14 @@
|
|||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
from .config import _cfg, _cfgi
|
||||
engine = create_engine(_cfg('connection-string'))
|
||||
db = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
|
||||
|
||||
Base = declarative_base()
|
||||
Base.query = db.query_property()
|
||||
|
||||
def init_db():
|
||||
import fosspay.objects
|
||||
Base.metadata.create_all(bind=engine)
|
|
@ -0,0 +1,19 @@
|
|||
def makeMask(n):
|
||||
"return a mask of n bits as a long integer"
|
||||
return (2 << n - 1) - 1
|
||||
|
||||
|
||||
def dottedQuadToNum(ip):
|
||||
"convert decimal dotted quad string to long integer"
|
||||
parts = ip.split(".")
|
||||
return int(parts[0]) | (int(parts[1]) << 8) | (int(parts[2]) << 16) | (int(parts[3]) << 24)
|
||||
|
||||
|
||||
def networkMask(ip, bits):
|
||||
"Convert a network address to a long integer"
|
||||
return dottedQuadToNum(ip) & makeMask(bits)
|
||||
|
||||
|
||||
def addressInNetwork(ip, net):
|
||||
"Is an address in a network"
|
||||
return ip & net == net
|
|
@ -0,0 +1,42 @@
|
|||
from sqlalchemy import Column, Integer, String, Unicode, Boolean, DateTime
|
||||
from sqlalchemy import ForeignKey, Table, UnicodeText, Text, text
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
from .database import Base
|
||||
|
||||
from datetime import datetime
|
||||
import bcrypt
|
||||
import os
|
||||
import hashlib
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'user'
|
||||
id = Column(Integer, primary_key = True)
|
||||
email = Column(String(256), nullable = False, index = True)
|
||||
admin = Column(Boolean())
|
||||
password = Column(String)
|
||||
created = Column(DateTime)
|
||||
passwordReset = Column(String(128))
|
||||
passwordResetExpiry = Column(DateTime)
|
||||
|
||||
def set_password(self, password):
|
||||
self.password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
|
||||
|
||||
def __init__(self, email, password):
|
||||
self.email = email
|
||||
self.admin = False
|
||||
self.created = datetime.now()
|
||||
self.set_password(password)
|
||||
|
||||
def __repr__(self):
|
||||
return '<User %r>' % self.username
|
||||
|
||||
# Flask.Login stuff
|
||||
# We don't use most of these features
|
||||
def is_authenticated(self):
|
||||
return True
|
||||
def is_active(self):
|
||||
return True
|
||||
def is_anonymous(self):
|
||||
return False
|
||||
def get_id(self):
|
||||
return self.email
|
|
@ -0,0 +1,5 @@
|
|||
stripe
|
||||
Flask
|
||||
Jinja2
|
||||
Flask-Misaka
|
||||
gunicorn
|
|
@ -0,0 +1,64 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<h1>Fosspay Admin</h1>
|
||||
{% if first %}
|
||||
<div class="well">
|
||||
<p>
|
||||
You're set up and ready to go! This is your admin panel.
|
||||
Yeah, it's not pretty. Next steps:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Add some projects. Donors can tell you which project they want to support
|
||||
when they donate.
|
||||
</li>
|
||||
<li>
|
||||
Customize the look & feel. Look at the contents of the <code>templates</code>
|
||||
directory - you can copy and paste any of these templates into the
|
||||
<code>overrides</code> directory and change it to suit your needs.
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://drewdevault.com/donate?project=fosspay">Donate to fosspay upstream?</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/SirCmpwn/fosspay">Contribute code to fosspay upstream?</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h2>Projects</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Project Name</th>
|
||||
<th>One-off donations</th>
|
||||
<th>Recurring donations</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4>Add Project</h4>
|
||||
<form method="POST" action="/create-project">
|
||||
<div class="form-group">
|
||||
<input class="form-control" type="text" placeholder="Project name" name="name" />
|
||||
</div>
|
||||
<input type="submit" value="Add" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Donation History</h2>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<th>Project</th>
|
||||
<th>Comment</th>
|
||||
<th>Amount</th>
|
||||
<th style="width: 10%">Recurring</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
{% endblock %}
|
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
{% block title %}
|
||||
<title>Donate to {{_cfg("your-name")}}</title>
|
||||
{% endblock %}
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,73 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<h1>FossPay Setup</h1>
|
||||
<p>Congrats! You have FossPay up and running.</p>
|
||||
|
||||
<h2>config.ini</h2>
|
||||
<ul class="list-unstyled">
|
||||
<li>
|
||||
{% if _cfg("secret-key") == "hello world" %}
|
||||
<span class="glyphicon glyphicon-remove text-danger"></span>
|
||||
You need to change the secret key to something other than "hello world".
|
||||
{% else %}
|
||||
<span class="glyphicon glyphicon-ok text-success"></span>
|
||||
Your secret key looks good.
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{% if _cfg("domain") == "localhost:5000" %}
|
||||
<span class="glyphicon glyphicon-remove text-danger"></span>
|
||||
You should change your domain to something other than localhost.
|
||||
{% else %}
|
||||
<span class="glyphicon glyphicon-ok text-success"></span>
|
||||
Your domain is set to "{{_cfg("domain")}}".
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{% if _cfg("protocol") != "https" %}
|
||||
<span class="glyphicon glyphicon-remove text-danger"></span>
|
||||
Stripe requires your website to use HTTPS.
|
||||
{% else %}
|
||||
<span class="glyphicon glyphicon-ok text-success"></span>
|
||||
Stripe requires your website to use HTTPS.
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{% if not _cfg("smtp-host") %}
|
||||
<span class="glyphicon glyphicon-remove text-danger"></span>
|
||||
You should configure an SMTP server to send emails with.
|
||||
{% else %}
|
||||
<span class="glyphicon glyphicon-ok text-success"></span>
|
||||
Your email configuration looks good.
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{% if not _cfg("stripe-secret") or not _cfg("stripe-publish") %}
|
||||
<span class="glyphicon glyphicon-remove text-danger"></span>
|
||||
Your Stripe API keys are not in your config file.
|
||||
{% else %}
|
||||
<span class="glyphicon glyphicon-ok text-success"></span>
|
||||
Your Stripe API keys look good.
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
<p>You can make changes and refresh this page if you like.</p>
|
||||
|
||||
<h2>Admin Account</h2>
|
||||
<p>Enter your details for the admin account:</p>
|
||||
<form class="form" action="/setup" method="POST">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="email"
|
||||
placeholder="Email" value="{{_cfg("your-email")}}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" name="password" placeholder="Password" />
|
||||
</div>
|
||||
<input type="submit" value="Continue" class="btn btn-primary" />
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
Loading…
Reference in New Issue