from flask_user import *
from . import bp
from .auth import is_api_authd
+from .support import error, handleCreateRelease
from app import csrf
from app.models import *
from app.utils import is_package_page
return jsonify(ret)
+@bp.route("/api/packages/<author>/<name>/releases/")
+@is_package_page
+def list_releases(package):
+ releases = package.releases.filter_by(approved=True).all()
+ return jsonify([ rel.getAsDictionary() for rel in releases ])
+
+
@bp.route("/api/topics/")
def topics():
qb = QueryBuilder(request.args)
@bp.route("/api/markdown/", methods=["POST"])
@csrf.exempt
-def clean_markdown():
+def markdown():
return render_markdown(request.data.decode("utf-8"))
+
+
+@bp.route("/api/packages/<author>/<name>/releases/new/", methods=["POST"])
+@csrf.exempt
+@is_package_page
+@is_api_authd
+def create_release(token, package):
+ json = request.json
+ if json is None:
+ return error(400, "JSON post data is required")
+
+ for option in ["method", "title", "ref"]:
+ if json.get(option) is None:
+ return error(400, option + " is required in the POST data")
+
+
+ if json["method"].lower() != "vcs":
+ return error(400, "Release-creation methods other than VCS are not supported")
+
+ return handleCreateRelease(token, package, json["title"], json["ref"])
--- /dev/null
+from app.models import PackageRelease, db, Permission
+from app.tasks.importtasks import makeVCSRelease
+from celery import uuid
+from flask import jsonify, make_response, url_for
+import datetime
+
+
+def error(status, message):
+ return make_response(jsonify({ "success": False, "error": message }), status)
+
+
+def handleCreateRelease(token, package, title, ref):
+ if not token.canOperateOnPackage(package):
+ return error(403, "API token does not have access to the package")
+
+ if not package.checkPerm(token.owner, Permission.MAKE_RELEASE):
+ return error(403, "Permission denied. Missing MAKE_RELEASE permission")
+
+ five_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes=5)
+ count = package.releases.filter(PackageRelease.releaseDate > five_minutes_ago).count()
+ if count >= 2:
+ return error(429, "Too many requests, please wait before trying again")
+
+ rel = PackageRelease()
+ rel.package = package
+ rel.title = title
+ rel.url = ""
+ rel.task_id = uuid()
+ rel.min_rel = None
+ rel.max_rel = None
+ db.session.add(rel)
+ db.session.commit()
+
+ makeVCSRelease.apply_async((rel.id, ref), task_id=rel.task_id)
+
+ return jsonify({
+ "success": True,
+ "task": url_for("tasks.check", id=rel.task_id),
+ "release": rel.getAsDictionary()
+ })
* GET `/api/packages/` - See [Package Queries](#package-queries)
* GET `/api/packages/<username>/<name>/`
+### Releases
+
+* GET `/api/packages/<username>/<name>/releases/`
+* POST `/api/packages/<username>/<name>/releases/`
+ * Requires authentication.
+ * `title`: human-readable name of the release.
+ * `method`: Must be `vcs`.
+ * `min_protocol`: minimum Minetest protocol version. See [Minetest](#minetest).
+ * `min_protocol`: maximum Minetest protocol version. See [Minetest](#minetest).
+ * If `vcs` release-creation method:
+ * `ref` - git reference.
+
+
### Topics
* GET `/api/topics/` - Supports [Package Queries](#package-queries), and the following two options:
"short_description": self.short_desc,
"desc": self.desc,
"type": self.type.toName(),
- "created_at": self.created_at,
+ "created_at": self.created_at.isoformat(),
"license": self.license.name,
"media_license": self.media_license.name,
# If the release is approved, then the task_id must be null and the url must be present
CK_approval_valid = db.CheckConstraint("not approved OR (task_id IS NULL AND (url = '') IS NOT FALSE)")
+ def getAsDictionary(self):
+ return {
+ "id": self.id,
+ "title": self.title,
+ "url": self.url if self.url != "" else None,
+ "release_date": self.releaseDate.isoformat(),
+ "commit": self.commit_hash,
+ "downloads": self.downloads,
+ "min_protocol": self.min_rel and self.min_rel.protocol,
+ "max_protocol": self.max_rel and self.max_rel.protocol
+ }
+
def getEditURL(self):
return url_for("packages.edit_release",
author=self.package.author.username,
package = db.relationship("Package", foreign_keys=[package_id])
def canOperateOnPackage(self, package):
- if self.package and self.package != None:
+ if self.package and self.package != package:
return False
- return package.owner == self.owner
+ return package.author == self.owner
class EditRequest(db.Model):