Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,379 @@
|
||||
#!/usr/bin/python
|
||||
import flask
|
||||
import datetime
|
||||
import json
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.sql.expression import asc, desc, or_
|
||||
from werkzeug.debug import DebuggedApplication
|
||||
import hashlib
|
||||
import os, sys
|
||||
import os.path
|
||||
from flask.ext.cache import Cache
|
||||
from os import listdir, walk, chdir
|
||||
from os.path import isdir, join
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
cache = Cache(app, config={'CACHE_TYPE':'simple'})
|
||||
application = DebuggedApplication(app, True)
|
||||
#app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///api.db"
|
||||
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://bmssql3:4c403a47@localhost/bmssql3"
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
class Design(db.Model):
|
||||
__tablename__ = "designs"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
ctime = db.Column(db.DateTime, default=datetime.datetime.now)
|
||||
name = db.Column(db.String(255))
|
||||
size = db.Column(db.Float)
|
||||
data = db.Column(db.Text)
|
||||
color = db.Column(db.String(7))
|
||||
mods = db.Column(db.String(255))
|
||||
views = db.Column(db.Integer, default=0)
|
||||
|
||||
voteTime = db.Column(db.DateTime)
|
||||
upvotes = db.Column(db.Integer, default=0)
|
||||
|
||||
commentTime = db.Column(db.DateTime)
|
||||
commentCount = db.Column(db.Integer, default=0)
|
||||
|
||||
author = db.Column(db.String(255))
|
||||
token = db.Column(db.String(255))
|
||||
description = db.Column(db.Text)
|
||||
|
||||
class Comment(db.Model):
|
||||
__tablename__ = "comments"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
design = db.Column(db.Integer, index=True)
|
||||
|
||||
ctime = db.Column(db.DateTime, default=datetime.datetime.now)
|
||||
author = db.Column(db.String(255))
|
||||
token = db.Column(db.String(255))
|
||||
content = db.Column(db.Text)
|
||||
|
||||
class Vote(db.Model):
|
||||
__tablename__ = "votes"
|
||||
token = db.Column(db.String(255), primary_key=True, autoincrement=False)
|
||||
design = db.Column(db.Integer, primary_key=True, autoincrement=False)
|
||||
time = db.Column(db.DateTime, default=datetime.datetime.now)
|
||||
|
||||
def design_spec(design):
|
||||
output = {}
|
||||
output["id"] = design.id
|
||||
output["name"] = design.name
|
||||
output["size"] = design.size
|
||||
output["data"] = design.data
|
||||
output["author"] = design.author
|
||||
output["color"] = design.color
|
||||
output["description"] = design.description
|
||||
output["mods"] = design.mods
|
||||
output["ctime"] = design.ctime.strftime("%Y-%m-%d %H:%M")
|
||||
output["commentCount"] = design.commentCount
|
||||
output["upvotes"] = design.upvotes
|
||||
|
||||
return output
|
||||
|
||||
def design_list(query, paginate=True):
|
||||
#Apply paginization
|
||||
if paginate:
|
||||
pagestart = 0
|
||||
pagelimit = 10
|
||||
if "limit" in flask.request.args:
|
||||
pagelimit = int(flask.request.args["limit"])
|
||||
if "page" in flask.request.args:
|
||||
pagestart = pagelimit * int(flask.request.args["page"])
|
||||
query = query.slice(pagestart, pagestart+pagelimit+1);
|
||||
|
||||
#Unify in output
|
||||
output = []
|
||||
for d in query.all():
|
||||
output.append(design_spec(d))
|
||||
return output
|
||||
|
||||
@app.route("/design/<int:design_id>")
|
||||
def design(design_id):
|
||||
design = Design.query.filter_by(id=design_id).first()
|
||||
|
||||
if not design:
|
||||
flask.abort(404)
|
||||
return
|
||||
|
||||
output = design_spec(design)
|
||||
|
||||
token = flask.request.headers.get("APIToken")
|
||||
if token:
|
||||
output["hasUpvoted"] = Vote.query.filter_by(design=design.id, token=token).count() != 0
|
||||
output["isMine"] = design.token == token
|
||||
else:
|
||||
output["hasUpvoted"] = False
|
||||
output["isMine"] = False
|
||||
|
||||
clist = []
|
||||
output["comments"] = clist
|
||||
|
||||
comments = Comment.query.filter_by(design=design.id)
|
||||
for c in comments:
|
||||
clist.append({
|
||||
"author": c.author,
|
||||
"ctime": c.ctime.strftime("%Y-%m-%d %H:%M"),
|
||||
"content": c.content
|
||||
})
|
||||
|
||||
design.views += 1
|
||||
db.session.commit()
|
||||
|
||||
return json.dumps(output)
|
||||
|
||||
@app.route("/design/<int:design_id>/vote", methods=["POST"])
|
||||
def design_vote(design_id):
|
||||
design = Design.query.filter_by(id=design_id).first()
|
||||
token = flask.request.headers.get("APIToken")
|
||||
|
||||
if not design or not token:
|
||||
flask.abort(404)
|
||||
return
|
||||
|
||||
if design.token == token:
|
||||
flask.abort(403)
|
||||
return
|
||||
|
||||
if Vote.query.filter_by(design=design.id, token=token).count() != 0:
|
||||
flask.abort(403)
|
||||
return
|
||||
|
||||
vote = Vote()
|
||||
vote.token = token
|
||||
vote.design = design.id
|
||||
|
||||
design.upvotes += 1
|
||||
design.voteTime = datetime.datetime.now()
|
||||
|
||||
db.session.add(vote)
|
||||
db.session.commit()
|
||||
|
||||
return "{}"
|
||||
|
||||
@app.route("/design/<int:design_id>/comment", methods=["POST"])
|
||||
def design_comment(design_id):
|
||||
design = Design.query.filter_by(id=design_id).first()
|
||||
token = flask.request.headers.get("APIToken")
|
||||
|
||||
if not design or not token:
|
||||
flask.abort(404)
|
||||
return
|
||||
|
||||
comment = Comment()
|
||||
comment.design = design.id
|
||||
comment.author = flask.request.form.get("author", "")
|
||||
comment.content = flask.request.form.get("content", "")
|
||||
comment.token = token
|
||||
|
||||
if not comment.author or not comment.content:
|
||||
flask.abort(400)
|
||||
return
|
||||
|
||||
design.commentCount += 1
|
||||
design.commentTime = datetime.datetime.now()
|
||||
|
||||
db.session.add(comment)
|
||||
db.session.commit()
|
||||
|
||||
return "{}"
|
||||
|
||||
@app.route("/design/<int:design_id>/delete", methods=["POST"])
|
||||
def design_delete(design_id):
|
||||
design = Design.query.filter_by(id=design_id).first()
|
||||
token = flask.request.headers.get("APIToken")
|
||||
|
||||
if not design or not token:
|
||||
flask.abort(404)
|
||||
return
|
||||
|
||||
if design.token != token:
|
||||
flask.abort(403)
|
||||
return
|
||||
|
||||
Vote.query.filter_by(design=design.id).delete()
|
||||
Comment.query.filter_by(design=design.id).delete()
|
||||
|
||||
db.session.delete(design)
|
||||
db.session.commit()
|
||||
|
||||
return "{}"
|
||||
|
||||
@app.route("/designs/submit", methods=["POST"])
|
||||
def submit_design():
|
||||
design = Design()
|
||||
design.name = flask.request.form.get("name", "")
|
||||
design.author = flask.request.form.get("author", "")
|
||||
design.description = flask.request.form.get("description", "")
|
||||
design.data = flask.request.form.get("data", "")
|
||||
design.mods = flask.request.form.get("mods", "base")
|
||||
design.color = flask.request.form.get("color", "#ffffff")
|
||||
|
||||
token = flask.request.headers.get("APIToken")
|
||||
if token:
|
||||
design.token = token
|
||||
|
||||
try:
|
||||
design.size = float(flask.request.form.get("size", "0"))
|
||||
except:
|
||||
design.size = 0
|
||||
|
||||
if not design.name or not design.author or not design.data or design.size < 1:
|
||||
flask.abort(400)
|
||||
return
|
||||
|
||||
db.session.add(design)
|
||||
db.session.commit()
|
||||
return str(design.id)
|
||||
|
||||
@app.route("/designs/recent")
|
||||
def designs_recent():
|
||||
q = Design.query.order_by(desc(Design.ctime))
|
||||
return json.dumps(design_list(q))
|
||||
|
||||
@app.route("/designs/toprated")
|
||||
def designs_toprated():
|
||||
q = Design.query.order_by(desc(Design.upvotes))
|
||||
return json.dumps(design_list(q))
|
||||
|
||||
@app.route("/designs/search/<term>")
|
||||
def designs_search(term):
|
||||
q = Design.query.filter(or_(Design.name.like("%"+term+"%"), Design.author.like("%"+term+"%")))
|
||||
return json.dumps(design_list(q))
|
||||
|
||||
@app.route("/designs/featured")
|
||||
def designs_featured():
|
||||
featured = []
|
||||
#featured += design_list(Design.query.order_by(desc(Design.upvotes)).limit(2), paginate=False)
|
||||
featured = design_list(Design.query.order_by(desc(text("(upvotes / POW((UNIX_TIMESTAMP()-UNIX_TIMESTAMP(ctime))/60/60/24+1,1.8))"))).limit(2), paginate=False)
|
||||
featured += design_list(Design.query.order_by(desc(Design.voteTime)).limit(1), paginate=False)
|
||||
featured += design_list(Design.query.order_by(desc(Design.commentTime)).limit(1), paginate=False)
|
||||
featured += design_list(Design.query.order_by(desc(Design.ctime)).limit(6), paginate=False)
|
||||
|
||||
output = []
|
||||
for d in featured:
|
||||
if any(x["id"] == d["id"] for x in output):
|
||||
continue
|
||||
d["description"] = d["description"] if len(d["description"]) < 120 else d["description"][0:120]
|
||||
output.append(d)
|
||||
if len(output) > 6:
|
||||
break
|
||||
return json.dumps(output)
|
||||
|
||||
@app.route("/updates/version")
|
||||
def up_version():
|
||||
data = getVersions()
|
||||
return data["latest"]
|
||||
|
||||
@app.route("/updates/latest")
|
||||
def latest_list():
|
||||
data = getVersions()
|
||||
VERSIONS = data["versions"]
|
||||
LATEST_VERSION = data["latest"]
|
||||
NEW_HASHES = data["new_hashes"]
|
||||
|
||||
if LATEST_VERSION not in VERSIONS:
|
||||
return "Invalid latest version: "+LATEST_VERSION
|
||||
ver = VERSIONS[LATEST_VERSION]
|
||||
output = ""
|
||||
for relfile, filehash in ver["files"].items():
|
||||
output += relfile+"\t"+filehash+"\n"
|
||||
return output
|
||||
|
||||
@app.route("/updates/<prevhash>/<newhash>")
|
||||
def get_file(prevhash, newhash):
|
||||
if prevhash == newhash:
|
||||
return ""
|
||||
data = getVersions()
|
||||
VERSIONS = data["versions"]
|
||||
LATEST_VERSION = data["latest"]
|
||||
NEW_HASHES = data["new_hashes"]
|
||||
|
||||
newfname = ""
|
||||
relname = ""
|
||||
oldfname = ""
|
||||
oldrelname = ""
|
||||
for vname, ver in VERSIONS.items():
|
||||
if newhash in ver["hashes"]:
|
||||
relname = ver["hashes"][newhash]
|
||||
newfname = join("updates", vname, relname)
|
||||
if prevhash in ver["hashes"]:
|
||||
oldrelname = ver["hashes"][prevhash]
|
||||
oldfname = join("updates", vname, relname)
|
||||
|
||||
if oldrelname != relname:
|
||||
havefile = True
|
||||
for vname, ver in VERSIONS.items():
|
||||
if relname not in ver["files"]:
|
||||
havefile = False
|
||||
break
|
||||
|
||||
if havefile or prevhash not in NEW_HASHES:
|
||||
flask.abort(404)
|
||||
return "INVALID PREVIOUS HASH GIVEN"
|
||||
|
||||
if not os.path.exists(newfname):
|
||||
flask.abort(404)
|
||||
return "FILE NOT FOUND"
|
||||
|
||||
bsize = 65536
|
||||
def generate():
|
||||
with open(newfname, 'rb') as newfile:
|
||||
buf = newfile.read(bsize)
|
||||
while len(buf) > 0:
|
||||
yield buf
|
||||
buf = newfile.read(bsize)
|
||||
|
||||
return flask.Response(generate(), mimetype='application/octet-stream')
|
||||
|
||||
@cache.memoize()
|
||||
def getVersions():
|
||||
output = {"latest": "v1.0.0", "versions": {}, "new_hashes": set(), "new_files": {"Star Ruler 2.exe"}}
|
||||
dirlist = listdir("updates")
|
||||
for f in dirlist:
|
||||
prefix = join("updates", f)
|
||||
if not isdir(prefix):
|
||||
continue
|
||||
ver = {"hashes": {}, "files": {}}
|
||||
output["versions"][f] = ver
|
||||
for (dirpath, dirnames, filenames) in walk(prefix):
|
||||
for fname in filenames:
|
||||
absfile = join(dirpath, fname)
|
||||
relfile = absfile[len(prefix)+1:]
|
||||
filehash = hashfile(absfile)
|
||||
ver["hashes"][filehash] = relfile
|
||||
ver["files"][relfile] = filehash
|
||||
|
||||
if relfile in output["new_files"]:
|
||||
output["new_hashes"].add(filehash)
|
||||
with open("updates/latest") as f:
|
||||
output["latest"] = f.read().strip()
|
||||
if output["latest"] in output["versions"]:
|
||||
latest = output["versions"][output["latest"]]
|
||||
for vname, ver in output["versions"].items():
|
||||
if vname == output["latest"]:
|
||||
continue
|
||||
for f in ver["files"]:
|
||||
if f not in latest["files"]:
|
||||
latest["files"][f] = "0"*32
|
||||
return output
|
||||
|
||||
|
||||
def hashfile(fname):
|
||||
hasher = hashlib.md5()
|
||||
bsize = 65536
|
||||
with open(fname, 'rb') as f:
|
||||
buf = f.read(bsize)
|
||||
while len(buf) > 0:
|
||||
hasher.update(buf)
|
||||
buf = f.read(bsize)
|
||||
return hasher.hexdigest()
|
||||
|
||||
if __name__ == "__main__":
|
||||
#db.create_all()
|
||||
#app.run(debug=True)
|
||||
app.run(host="0.0.0.0", port=2041, debug=True)
|
||||
|
||||
# vim: ff=unix et sw=4 :
|
||||
Reference in New Issue
Block a user