~edwargix/git.sr.ht

f4328eee74ab8a39db3c82e69ecb523355aac60a — Drew DeVault 8 years ago fcbf809
Update to srht standards
8 files changed, 10 insertions(+), 255 deletions(-)

M git/app.py
M git/blueprints/cgit.py
D git/config.py
D git/db.py
D git/flask.py
D git/validation.py
M run.py
D templates/layout.html
M git/app.py => git/app.py +7 -31
@@ 1,25 1,18 @@
from flask import Flask, render_template, request, g, Response, redirect, url_for
from jinja2 import FileSystemLoader, ChoiceLoader
from flask import render_template, request 

import random
import sys
import os
import locale

from git.config import cfg, cfgi
from git.db import db, init_db
from git.validation import Validation
from git.flask import MetaFlask
from srht.config import cfg, cfgi
from srht.database import DbSession
db = DbSession(cfg("sr.ht", "connection-string"))
db.init()

app = MetaFlask(__name__)
from srht.flask import SrhtFlask
app = SrhtFlask("git", __name__)
app.secret_key = cfg("server", "secret-key")
app.jinja_env.cache = None
init_db()

app.jinja_loader = ChoiceLoader([
    FileSystemLoader("overrides"),
    FileSystemLoader("templates"),
])

try:
    locale.setlocale(locale.LC_ALL, 'en_US')


@@ 47,20 40,3 @@ def handle_404(e):
    if request.path.startswith("/api"):
        return { "errors": [ { "reason": "404 not found" } ] }, 404
    return render_template("not_found.html"), 404

@app.context_processor
def inject():
    return {
        'root': cfg("server", "protocol") + "://" + cfg("server", "domain"),
        'domain': cfg("server", "domain"),
        'protocol': cfg("server", "protocol"),
        'len': len,
        'any': any,
        'request': request,
        'locale': locale,
        'url_for': url_for,
        'cfg': cfg,
        'cfgi': cfgi,
        'valid': Validation(request),
        'datef': lambda d: d.strftime('%Y-%m-%d %H:%M:%S UTC') if d is not None else 'Never',
    }

M git/blueprints/cgit.py => git/blueprints/cgit.py +2 -2
@@ 1,8 1,8 @@
from flask import Blueprint, Response, request, render_template
import requests
from git.config import cfg
from srht.config import cfg

cgit = Blueprint('cgit', __name__, template_folder="../../templates")
cgit = Blueprint('cgit', __name__)

upstream = cfg("cgit", "remote")


D git/config.py => git/config.py +0 -23
@@ 1,23 0,0 @@
import logging

try:
    from configparser import ConfigParser
except ImportError:
    # Python 2 support
    from ConfigParser import ConfigParser

logger = logging.getLogger("sr.ht")
logger.setLevel(logging.DEBUG)

sh = logging.StreamHandler()
sh.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
sh.setFormatter(formatter)

logger.addHandler(sh)

config = ConfigParser()
config.readfp(open('config.ini'))

cfg = lambda s, k: config.get(s, k)
cfgi = lambda s, k: int(cfg(s, k))

D git/db.py => git/db.py +0 -28
@@ 1,28 0,0 @@
from sqlalchemy import create_engine, event
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
from git.config import cfg

engine = create_engine(cfg('git.sr.ht', 'connection-string'))
db = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))

Base = declarative_base()
Base.query = db.query_property()

def init_db():
    #import git.types

    @event.listens_for(Base, 'before_insert', propagate=True)
    def before_insert(mapper, connection, target):
        if hasattr(target, 'created'):
            target.created = datetime.utcnow()
        if hasattr(target, 'updated'):
            target.updated = datetime.utcnow()

    @event.listens_for(Base, 'before_update', propagate=True)
    def before_update(mapper, connection, target):
        if hasattr(target, 'updated'):
            target.updated = datetime.utcnow()

    Base.metadata.create_all(bind=engine)

D git/flask.py => git/flask.py +0 -36
@@ 1,36 0,0 @@
from flask import Flask, Response
from enum import Enum
import decimal
import datetime
import json

DATE_FORMAT = "%Y-%m-%dT%H:%M:%S"

def date_handler(obj):
    if hasattr(obj, 'strftime'):
        return obj.strftime(DATE_FORMAT)
    if isinstance(obj, decimal.Decimal):
        return "{:.2f}".format(obj)
    if isinstance(obj, Enum):
        return obj.name
    return obj

class MetaFlask(Flask):
    def make_response(self, rv):
        # Converts responses from dicts to JSON response objects
        response = None

        def jsonify_wrap(obj):
            jsonification = json.dumps(obj, default=date_handler)
            return Response(jsonification, mimetype='application/json')

        if isinstance(rv, tuple) and \
            (isinstance(rv[0], dict) or isinstance(rv[0], list)):
            response = jsonify_wrap(rv[0]), rv[1]
        elif isinstance(rv, dict):
            response = jsonify_wrap(rv)
        elif isinstance(rv, list):
            response = jsonify_wrap(rv)
        else:
            response = rv
        return super(MetaFlask, self).make_response(response)

D git/validation.py => git/validation.py +0 -73
@@ 1,73 0,0 @@
from markupsafe import Markup
from urllib import parse
import json

class ValidationError:
    def __init__(self, field, message):
        self.field = field
        self.message = message

    def json(self):
        j = dict()
        if self.field:
            j['field'] = self.field
        if self.message:
            j['reason'] = self.message
        return j

class Validation:
    def __init__(self, request):
        contentType = request.headers.get("Content-Type")
        if contentType and contentType == "application/json":
            self.source = json.loads(request.data.decode('utf-8'))
        else:
            self.source = request.form
        self.request = request
        self.errors = []

    @property
    def ok(self):
        return len(self.errors) == 0

    def cls(self, name):
        return 'has-danger' if any([e for e in self.errors if e.field == name]) else ""

    def summary(self, name=None):
        errors = [e.message for e in self.errors if e.field == name or name == '@all']
        if len(errors) == 0:
            return ''
        if name is None:
            return Markup('<div class="alert alert-danger">{}</div>'
                    .format('<br />'.join(errors)))
        else:
            return Markup('<div class="form-control-feedback">{}</div>'
                    .format('<br />'.join(errors)))

    @property
    def response(self):
        return { "errors": [ e.json() for e in self.errors ] }, 400

    def error(self, message, field=None):
        self.errors.append(ValidationError(field, message))

    def optional(self, name, default=None):
        return self.source.get(name) or default

    def require(self, name, friendly_name=None):
        value = self.source.get(name)
        if not friendly_name:
            friendly_name = name
        if not value:
            self.error('{} is required'.format(friendly_name), field=name)
        return value

    def expect(self, condition, message, field=None):
        if not condition:
            self.error(message, field)

def valid_url(url):
    try:
        u = parse.urlparse(url)
        return bool(u.scheme and u.netloc and u.scheme in ['http', 'https'])
    except:
        return False

M run.py => run.py +1 -1
@@ 1,5 1,5 @@
from git.app import app
from git.config import cfg, cfgi
from srht.config import cfg, cfgi

import os


D templates/layout.html => templates/layout.html +0 -61
@@ 1,61 0,0 @@
<!doctype html>
<html>
  <head>
    {% block title %}
    <title>sr.ht git</title>
    {% endblock %}
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/main.css">
  </head>
  <body>
    {% block nav %}
    <nav class="navbar navbar-light">
      <a class="navbar-brand" href="{{cfg("network", "meta")}}">sr.ht</a>
      <ul class="nav navbar-nav">
        <li class="nav-item">
            <a class="nav-link" href="{{cfg("network", "files")}}">files</a>
        </li>
        <li class="nav-item active">
            <a class="nav-link" href="{{cfg("network", "git")}}">git</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{cfg("network", "bugs")}}">bugs</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{cfg("network", "lists")}}">lists</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{cfg("network", "ftp")}}">ftp</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{cfg("network", "man")}}">man</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{cfg("network", "meta")}}">meta</a>
        </li>
      </ul>
      {% if current_user %}
      <div class="login pull-xs-right">
        Logged in as
        <a href="{{cfg("network", "meta")}}/profile">{{current_user.username}}</a>
        &mdash;
        <a href="/logout">Log out</a>
      </div>
      {% else %}
      <div class="login pull-xs-right">
        <a href="/login">Log in</a>
        &mdash;
        <a href="{{cfg("network", "meta")}}">Register</a>
      </div>
      {% endif %}
    </nav>
    {% endblock %}
    {% block body %}
    <div class="container">
        {% block content %}{% endblock %}
    </div>
    {% endblock %}
    {% block modal %}{% endblock %}
    {% block scripts %}{% endblock %}
  </body>
</html>