~edwargix/git.sr.ht

624e6bd5ba1f762310deca61b7bf6e761dfaea73 — Drew DeVault 7 years ago c912f6e
Add API endpoint for repository creation, editing
M gitsrht/blueprints/api.py => gitsrht/blueprints/api.py +33 -0
@@ 1,7 1,11 @@
from flask import Blueprint, request, abort
from gitsrht.types import Repository, RepoVisibility, User
from gitsrht.access import UserAccess, has_access, get_repo
from gitsrht.blueprints.public import check_repo
from gitsrht.repos import create_repo
from srht.validation import Validation
from srht.oauth import oauth
from srht.database import db

api = Blueprint("api", __name__)



@@ 32,6 36,15 @@ def repos_GET(oauth_token):
        "results": [repo_json(r) for r in repos]
    }

@api.route("/api/repos", methods=["POST"])
@oauth("repos:write")
def repos_POST(oauth_token):
    valid = Validation(request)
    repo = create_repo(valid, oauth_token.user)
    if not valid.ok:
        return valid.response
    return repo_json(repo)

@api.route("/api/repos/~<owner>")
def repos_username_GET(owner):
    user = User.query.filter(User.username == owner).first()


@@ 54,3 67,23 @@ def repos_username_GET(owner):
        "next": next_id,
        "results": [repo_json(r) for r in repos]
    }

@api.route("/api/repos/~<owner>/<name>")
def repos_by_name_GET(owner, name):
    user, repo = check_repo(owner, name)
    return repo_json(repo)

def prop(valid, resource, prop, **kwargs):
    value = valid.optional(prop, **kwargs)
    if value:
        setattr(resource, prop, value)

@api.route("/api/repos/~<owner>/<name>", methods=["PUT"])
@oauth("repos:write")
def repos_by_name_PUT(oauth_token, owner, name):
    user, repo = check_repo(owner, name, authorized=oauth_token.user)
    valid = Validation(request)
    prop(valid, repo, "visibility", cls=RepoVisibility)
    prop(valid, repo, "description", cls=str)
    db.session.commit()
    return repo_json(repo)

M gitsrht/blueprints/manage.py => gitsrht/blueprints/manage.py +5 -41
@@ 6,10 6,8 @@ from srht.validation import Validation
from gitsrht.types import Repository, RepoVisibility
from gitsrht.decorators import loginrequired
from gitsrht.access import check_access, UserAccess
from gitsrht.repos import create_repo
import shutil
import subprocess
import os
import re

manage = Blueprint('manage', __name__)
repos_path = cfg("cgit", "repos")


@@ 25,48 23,14 @@ def index():
@loginrequired
def create():
    valid = Validation(request)
    repo_name = valid.require("repo-name", friendly_name="Name")
    valid.expect(not repo_name or re.match(r'^[a-z._-][a-z0-9._-]*$', repo_name),
            "Name must match [a-z._-][a-z0-9._-]*", field="repo-name")
    description = valid.optional("repo-description")
    visibility = valid.require("repo-visibility", friendly_name="Visibility")
    valid.expect(not visibility or visibility in [m[0] for m in RepoVisibility.__members__.items()],
            "Expected one of public, private, unlisted for visibility", field="repo-visibility")
    repos = Repository.query.filter(Repository.owner_id == current_user.id)\
            .order_by(Repository.updated.desc()).all()
    valid.expect(not repo_name or not repo_name in [r.name for r in repos],
            "This name is already in use.", field="repo-name")
    another = valid.optional("another")

    repo = create_repo(valid, current_user)
    if not valid.ok:
        return render_template("create.html",
                valid=valid,
                repos=repos,
                repo_name=repo_name,
                repo_description=description,
                visibility=visibility)

    repo = Repository()
    repo.name = repo_name
    repo.description = description
    repo.owner_id = current_user.id
    repo.visibility = RepoVisibility[visibility]
    repo.path = os.path.join(repos_path, "~" + current_user.username, repo.name)
    db.session.add(repo)

    subprocess.run(["mkdir", "-p", repo.path])
    subprocess.run(["git", "init", "--bare"], cwd=repo.path)

    db.session.commit()

    subprocess.run(["git", "config", "srht.repo-id", str(repo.id)], cwd=repo.path)
    subprocess.run(["ln", "-s", post_update, os.path.join(repo.path, "hooks", "update")])
    subprocess.run(["ln", "-s", post_update, os.path.join(repo.path, "hooks", "post-update")])

        return render_template("create.html", **valid.kwargs)
    another = valid.optional("another")
    if another == "on":
        return redirect("/create?another")
    else:
        return redirect("/~{}/{}".format(current_user.username, repo_name))
        return redirect("/~{}/{}".format(current_user.username, repo.name))

@manage.route("/<owner_name>/<repo_name>/settings/info")
@loginrequired

M gitsrht/blueprints/public.py => gitsrht/blueprints/public.py +2 -2
@@ 21,7 21,7 @@ def index():
        repos = None
    return render_template("index.html", repos=repos)

def check_repo(user, repo):
def check_repo(user, repo, authorized=current_user):
    u = User.query.filter(User.username == user).first()
    if not u:
        abort(404)


@@ 30,7 30,7 @@ def check_repo(user, repo):
    if not _repo:
        abort(404)
    if _repo.visibility == RepoVisibility.private:
        if not current_user or current_user.id != _repo.owner_id:
        if not authorized or authorized.id != _repo.owner_id:
            abort(404)
    return u, _repo


A gitsrht/repos.py => gitsrht/repos.py +43 -0
@@ 0,0 1,43 @@
import subprocess
from srht.database import db
from srht.config import cfg
from gitsrht.types import Repository, RepoVisibility
import re
import os

repos_path = cfg("cgit", "repos")
post_update = cfg("git.sr.ht", "post-update-script")

def create_repo(valid, owner):
    repo_name = valid.require("name", friendly_name="Name")
    valid.expect(not repo_name or re.match(r'^[a-z._-][a-z0-9._-]*$', repo_name),
            "Name must match [a-z._-][a-z0-9._-]*", field="name")
    description = valid.optional("description")
    visibility = valid.optional("visibility",
            default="public",
            cls=RepoVisibility)
    repos = Repository.query.filter(Repository.owner_id == owner.id)\
            .order_by(Repository.updated.desc()).all()
    valid.expect(not repo_name or not repo_name in [r.name for r in repos],
            "This name is already in use.", field="name")

    if not valid.ok:
        return None

    repo = Repository()
    repo.name = repo_name
    repo.description = description
    repo.owner_id = owner.id
    repo.visibility = visibility
    repo.path = os.path.join(repos_path, "~" + owner.username, repo.name)
    db.session.add(repo)

    subprocess.run(["mkdir", "-p", repo.path])
    subprocess.run(["git", "init", "--bare"], cwd=repo.path)

    db.session.commit()

    subprocess.run(["git", "config", "srht.repo-id", str(repo.id)], cwd=repo.path)
    subprocess.run(["ln", "-s", post_update, os.path.join(repo.path, "hooks", "update")])
    subprocess.run(["ln", "-s", post_update, os.path.join(repo.path, "hooks", "post-update")])
    return repo

M gitsrht/templates/create.html => gitsrht/templates/create.html +13 -13
@@ 5,28 5,28 @@
    <section class="col-md-6">
      <h3 id="create">Create new repository</h3>
      <form method="POST" action="/create">
        <div class="form-group {{valid.cls("repo-name")}}">
          <label for="repo-name">Name</label>
        <div class="form-group {{valid.cls("name")}}">
          <label for="name">Name</label>
          <input
            {% if another %}
            autofocus
            {% endif %}
            type="text"
            name="repo-name"
            id="repo-name"
            name="name"
            id="name"
            class="form-control"
            value="{{ repo_name or "" }}" />
          {{valid.summary("repo-name")}}
          {{valid.summary("name")}}
        </div>
        <div class="form-group {{valid.cls("repo-description")}}">
          <label for="repo-description">Description</label>
        <div class="form-group {{valid.cls("description")}}">
          <label for="description">Description</label>
          <input
            type="text"
            name="repo-description"
            id="repo-description"
            name="description"
            id="description"
            class="form-control"
            value="{{ repo_description or "" }}" />
          {{valid.summary("repo-description")}}
          {{valid.summary("description")}}
        </div>
        <fieldset class="form-group">
          <legend>Visibility</legend>


@@ 38,7 38,7 @@
              <input
                class="form-check-input"
                type="radio"
                name="repo-visibility"
                name="visibility"
                value="public"
                checked> Public
            </label>


@@ 51,7 51,7 @@
              <input
                class="form-check-input"
                type="radio"
                name="repo-visibility"
                name="visibility"
                value="unlisted"> Unlisted
            </label>
          </div>


@@ 63,7 63,7 @@
              <input
                class="form-check-input"
                type="radio"
                name="repo-visibility"
                name="visibility"
                value="private"> Private
            </label>
          </div>