~edwargix/git.sr.ht

bf7b4d0915a1b3c0b28622fe09039ae7972a525d — Drew DeVault 8 years ago 4582591
Implement repository creation
M git/app.py => git/app.py +11 -7
@@ 27,18 27,22 @@ try:
except:
    pass

from git.blueprints.cgit import cgit
def oauth_url(return_to):
    return "{}/oauth/authorize?client_id={}&scopes=profile,keys&state={}".format(
        cfg("network", "meta"),
        cfg("meta.sr.ht", "oauth-client-id"),
        urllib.parse.quote_plus(return_to))

from git.blueprints.auth import auth
from git.blueprints.cgit import cgit
from git.blueprints.manage import manage

app.register_blueprint(cgit)
app.register_blueprint(auth)
app.register_blueprint(cgit)
app.register_blueprint(manage)

@app.context_processor
def inject():
    return {
        "oauth_url": "{}/oauth/authorize?client_id={}&scopes=profile,keys&state={}".format(
            cfg("network", "meta"),
            cfg("meta.sr.ht", "oauth-client-id"),
            urllib.parse.quote(request.full_path)
        )
        "oauth_url": oauth_url(request.full_path)
    }

M git/blueprints/auth.py => git/blueprints/auth.py +1 -1
@@ 1,4 1,4 @@
from flask import Blueprint, Response, request, render_template, redirect
from flask import Blueprint, request, render_template, redirect
from flask_login import login_user, logout_user
from srht.config import cfg
from srht.flask import DATE_FORMAT

A git/blueprints/manage.py => git/blueprints/manage.py +60 -0
@@ 0,0 1,60 @@
from flask import Blueprint, request, render_template, redirect
from flask_login import current_user
from srht.config import cfg
from srht.database import db
from srht.validation import Validation
from git.types import Repository, RepoVisibility
from git.decorators import loginrequired
import subprocess
import os
import re

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

@manage.route("/manage")
@loginrequired
def index():
    repos = Repository.query.filter(Repository.owner_id == current_user.id)\
            .order_by(Repository.updated).all()
    return render_template("manage.html", repos=repos)

@manage.route("/manage/create", methods=["POST"])
@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")
    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).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")

    if not valid.ok:
        return render_template("manage.html",
                valid=valid,
                repos=repos,
                repo_name=repo_name)

    repo = Repository()
    repo.name = repo_name
    repo.owner_id = current_user.id
    repo.visibility = RepoVisibility[visibility]
    db.session.add(repo)

    path = os.path.join(repos_path, "~" + current_user.username)

    subprocess.run(["git", "init", "--bare", repo_name], cwd=path)
    subprocess.run(["ln", "-s", repo_name, repo_name + ".git"], cwd=path)

    # TODO: other shit, probably, like setting up hooks

    db.session.commit()

    return redirect("/~{}/{}".format(current_user.username, repo_name))

A git/decorators.py => git/decorators.py +15 -0
@@ 0,0 1,15 @@
from flask import redirect, request, abort
from flask_login import current_user
from functools import wraps
from git.app import oauth_url

import urllib

def loginrequired(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        if not current_user:
            return redirect(oauth_url(request.url))
        else:
            return f(*args, **kwargs)
    return wrapper

M git/types/__init__.py => git/types/__init__.py +1 -1
@@ 1,2 1,2 @@
from .user import User
from .repository import Repository
from .repository import Repository, RepoVisibility

M git/types/repository.py => git/types/repository.py +10 -0
@@ 1,6 1,12 @@
import sqlalchemy as sa
import sqlalchemy_utils as sau
from srht.database import Base
from enum import Enum

class RepoVisibility(Enum):
    public = 'public'
    private = 'private'
    unlisted = 'unlisted'

class Repository(Base):
    __tablename__ = 'repository'


@@ 10,3 16,7 @@ class Repository(Base):
    name = sa.Column(sa.Unicode(256), nullable=False)
    owner_id = sa.Column(sa.Integer, sa.ForeignKey('user.id'), nullable=False)
    owner = sa.orm.relationship('User', backref=sa.orm.backref('repos'))
    visibility = sa.Column(
            sau.ChoiceType(RepoVisibility, impl=sa.String()),
            nullable=False,
            default=RepoVisibility.public)

A templates/manage.html => templates/manage.html +76 -0
@@ 0,0 1,76 @@
{% extends "git.html" %}
{% block content %}
<div class="container">
  <div class="row">
    <section class="col-md-6">
      {% if len(repos) != 0 %}
      <h3>Manage repositories</h3>
      <dl>
        {% for repo in repos %}
          <dt>
            <a href="/~{{current_user.username}}/{{repo.name}}">
              ~{{current_user.username}}/{{repo.name}}
            </a>
          </dt>
          <dd>
            <a href="#">Edit details</a>
            &mdash;
            <a href="#">Manage collaborators</a>
            &mdash;
            <a href="#">Delete repository</a>
          </dd>
        {% endfor %}
      </dl>
      {% endif %}
      <h3>Create new repository</h3>
      <form method="POST" action="/manage/create">
        <div class="form-group {{valid.cls("repo-name")}}">
          <label for="client-name">Name</label>
          <input
            type="text"
            name="repo-name"
            id="repo-name"
            class="form-control"
            value="{{ repo_name or "" }}" />
          {{valid.summary("repo-name")}}
        </div>
        <fieldset class="form-group">
          <legend>Visibility</legend>
          <div class="form-check form-check-inline">
            <label class="form-check-label">
              <input
                class="form-check-input"
                type="radio"
                name="repo-visibility"
                value="public"
                title="Publically visible and listed on your profile"
                checked> Public
            </label>
          </div>
          <div class="form-check form-check-inline">
            <label class="form-check-label">
              <input
                class="form-check-input"
                type="radio"
                name="repo-visibility"
                title="Visible to anyone with the link, but not shown on your profile"
                value="unlisted"> Unlisted
            </label>
          </div>
          <div class="form-check form-check-inline">
            <label class="form-check-label">
              <input
                class="form-check-input"
                type="radio"
                name="repo-visibility"
                title="Only visible to you and your collaborators"
                value="private"> Private
            </label>
          </div>
        </fieldset>
        <button type="submit" class="btn btn-default">Create</button>
      </form>
    </section>
  </div>
</div>
{% endblock %}