mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-22 22:12:24 +01:00
Add API to delete releases
This commit is contained in:
parent
e1fe63ab19
commit
912ebbc409
@ -83,9 +83,6 @@ def homepage():
|
|||||||
downloads_result = db.session.query(func.sum(Package.downloads)).one_or_none()
|
downloads_result = db.session.query(func.sum(Package.downloads)).one_or_none()
|
||||||
downloads = 0 if not downloads_result or not downloads_result[0] else downloads_result[0]
|
downloads = 0 if not downloads_result or not downloads_result[0] else downloads_result[0]
|
||||||
|
|
||||||
tags = db.session.query(func.count(Tags.c.tag_id), Tag) \
|
|
||||||
.select_from(Tag).outerjoin(Tags).group_by(Tag.id).order_by(db.asc(Tag.title)).all()
|
|
||||||
|
|
||||||
def mapPackages(packages):
|
def mapPackages(packages):
|
||||||
return [pkg.getAsDictionaryKey() for pkg in packages]
|
return [pkg.getAsDictionaryKey() for pkg in packages]
|
||||||
|
|
||||||
@ -97,7 +94,7 @@ def homepage():
|
|||||||
"pop_mod": mapPackages(pop_mod),
|
"pop_mod": mapPackages(pop_mod),
|
||||||
"pop_txp": mapPackages(pop_txp),
|
"pop_txp": mapPackages(pop_txp),
|
||||||
"pop_game": mapPackages(pop_gam),
|
"pop_game": mapPackages(pop_gam),
|
||||||
"high_reviewed": mapPackages(high_reviewed),
|
"high_reviewed": mapPackages(high_reviewed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -113,9 +110,6 @@ def resolve_package_deps(out, package, only_hard):
|
|||||||
if only_hard and dep.optional:
|
if only_hard and dep.optional:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
name = None
|
|
||||||
fulfilled_by = None
|
|
||||||
|
|
||||||
if dep.package:
|
if dep.package:
|
||||||
name = dep.package.name
|
name = dep.package.name
|
||||||
fulfilled_by = [ dep.package.getId() ]
|
fulfilled_by = [ dep.package.getId() ]
|
||||||
@ -174,7 +168,7 @@ def topic_set_discard():
|
|||||||
|
|
||||||
@bp.route("/api/minetest_versions/")
|
@bp.route("/api/minetest_versions/")
|
||||||
def versions():
|
def versions():
|
||||||
return jsonify([{ "name": rel.name, "protocol_version": rel.protocol }\
|
return jsonify([rel.getAsDictionary() \
|
||||||
for rel in MinetestRelease.query.all() if rel.getActual() is not None])
|
for rel in MinetestRelease.query.all() if rel.getActual() is not None])
|
||||||
|
|
||||||
|
|
||||||
@ -196,8 +190,7 @@ def markdown():
|
|||||||
@bp.route("/api/packages/<author>/<name>/releases/")
|
@bp.route("/api/packages/<author>/<name>/releases/")
|
||||||
@is_package_page
|
@is_package_page
|
||||||
def list_releases(package):
|
def list_releases(package):
|
||||||
releases = package.releases.filter_by(approved=True).all()
|
return jsonify([ rel.getAsDictionary() for rel in package.releases.all() ])
|
||||||
return jsonify([ rel.getAsDictionary() for rel in releases ])
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/api/packages/<author>/<name>/releases/new/", methods=["POST"])
|
@bp.route("/api/packages/<author>/<name>/releases/new/", methods=["POST"])
|
||||||
@ -215,14 +208,11 @@ def create_release(token, package):
|
|||||||
if "title" not in data:
|
if "title" not in data:
|
||||||
error(400, "Title is required in the POST data")
|
error(400, "Title is required in the POST data")
|
||||||
|
|
||||||
if request.json:
|
if data.get("method") == "git":
|
||||||
for option in ["method", "ref"]:
|
for option in ["method", "ref"]:
|
||||||
if option not in data:
|
if option not in data:
|
||||||
error(400, option + " is required in the POST data")
|
error(400, option + " is required in the POST data")
|
||||||
|
|
||||||
if data["method"].lower() != "git":
|
|
||||||
error(400, "Release-creation methods other than git are not supported")
|
|
||||||
|
|
||||||
return api_create_vcs_release(token, package, data["title"], data["ref"])
|
return api_create_vcs_release(token, package, data["title"], data["ref"])
|
||||||
|
|
||||||
elif request.files:
|
elif request.files:
|
||||||
@ -232,6 +222,43 @@ def create_release(token, package):
|
|||||||
|
|
||||||
return api_create_zip_release(token, package, data["title"], file)
|
return api_create_zip_release(token, package, data["title"], file)
|
||||||
|
|
||||||
|
else:
|
||||||
|
error(400, "Unknown release-creation method. Specify the method or provide a file.")
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/api/packages/<author>/<name>/releases/<int:id>/")
|
||||||
|
@is_package_page
|
||||||
|
def release(package: Package, id: int):
|
||||||
|
release = PackageRelease.query.get(id)
|
||||||
|
if release is None or release.package != package:
|
||||||
|
error(404, "Release not found")
|
||||||
|
|
||||||
|
return jsonify(release.getAsDictionary())
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/api/packages/<author>/<name>/releases/<int:id>/", methods=["DELETE"])
|
||||||
|
@csrf.exempt
|
||||||
|
@is_package_page
|
||||||
|
@is_api_authd
|
||||||
|
def delete_release(token: APIToken, package: Package, id: int):
|
||||||
|
release = PackageRelease.query.get(id)
|
||||||
|
if release is None or release.package != package:
|
||||||
|
error(404, "Release not found")
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
error(401, "Authentication needed")
|
||||||
|
|
||||||
|
if not token.canOperateOnPackage(package):
|
||||||
|
error(403, "API token does not have access to the package")
|
||||||
|
|
||||||
|
if not release.checkPerm(token.owner, Permission.DELETE_RELEASE):
|
||||||
|
error(403, "Unable to delete the release, make sure there's a newer release available")
|
||||||
|
|
||||||
|
db.session.delete(release)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return jsonify({"success": True})
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/api/packages/<author>/<name>/screenshots/")
|
@bp.route("/api/packages/<author>/<name>/screenshots/")
|
||||||
@is_package_page
|
@is_package_page
|
||||||
|
@ -15,7 +15,7 @@ Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
|
|||||||
|
|
||||||
### Misc
|
### Misc
|
||||||
|
|
||||||
* GET `/api/whoami/` - Json dictionary with the following keys:
|
* GET `/api/whoami/` - JSON dictionary with the following keys:
|
||||||
* `is_authenticated` - True on successful API authentication
|
* `is_authenticated` - True on successful API authentication
|
||||||
* `username` - Username of the user authenticated as, null otherwise.
|
* `username` - Username of the user authenticated as, null otherwise.
|
||||||
* 4xx status codes will be thrown on unsupported authentication type, invalid access token, or other errors.
|
* 4xx status codes will be thrown on unsupported authentication type, invalid access token, or other errors.
|
||||||
@ -40,13 +40,24 @@ Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
|
|||||||
* `pop_txp` - popular textures
|
* `pop_txp` - popular textures
|
||||||
* `pop_game` - popular games
|
* `pop_game` - popular games
|
||||||
* `high_reviewed` - highest reviewed
|
* `high_reviewed` - highest reviewed
|
||||||
|
* `tags`
|
||||||
|
|
||||||
### Releases
|
### Releases
|
||||||
|
|
||||||
* GET `/api/packages/<username>/<name>/releases/` (List)
|
* GET `/api/packages/<username>/<name>/releases/` (List)
|
||||||
|
* Returns array of release dictionaries with keys:
|
||||||
|
* `id`: release ID
|
||||||
|
* `title`: human-readable title
|
||||||
|
* `release_date`: Date released
|
||||||
|
* `url`: download URL
|
||||||
|
* `commit`: commit hash or null
|
||||||
|
* `downloads`: number of downloads
|
||||||
|
* `min_minetest_version`: dict or null, minimum supported minetest version (inclusive).
|
||||||
|
* `max_minetest_version`: dict or null, minimum supported minetest version (inclusive).
|
||||||
|
* GET `/api/packages/<username>/<name>/releases/<id>/` (Read)
|
||||||
* POST `/api/packages/<username>/<name>/releases/new/` (Create)
|
* POST `/api/packages/<username>/<name>/releases/new/` (Create)
|
||||||
* Requires authentication.
|
* Requires authentication.
|
||||||
* Body is multipart form if zip upload, JSON otherwise.
|
* Body can be JSON or multipart form data. Zip uploads must be multipart form data.
|
||||||
* `title`: human-readable name of the release.
|
* `title`: human-readable name of the release.
|
||||||
* For Git release creation:
|
* For Git release creation:
|
||||||
* `method`: must be `git`.
|
* `method`: must be `git`.
|
||||||
@ -54,7 +65,10 @@ Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
|
|||||||
* For zip upload release creation:
|
* For zip upload release creation:
|
||||||
* `file`: multipart file to upload, like `<input type=file>`.
|
* `file`: multipart file to upload, like `<input type=file>`.
|
||||||
* You can set min and max Minetest Versions [using the content's .conf file](/help/package_config/).
|
* You can set min and max Minetest Versions [using the content's .conf file](/help/package_config/).
|
||||||
|
* DELETE `/api/packages/<username>/<name>/releases/<id>/` (Delete)
|
||||||
|
* Requires authentication.
|
||||||
|
* Deletes release.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -64,9 +78,13 @@ curl -X POST https://content.minetest.net/api/packages/username/name/releases/ne
|
|||||||
-d "{\"method\": \"git\", \"title\": \"My Release\", \"ref\": \"master\" }"
|
-d "{\"method\": \"git\", \"title\": \"My Release\", \"ref\": \"master\" }"
|
||||||
|
|
||||||
# Create release from zip upload
|
# Create release from zip upload
|
||||||
curl https://content.minetest.net/api/packages/username/name/releases/new/ \
|
curl -X POST https://content.minetest.net/api/packages/username/name/releases/new/ \
|
||||||
-H "Authorization: Bearer YOURTOKEN" \
|
-H "Authorization: Bearer YOURTOKEN" \
|
||||||
-F title="My Release" -F file=@path/to/file.zip
|
-F title="My Release" -F file=@path/to/file.zip
|
||||||
|
|
||||||
|
# Delete release
|
||||||
|
curl -X DELETE https://content.minetest.net/api/packages/username/name/releases/3/ \
|
||||||
|
-H "Authorization: Bearer YOURTOKEN"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Screenshots
|
### Screenshots
|
||||||
@ -96,7 +114,7 @@ Examples:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create screenshots
|
# Create screenshots
|
||||||
curl https://content.minetest.net/api/packages/username/name/screenshots/new/ \
|
curl -X POST https://content.minetest.net/api/packages/username/name/screenshots/new/ \
|
||||||
-H "Authorization: Bearer YOURTOKEN" \
|
-H "Authorization: Bearer YOURTOKEN" \
|
||||||
-F title="My Release" -F file=@path/to/screnshot.png
|
-F title="My Release" -F file=@path/to/screnshot.png
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from werkzeug.exceptions import abort
|
import datetime
|
||||||
|
|
||||||
from app.logic.LogicError import LogicError
|
from app.logic.LogicError import LogicError
|
||||||
from app.logic.uploads import upload_file
|
from app.logic.uploads import upload_file
|
||||||
@ -7,10 +7,15 @@ from app.utils import addNotification
|
|||||||
|
|
||||||
|
|
||||||
def do_create_screenshot(user: User, package: Package, title: str, file):
|
def do_create_screenshot(user: User, package: Package, title: str, file):
|
||||||
|
thirty_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes=30)
|
||||||
|
count = package.screenshots.filter(PackageScreenshot.created_at > thirty_minutes_ago).count()
|
||||||
|
if count >= 20:
|
||||||
|
raise LogicError(429, "Too many requests, please wait before trying again")
|
||||||
|
|
||||||
uploaded_url, uploaded_path = upload_file(file, "image", "a PNG or JPG image file")
|
uploaded_url, uploaded_path = upload_file(file, "image", "a PNG or JPG image file")
|
||||||
|
|
||||||
counter = 1
|
counter = 1
|
||||||
for screenshot in package.screenshots:
|
for screenshot in package.screenshots.all():
|
||||||
screenshot.order = counter
|
screenshot.order = counter
|
||||||
counter += 1
|
counter += 1
|
||||||
|
|
||||||
|
@ -759,6 +759,13 @@ class MinetestRelease(db.Model):
|
|||||||
def getActual(self):
|
def getActual(self):
|
||||||
return None if self.name == "None" else self
|
return None if self.name == "None" else self
|
||||||
|
|
||||||
|
def getAsDictionary(self):
|
||||||
|
return {
|
||||||
|
"name": self.name,
|
||||||
|
"protocol_version": self.protocol,
|
||||||
|
"is_dev": "-dev" in self.name,
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cls, version, protocol_num):
|
def get(cls, version, protocol_num):
|
||||||
if version:
|
if version:
|
||||||
@ -810,8 +817,8 @@ class PackageRelease(db.Model):
|
|||||||
"release_date": self.releaseDate.isoformat(),
|
"release_date": self.releaseDate.isoformat(),
|
||||||
"commit": self.commit_hash,
|
"commit": self.commit_hash,
|
||||||
"downloads": self.downloads,
|
"downloads": self.downloads,
|
||||||
"min_protocol": self.min_rel and self.min_rel.protocol,
|
"min_minetest_version": self.min_rel and self.min_rel.getAsDictionary(),
|
||||||
"max_protocol": self.max_rel and self.max_rel.protocol
|
"max_minetest_version": self.max_rel and self.max_rel.getAsDictionary(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def getEditURL(self):
|
def getEditURL(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user