Move signin to use AJAX. Render all flask templates with the common header. Move the header to a partial. Add account recovery.

This commit is contained in:
yackob03 2013-10-14 17:50:07 -04:00
parent e182163d34
commit 4c15072c5a
17 changed files with 653 additions and 617 deletions

1
app.py
View file

@ -32,7 +32,6 @@ Principal(app, use_sessions=True)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'signin'
mail = Mail()
mail.init_app(app)

View file

@ -109,6 +109,29 @@ def confirm_user_email(code):
return user
def create_reset_password_email_code(email):
try:
user = User.get(User.email == email)
except User.DoesNotExist:
raise InvalidEmailAddressException('Email address was not found.');
code = EmailConfirmation.create(user=user, pw_reset=True)
return code
def validate_reset_code(code):
try:
code = EmailConfirmation.get(EmailConfirmation.code == code,
EmailConfirmation.pw_reset == True)
except EmailConfirmation.DoesNotExist:
return None
user = code.user
code.delete_instance()
return user
def get_user(username):
try:
return User.get(User.username == username)

View file

@ -2,19 +2,21 @@ import logging
import stripe
from flask import request, make_response, jsonify, abort
from flask.ext.login import login_required, current_user
from flask.ext.login import login_required, current_user, logout_user
from flask.ext.principal import identity_changed, AnonymousIdentity
from functools import wraps
from collections import defaultdict
from data import model
from app import app
from util.email import send_confirmation_email
from util.email import send_confirmation_email, send_recovery_email
from util.names import parse_repository_name
from util.gravatar import compute_hash
from auth.permissions import (ReadRepositoryPermission,
ModifyRepositoryPermission,
AdministerRepositoryPermission)
from endpoints import registry
from endpoints.web import common_login
import re
logger = logging.getLogger(__name__)
@ -106,6 +108,53 @@ def create_user_api():
return error_resp
@app.route('/api/signin', methods=['POST'])
def signin_api():
signin_data = request.get_json()
username = signin_data['username']
password = signin_data['password']
#TODO Allow email login
needs_email_verification = False
invalid_credentials = False
verified = model.verify_user(username, password)
if verified:
if common_login(verified):
return make_response('Success', 200)
else:
needs_email_verification = True
else:
invalid_credentials = True
response = jsonify({
'needsEmailVerification': needs_email_verification,
'invalidCredentials': invalid_credentials,
})
response.status_code = 403
return response
@app.route("/api/signout", methods=['POST'])
@api_login_required
def logout():
logout_user()
identity_changed.send(app, identity=AnonymousIdentity())
return make_response('Success', 200)
@app.route("/api/recovery", methods=['POST'])
def send_recovery():
email = request.get_json()['email']
code = model.create_reset_password_email_code(email)
send_recovery_email(email, code.code)
return make_response('Created', 201)
@app.route('/api/users/<prefix>', methods=['GET'])
@api_login_required
def get_matching_users(prefix):

View file

@ -1,9 +1,9 @@
import logging
import requests
from flask import (abort, send_file, redirect, request, url_for,
render_template, make_response)
from flask.ext.login import login_user, UserMixin, login_required, logout_user
from flask import (abort, redirect, request, url_for, render_template,
make_response)
from flask.ext.login import login_user, UserMixin, login_required
from flask.ext.principal import identity_changed, Identity, AnonymousIdentity
from data import model
@ -37,7 +37,7 @@ def load_user(username):
@app.route('/', methods=['GET'], defaults={'path': ''})
@app.route('/repository/<path:path>', methods=['GET'])
def index(path):
return send_file('templates/index.html')
return render_template('index.html')
@app.route('/plans/')
@ -55,6 +55,11 @@ def user():
return index('')
@app.route('/signin/')
def signin():
return index('')
@app.route('/status', methods=['GET'])
def status():
return make_response('Healthy')
@ -62,12 +67,12 @@ def status():
@app.route('/tos', methods=['GET'])
def tos():
return send_file('templates/tos.html')
return render_template('tos.html')
@app.route('/privacy', methods=['GET'])
def privacy():
return send_file('templates/privacy.html')
return render_template('privacy.html')
def common_login(db_user):
@ -81,34 +86,6 @@ def common_login(db_user):
return False
@app.route('/signin', methods=['GET'])
def render_signin_page():
return render_template('signin.html',
github_client_id=app.config['GITHUB_CLIENT_ID'])
@app.route('/signin', methods=['POST'])
def signin():
username = request.form['username']
password = request.form['password']
#TODO Allow email login
verified = model.verify_user(username, password)
if verified:
if common_login(verified):
return redirect(request.args.get('next') or url_for('index'))
else:
return render_template('signin.html',
needs_email_verification=True,
github_client_id=app.config['GITHUB_CLIENT_ID'])
else:
return render_template('signin.html',
username=username,
invalid_credentials=True,
github_client_id=app.config['GITHUB_CLIENT_ID'])
@app.route('/oauth2/github/callback', methods=['GET'])
def github_oauth_callback():
code = request.args.get('code')
@ -183,16 +160,18 @@ def confirm_email():
return redirect(url_for('index'))
@app.route('/recovery', methods=['GET'])
def confirm_recovery():
code = request.values['code']
user = model.validate_reset_code(code)
if user:
common_login(user)
return redirect(url_for('user'))
else:
abort(403)
@app.route('/reset', methods=['GET'])
def password_reset():
pass
@app.route("/signout")
@login_required
def logout():
logout_user()
identity_changed.send(app, identity=AnonymousIdentity())
return redirect(url_for('index'))

View file

@ -975,4 +975,29 @@ p.editable:hover i {
.tos ul {
margin-top: 10px;
}
.form-signin input {
margin-bottom: 20px;
}
.form-signin {
text-align: center;
margin-bottom: 20px;
}
.social-alternate {
color: #777;
font-size: 3em;
margin-left: 43px;
}
.social-alternate .inner-text {
text-align: center;
position: relative;
color: white;
left: -42px;
top: -9px;
font-weight: bold;
font-size: .4em;
}

View file

@ -1,66 +0,0 @@
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #eee;
}
.signin-container {
text-align: center;
}
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
text-align: center;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin .checkbox {
font-weight: normal;
}
.form-signin .form-control {
position: relative;
font-size: 16px;
height: auto;
padding: 10px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.alert {
max-width: 300px;
margin: 0 auto;
}
.social-alternate {
color: #777;
font-size: 3em;
margin-left: 43px;
}
.social-alternate .inner-text {
text-align: center;
position: relative;
color: white;
left: -43px;
top: -9px;
font-weight: bold;
font-size: .4em;
}

View file

@ -136,6 +136,7 @@ quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics',
when('/user/', {title: 'User Admin', templateUrl: '/static/partials/user-admin.html', controller: UserAdminCtrl}).
when('/guide/', {title: 'Getting Started Guide', templateUrl: '/static/partials/guide.html', controller: GuideCtrl}).
when('/plans/', {title: 'Plans and Pricing', templateUrl: '/static/partials/plans.html', controller: PlansCtrl}).
when('/signin/', {title: 'Signin', templateUrl: '/static/partials/signin.html', controller: SigninCtrl}).
when('/', {title: 'Hosted Private Docker Registry', templateUrl: '/static/partials/landing.html', controller: LandingCtrl}).
otherwise({redirectTo: '/'});
}]).

View file

@ -35,48 +35,101 @@ function getMarkedDown(string) {
return Markdown.getSanitizingConverter().makeHtml(string || '');
}
function HeaderCtrl($scope, UserService) {
function HeaderCtrl($scope, $location, UserService, Restangular) {
$scope.$watch( function () { return UserService.currentUser(); }, function (currentUser) {
$scope.user = currentUser;
}, true);
$('#repoSearch').typeahead({
name: 'repositories',
remote: {
url: '/api/find/repository?query=%QUERY',
filter: function(data) {
var datums = [];
for (var i = 0; i < data.repositories.length; ++i) {
var repo = data.repositories[i];
datums.push({
'value': repo.name,
'tokens': [repo.name, repo.namespace],
'repo': repo
});
$scope.signout = function() {
var signoutPost = Restangular.one('signout');
signoutPost.customPOST().then(function() {
UserService.load();
$location.path('/');
});
}
$scope.$on('$includeContentLoaded', function() {
// THIS IS BAD, MOVE THIS TO A DIRECTIVE
$('#repoSearch').typeahead({
name: 'repositories',
remote: {
url: '/api/find/repository?query=%QUERY',
filter: function(data) {
var datums = [];
for (var i = 0; i < data.repositories.length; ++i) {
var repo = data.repositories[i];
datums.push({
'value': repo.name,
'tokens': [repo.name, repo.namespace],
'repo': repo
});
}
return datums;
}
},
template: function (datum) {
template = '<div class="repo-mini-listing">';
template += '<i class="icon-hdd icon-large"></i>'
template += '<span class="name">' + datum.repo.namespace +'/' + datum.repo.name + '</span>'
if (datum.repo.description) {
template += '<span class="description">' + getFirstTextLine(datum.repo.description) + '</span>'
}
return datums;
}
},
template: function (datum) {
template = '<div class="repo-mini-listing">';
template += '<i class="icon-hdd icon-large"></i>'
template += '<span class="name">' + datum.repo.namespace +'/' + datum.repo.name + '</span>'
if (datum.repo.description) {
template += '<span class="description">' + getFirstTextLine(datum.repo.description) + '</span>'
}
template += '</div>'
return template;
},
template += '</div>'
return template;
},
});
});
$('#repoSearch').on('typeahead:selected', function (e, datum) {
$('#repoSearch').typeahead('setQuery', '');
document.location = '/repository/' + datum.repo.namespace + '/' + datum.repo.name
$('#repoSearch').on('typeahead:selected', function (e, datum) {
$('#repoSearch').typeahead('setQuery', '');
document.location = '/repository/' + datum.repo.namespace + '/' + datum.repo.name
});
});
}
function SigninCtrl($scope, $location, $timeout, Restangular, KeyService, UserService) {
$scope.githubClientId = KeyService.githubClientId;
var appendMixpanelId = function() {
if (mixpanel.get_distinct_id !== undefined) {
$scope.mixpanelDistinctIdClause = "&state=" + mixpanel.get_distinct_id();
} else {
// Mixpanel not yet loaded, try again later
$timeout(appendMixpanelId, 200);
}
};
appendMixpanelId();
$scope.signin = function() {
var signinPost = Restangular.one('signin');
signinPost.customPOST($scope.user).then(function() {
$scope.needsEmailVerification = false;
$scope.invalidCredentials = false;
// Redirect to the landing page
UserService.load();
$location.path('/');
}, function(result) {
$scope.needsEmailVerification = result.data.needsEmailVerification;
$scope.invalidCredentials = result.data.invalidCredentials;
});
};
$scope.sendRecovery = function() {
var signinPost = Restangular.one('recovery');
signinPost.customPOST($scope.recovery).then(function() {
$scope.invalidEmail = false;
$scope.sent = true;
}, function(result) {
$scope.invalidEmail = true;
$scope.sent = false;
});
};
};
function PlansCtrl($scope, UserService, PlanService) {
$scope.plans = PlanService.planList();
@ -328,35 +381,38 @@ function RepoAdminCtrl($scope, Restangular, $routeParams, $rootScope) {
var namespace = $routeParams.namespace;
var name = $routeParams.name;
$('#userSearch').typeahead({
name: 'users',
remote: {
url: '/api/users/%QUERY',
filter: function(data) {
var datums = [];
for (var i = 0; i < data.users.length; ++i) {
var user = data.users[i];
datums.push({
'value': user,
'tokens': [user],
'username': user
});
$scope.$on('$viewContentLoaded', function() {
// THIS IS BAD, MOVE THIS TO A DIRECTIVE
$('#userSearch').typeahead({
name: 'users',
remote: {
url: '/api/users/%QUERY',
filter: function(data) {
var datums = [];
for (var i = 0; i < data.users.length; ++i) {
var user = data.users[i];
datums.push({
'value': user,
'tokens': [user],
'username': user
});
}
return datums;
}
return datums;
}
},
template: function (datum) {
template = '<div class="user-mini-listing">';
template += '<i class="icon-user icon-large"></i>'
template += '<span class="name">' + datum.username + '</span>'
template += '</div>'
return template;
},
});
},
template: function (datum) {
template = '<div class="user-mini-listing">';
template += '<i class="icon-user icon-large"></i>'
template += '<span class="name">' + datum.username + '</span>'
template += '</div>'
return template;
},
});
$('#userSearch').on('typeahead:selected', function(e, datum) {
$('#userSearch').typeahead('setQuery', '');
$scope.addNewPermission(datum.username);
$('#userSearch').on('typeahead:selected', function(e, datum) {
$('#userSearch').typeahead('setQuery', '');
$scope.addNewPermission(datum.username);
});
});
$scope.addNewPermission = function(username) {

View file

@ -0,0 +1,53 @@
<!-- Quay -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
<img src="/static/img/quay-logo.png">
</a>
</div>
<!-- Collapsable stuff -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav">
<li><a ng-href="/repository/">Repositories</a></li>
<li><a ng-href="/guide/">Getting Started</a></li>
<li><a ng-href="/plans/">Plans &amp; Pricing</a></li>
</ul>
<ul class="nav navbar-nav navbar-right" ng-switch on="user.anonymous">
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input id="repoSearch" type="text" class="form-control" placeholder="Find Repo">
</div>
</form>
<li class="dropdown" ng-switch-when="false">
<!--<button type="button" class="btn btn-default navbar-btn">Sign in</button>-->
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown" data-toggle="dropdown">
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=32&d=identicon" />
{{ user.username }}
<span class="badge" ng-show="user.askForPassword">1</span>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>
<a href="/user/">
Account Settings
<span class="badge" ng-show="user.askForPassword">1</span>
</a>
</li>
<li><a href="javascript:void(0)" ng-click="signout()">Sign out</a></li>
</ul>
</li>
<li ng-switch-default>
<a href="/signin/">Sign in</a>
</li>
</ul>
</div><!-- /.navbar-collapse -->

View file

@ -16,7 +16,7 @@
<h2>Your Top Repositories</h2>
<div class="repo-listing" ng-repeat="repository in myrepos">
<i class="icon-hdd icon-large"></i>
<a ng-href="/repository/{{repository.namespace}}/{{ repository.name }}">{{repository.namespace}}/{{repository.name}}</a>
<a ng-href="/repository/{{ repository.namespace }}/{{ repository.name }}">{{repository.namespace}}/{{repository.name}}</a>
<div class="description" ng-bind-html-unsafe="getCommentFirstLine(repository.description)"></div>
</div>
</div>

View file

@ -0,0 +1,72 @@
<div class="container signin-container">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<div class="panel-group" id="accordion">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" data-target="#collapseSignin">
Sign In
</a>
</h4>
</div>
<div id="collapseSignin" class="panel-collapse collapse in">
<div class="panel-body">
<form class="form-signin" ng-submit="signin();">
<input type="text" class="form-control input-lg" placeholder="Username" ng-model="user.username" autofocus>
<input type="password" class="form-control input-lg" placeholder="Password" ng-model="user.password">
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign In</button>
<span class="social-alternate">
<i class="icon-circle"></i>
<span class="inner-text">OR</span>
</span>
<a id='github-signin-link' href="https://github.com/login/oauth/authorize?client_id={{ githubClientId }}&scope=user:email{{ mixpanelDistinctIdClause }}" class="btn btn-primary btn-lg btn-block"><i class="icon-github icon-large"></i> Sign In with GitHub</a>
</form>
<div class="alert alert-danger" ng-show="invalidCredentials">Invalid username or password.</div>
<div class="alert alert-danger" ng-show="needsEmailVerification">You must verify your email address before you can sign in.</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h6 class="panel-title">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" data-target="#collapseForgot">
Forgot Password
</a>
</h4>
</div>
<div id="collapseForgot" class="panel-collapse collapse out">
<div class="panel-body">
<form class="form-signin" ng-submit="sendRecovery();">
<input type="text" class="form-control input-lg" placeholder="Email" ng-model="recovery.email">
<button class="btn btn-lg btn-primary btn-block" type="submit">Send Recovery Email</button>
</form>
<div class="alert alert-danger" ng-show="invalidEmail">Unable to locate account.</div>
<div class="alert alert-success" ng-show="sent">Account recovery email was sent.</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- <script type="text/javascript">
function appendMixpanelId() {
if (mixpanel.get_distinct_id !== undefined) {
var signinLink = document.getElementById("github-signin-link");
signinLink.href += ("&state=" + mixpanel.get_distinct_id());
} else {
// Mixpanel not yet loaded, try again later
window.setTimeout(appendMixpanelId, 200);
}
};
appendMixpanelId();
</script> -->

View file

@ -1,12 +1,23 @@
<!DOCTYPE html>
<html>
<html ng-app="quay">
<head>
<title>Sign In - Quay.io</title>
{% block title %}
{% endblock %}
{% block added_meta %}
{% endblock %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Hosted private docker repositories. Includes full user management and history. Free for public repositories.">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.no-icons.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.no-icons.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css">
<link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="static/css/signin.css">
<link rel="stylesheet" href="/static/css/quay.css">
<!-- Icons -->
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" />
@ -21,54 +32,48 @@
<link rel="apple-touch-icon" sizes="152x152" href="/static/img/apple-touch-icon-152x152.png" />
<!-- /Icons -->
<!-- start Mixpanel --><script type="text/javascript">
{% block added_stylesheets %}
{% endblock %}
<script src="//code.jquery.com/jquery.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
<script src="//cdn.jsdelivr.net/underscorejs/1.5.2/underscore-min.js"></script>
<script src="//cdn.jsdelivr.net/restangular/1.1.3/restangular.js"></script>
<script src="static/lib/angulartics.js"></script>
<script src="static/lib/angulartics-mixpanel.js"></script>
<script src="static/lib/angular-moment.min.js"></script>
<script src="static/lib/typeahead.min.js"></script>
{% block added_dependencies %}
{% endblock %}
<script src="static/js/app.js"></script>
<script src="static/js/controllers.js"></script>
<script src="static/js/graphing.js"></script>
<!-- start Mixpanel --><script type="text/javascript">
var isProd = document.location.hostname === 'quay.io';
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f);b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==
typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");for(g=0;g<i.length;g++)f(c,i[g]);
b._i.push([a,e,d])};b.__SV=1.2}})(document,window.mixpanel||[]);
mixpanel.init(isProd ? "50ff2b2569faa3a51c8f5724922ffb7e" : "38014a0f27e7bdc3ff8cc7cc29c869f9", { track_pageview : false });</script><!-- end Mixpanel -->
mixpanel.init(isProd ? "50ff2b2569faa3a51c8f5724922ffb7e" : "38014a0f27e7bdc3ff8cc7cc29c869f9", { track_pageview : false, debug: !isProd });</script><!-- end Mixpanel -->
</head>
<body>
<div class="container signin-container">
<img src="/static/img/quay-logo.png">
<!-- Nav bar -->
<nav class="navbar navbar-default" role="navigation" ng-include="'/static/partials/header.html'" ng-controller='HeaderCtrl' >
</nav>
<form method="post" class="form-signin">
<input type="text" class="form-control" placeholder="Username" name="username" value="{{ username }}"autofocus>
<input type="password" class="form-control" placeholder="Password" name="password">
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign In</button>
{% block body_content %}
<span class="social-alternate">
<i class="icon-circle"></i>
<span class="inner-text">OR</span>
</span>
<a id='github-signin-link' href="https://github.com/login/oauth/authorize?client_id={{ github_client_id }}&scope=user:email" class="btn btn-primary btn-lg btn-block"><i class="icon-github icon-large"></i> Sign In with GitHub</a>
</form>
{% if invalid_credentials %}
<div class="alert alert-danger">Invalid username or password.</div>
{% endif %}
{% if needs_email_verification %}
<div class="alert alert-danger">You must verify your email address before you can sign in.</div>
{% endif %}
</div>
<script type="text/javascript">
function appendMixpanelId() {
if (mixpanel.get_distinct_id !== undefined) {
var signinLink = document.getElementById("github-signin-link");
signinLink.href += ("&state=" + mixpanel.get_distinct_id());
} else {
// Mixpanel not yet loaded, try again later
window.setTimeout(appendMixpanelId, 200);
}
};
appendMixpanelId();
</script>
{% endblock %}
</body>
</html>
</html>

View file

@ -1,42 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Error Logging in with GitHub · Quay.io</title>
{% extends "base.html" %}
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.no-icons.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.min.css">
{% block title %}
<title>Error Logging in with GitHub · Quay.io</title>
{% endblock %}
<link rel="stylesheet" href="static/css/signin.css">
{% block body_content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>There was an error logging in with GitHub.</h2>
<!-- Icons -->
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="/static/img/apple-touch-icon.png" />
<link rel="apple-touch-icon" sizes="57x57" href="/static/img/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/static/img/apple-touch-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/static/img/apple-touch-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/static/img/apple-touch-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/static/img/apple-touch-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/static/img/apple-touch-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/static/img/apple-touch-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/static/img/apple-touch-icon-152x152.png" />
<!-- /Icons -->
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>There was an error logging in with GitHub.</h2>
{% if error_message %}
<div class="alert alert-danger">{{ error_message }}</div>
{% endif %}
{% if error_message %}
<div class="alert alert-danger">{{ error_message }}</div>
{% endif %}
<div>
Please register using the <a href="/">registration form</a> to continue.
</div>
<div>
Please register using the <a href="/">registration form</a> to continue.
</div>
</div>
</div>
</body>
</html>
</div>
{% endblock %}

View file

@ -1,132 +1,37 @@
<!DOCTYPE html>
<html ng-app="quay">
<head>
<title ng-bind="title + ' · Quay.io'">Quay - Private Docker Repository</title>
<base href="/">
{% extends "base.html" %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Hosted private docker repositories. Includes full user management and history. Free for public repositories.">
<meta name="google-site-verification" content="GalDznToijTsHYmLjJvE4QaB9uk_IP16aaGDz5D75T4" />
<meta name="fragment" content="!" />
{% block title %}
<title ng-bind="title + ' · Quay.io'">Quay - Private Docker Repository</title>
{% endblock %}
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.no-icons.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css">
<link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/static/lib/browser-chrome.css">
{% block added_meta %}
<base href="/">
<link rel="stylesheet" href="/static/css/quay.css">
<meta name="google-site-verification" content="GalDznToijTsHYmLjJvE4QaB9uk_IP16aaGDz5D75T4" />
<meta name="fragment" content="!" />
{% endblock %}
<!-- Icons -->
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="/static/img/apple-touch-icon.png" />
<link rel="apple-touch-icon" sizes="57x57" href="/static/img/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/static/img/apple-touch-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/static/img/apple-touch-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/static/img/apple-touch-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/static/img/apple-touch-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/static/img/apple-touch-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/static/img/apple-touch-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/static/img/apple-touch-icon-152x152.png" />
<!-- /Icons -->
{% block added_stylesheets %}
<link rel="stylesheet" href="/static/lib/browser-chrome.css">
{% endblock %}
<script src="//code.jquery.com/jquery.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="https://checkout.stripe.com/v2/checkout.js"></script>
{% block added_dependencies %}
<script src="https://checkout.stripe.com/v2/checkout.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
<script src="//cdn.jsdelivr.net/underscorejs/1.5.2/underscore-min.js"></script>
<script src="//cdn.jsdelivr.net/restangular/1.1.3/restangular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.2.1/moment.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.3.3/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/zeroclipboard/1.1.7/ZeroClipboard.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.2.1/moment.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.3.3/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/zeroclipboard/1.1.7/ZeroClipboard.min.js"></script>
<script src="static/lib/jquery.overscroll.min.js"></script>
<script src="static/lib/angulartics.js"></script>
<script src="static/lib/angulartics-mixpanel.js"></script>
<script src="static/lib/jquery.overscroll.min.js"></script>
<script src="static/lib/pagedown/Markdown.Converter.js"></script>
<script src="static/lib/pagedown/Markdown.Editor.js"></script>
<script src="static/lib/pagedown/Markdown.Sanitizer.js"></script>
<script src="static/lib/angular-moment.min.js"></script>
<script src="static/lib/pagedown/Markdown.Converter.js"></script>
<script src="static/lib/pagedown/Markdown.Editor.js"></script>
<script src="static/lib/pagedown/Markdown.Sanitizer.js"></script>
<script src="static/lib/d3-tip.js" charset="utf-8"></script>
<script src="static/lib/browser-chrome.js"></script>
{% endblock %}
<script src="static/lib/typeahead.min.js"></script>
<script src="static/lib/d3-tip.js" charset="utf-8"></script>
<script src="static/lib/browser-chrome.js"></script>
<script src="static/js/app.js"></script>
<script src="static/js/controllers.js"></script>
<script src="static/js/graphing.js"></script>
<!-- start Mixpanel --><script type="text/javascript">
var isProd = document.location.hostname === 'quay.io';
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f);b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==
typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");for(g=0;g<i.length;g++)f(c,i[g]);
b._i.push([a,e,d])};b.__SV=1.2}})(document,window.mixpanel||[]);
mixpanel.init(isProd ? "50ff2b2569faa3a51c8f5724922ffb7e" : "38014a0f27e7bdc3ff8cc7cc29c869f9", { track_pageview : false, debug: !isProd });</script><!-- end Mixpanel -->
</head>
<body>
<!-- Nav bar -->
<nav class="navbar navbar-default" role="navigation" ng-controller='HeaderCtrl'>
<!-- Quay -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
<img src="/static/img/quay-logo.png">
</a>
</div>
<!-- Collapsable stuff -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav">
<li><a ng-href="/repository/">Repositories</a></li>
<li><a ng-href="/guide/">Getting Started</a></li>
<li><a ng-href="/plans/">Plans &amp; Pricing</a></li>
</ul>
<ul class="nav navbar-nav navbar-right" ng-switch on="user.anonymous">
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input id="repoSearch" type="text" class="form-control" placeholder="Find Repo">
</div>
</form>
<li class="dropdown" ng-switch-when="false">
<!--<button type="button" class="btn btn-default navbar-btn">Sign in</button>-->
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown" data-toggle="dropdown">
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=32&d=identicon" />
{{ user.username }}
<span class="badge" ng-show="user.askForPassword">1</span>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>
<a href="/user/">
Account Settings
<span class="badge" ng-show="user.askForPassword">1</span>
</a>
</li>
<li><a href="/signout" target="_self">Sign out</a></li>
</ul>
</li>
<li ng-switch-default>
<a href="/signin" target="_self">Sign in</a>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<div ng-view></div>
</body>
</html>
{% block body_content %}
<div ng-view></div>
{% endblock %}

View file

@ -1,119 +1,85 @@
<!DOCTYPE html>
<html>
<head>
<title>Privacy Policy · Quay.io</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% extends "base.html" %}
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.no-icons.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="/static/css/quay.css">
{% block title %}
<title>Privacy Policy · Quay.io</title>
{% endblock %}
<!-- Icons -->
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="/static/img/apple-touch-icon.png" />
<link rel="apple-touch-icon" sizes="57x57" href="/static/img/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/static/img/apple-touch-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/static/img/apple-touch-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/static/img/apple-touch-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/static/img/apple-touch-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/static/img/apple-touch-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/static/img/apple-touch-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/static/img/apple-touch-icon-152x152.png" />
<!-- /Icons -->
</head>
<body>
{% block body_content %}
<div class="container privacy-policy">
<nav class="navbar navbar-default" role="navigation" ng-controller='HeaderCtrl'>
<!-- Quay -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
<img src="/static/img/quay-logo.png">
</a>
</div>
</nav>
<h2>Privacy Policy</h2>
<div class="container privacy-policy">
<dl>
<dt>What information do we collect?</dt>
<dd>
We collect information from you when you register on our site or subscribe to the service..
When ordering or registering on our site, as appropriate, you may be asked to enter your: e-mail address, mailing address or credit card information. You may, however, visit the public portion of our site anonymously.
</dd>
<h2>Privacy Policy</h2>
<dl>
<dt>What information do we collect?</dt>
<dd>
We collect information from you when you register on our site or subscribe to the service..
When ordering or registering on our site, as appropriate, you may be asked to enter your: e-mail address, mailing address or credit card information. You may, however, visit the public portion of our site anonymously.
<dt>What do we use your information for?</dt>
<dd>Any of the information we collect from you may be used in one of the following ways:
<ul>
<li>To personalize your experience(your information helps us to better respond to your individual needs)</li>
<li>
To improve our website<br>
(we continually strive to improve our website offerings based on the information and feedback we receive from you)</li>
<li>
To improve customer service<br>
(your information helps us to more effectively respond to your customer service requests and support needs)
</li>
<li>
To process transactions<br>
Your information, whether public or private, will not be sold, exchanged, transferred, or given to any other company for any reason whatsoever, without your consent, other than for the express purpose of delivering the purchased product or service requested.
</li>
<li>
To send periodic emails<br>
The email address you provide for order processing, may be used to send you information and updates pertaining to your order, in addition to receiving occasional company news, updates, related product or service information, etc.<br>
Note: If at any time you would like to unsubscribe from receiving future emails, we include detailed unsubscribe instructions at the bottom of each email.
</li>
</ul>
</dd>
<dt>What do we use your information for?</dt>
<dd>Any of the information we collect from you may be used in one of the following ways:
<ul>
<li>To personalize your experience(your information helps us to better respond to your individual needs)</li>
<li>
To improve our website<br>
(we continually strive to improve our website offerings based on the information and feedback we receive from you)</li>
<li>
To improve customer service<br>
(your information helps us to more effectively respond to your customer service requests and support needs)
</li>
<li>
To process transactions<br>
Your information, whether public or private, will not be sold, exchanged, transferred, or given to any other company for any reason whatsoever, without your consent, other than for the express purpose of delivering the purchased product or service requested.
</li>
<li>
To send periodic emails<br>
The email address you provide for order processing, may be used to send you information and updates pertaining to your order, in addition to receiving occasional company news, updates, related product or service information, etc.<br>
Note: If at any time you would like to unsubscribe from receiving future emails, we include detailed unsubscribe instructions at the bottom of each email.
</li>
</ul>
</dd>
<dt>How do we protect your information?</dt>
<dd>
We implement a variety of security measures to maintain the safety of your personal information when you place an order or enter, submit, or access your personal information.
We offer the use of a secure server. All supplied sensitive/credit information is transmitted via Secure Socket Layer (SSL) technology and then encrypted into our Payment gateway providers database only to be accessible by those authorized with special access rights to such systems, and are required to keep the information confidential.
After a transaction, your private information (credit cards, social security numbers, financials, etc.) will be kept on file for more than 60 days in order to continue subscription billing..
</dd>
<dt>Do we use cookies?</dt>
<dd>
Yes (Cookies are small files that a site or its service provider transfers to your computers hard drive through your Web browser (if you allow) that enables the sites or service providers systems to recognize your browser and capture and remember certain information
We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.
</dd>
<dt>Do we disclose any information to outside parties?</dt>
<dd>
We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our website, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.
</dd>
<dt>Third party links</dt>
<dd>
Occasionally, at our discretion, we may include or offer third party products or services on our website. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.
</dd>
<dt>California Online Privacy Protection Act Compliance</dt>
<dd>
Because we value your privacy we have taken the necessary precautions to be in compliance with the California Online Privacy Protection Act. We therefore will not distribute your personal information to outside parties without your consent.
As part of the California Online Privacy Protection Act, all users of our site may make any changes to their information at anytime by logging into the service and modifying their Account Settings and Payment Information.
</dd>
<dt>Childrens Online Privacy Protection Act Compliance</dt>
<dd>
We are in compliance with the requirements of COPPA (Childrens Online Privacy Protection Act), we do not collect any information from anyone under 13 years of age. Our website, products and services are all directed to people who are at least 13 years old or older.
</dd>
<dt>Terms and Conditions </dt>
<dd>
Please also visit our Terms and Conditions section establishing the use, disclaimers, and limitations of liability governing the use of our website at https://quay.io/tos
</dd>
<dt>Your Consent</dt>
<dd>
By using our site, you consent to our privacy policy.
</dd>
<dt>Changes to our Privacy Policy</dt>
<dd>
If we decide to change our privacy policy, we will post those changes on this page.
If you have any questions or concerns about our privacy policy, please direct them to the following email address:
<a href="mailto:support@quay.io">support@quay.io</a>
</dd>
</div>
</body>
</html>
<dt>How do we protect your information?</dt>
<dd>
We implement a variety of security measures to maintain the safety of your personal information when you place an order or enter, submit, or access your personal information.
We offer the use of a secure server. All supplied sensitive/credit information is transmitted via Secure Socket Layer (SSL) technology and then encrypted into our Payment gateway providers database only to be accessible by those authorized with special access rights to such systems, and are required to keep the information confidential.
After a transaction, your private information (credit cards, social security numbers, financials, etc.) will be kept on file for more than 60 days in order to continue subscription billing..
</dd>
<dt>Do we use cookies?</dt>
<dd>
Yes (Cookies are small files that a site or its service provider transfers to your computers hard drive through your Web browser (if you allow) that enables the sites or service providers systems to recognize your browser and capture and remember certain information
We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.
</dd>
<dt>Do we disclose any information to outside parties?</dt>
<dd>
We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our website, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.
</dd>
<dt>Third party links</dt>
<dd>
Occasionally, at our discretion, we may include or offer third party products or services on our website. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.
</dd>
<dt>California Online Privacy Protection Act Compliance</dt>
<dd>
Because we value your privacy we have taken the necessary precautions to be in compliance with the California Online Privacy Protection Act. We therefore will not distribute your personal information to outside parties without your consent.
As part of the California Online Privacy Protection Act, all users of our site may make any changes to their information at anytime by logging into the service and modifying their Account Settings and Payment Information.
</dd>
<dt>Childrens Online Privacy Protection Act Compliance</dt>
<dd>
We are in compliance with the requirements of COPPA (Childrens Online Privacy Protection Act), we do not collect any information from anyone under 13 years of age. Our website, products and services are all directed to people who are at least 13 years old or older.
</dd>
<dt>Terms and Conditions </dt>
<dd>
Please also visit our Terms and Conditions section establishing the use, disclaimers, and limitations of liability governing the use of our website at https://quay.io/tos
</dd>
<dt>Your Consent</dt>
<dd>
By using our site, you consent to our privacy policy.
</dd>
<dt>Changes to our Privacy Policy</dt>
<dd>
If we decide to change our privacy policy, we will post those changes on this page.
If you have any questions or concerns about our privacy policy, please direct them to the following email address:
<a href="mailto:support@quay.io">support@quay.io</a>
</dd>
</div>
{% endblock %}

View file

@ -1,132 +1,98 @@
<!DOCTYPE html>
<html>
<head>
<title>Terms of Service · Quay.io</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% extends "base.html" %}
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.no-icons.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="/static/css/quay.css">
{% block title %}
<title>Terms of Service · Quay.io</title>
{% endblock %}
<!-- Icons -->
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="/static/img/apple-touch-icon.png" />
<link rel="apple-touch-icon" sizes="57x57" href="/static/img/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/static/img/apple-touch-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/static/img/apple-touch-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/static/img/apple-touch-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/static/img/apple-touch-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/static/img/apple-touch-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/static/img/apple-touch-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/static/img/apple-touch-icon-152x152.png" />
<!-- /Icons -->
</head>
<body>
<nav class="navbar navbar-default" role="navigation" ng-controller='HeaderCtrl'>
<!-- Quay -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
<img src="/static/img/quay-logo.png">
</a>
{% block body_content %}
<div class="tos container">
<h2>Terms of Service</h2>
<p>The following terms and conditions govern all use of the Quay.io website and all content, services and products available at or through the website. The Website is owned and operated by DevTable, LLC. (“DevTable”). The Website is offered subject to your acceptance without modification of all of the terms and conditions contained herein and all other operating rules, policies (including, without limitation, Quay.ios Privacy Policy) and procedures that may be published from time to time on this Site by DevTable (collectively, the “Agreement”).</p>
<p>Please read this Agreement carefully before accessing or using the Website. By accessing or using any part of the web site, you agree to become bound by the terms and conditions of this agreement. If you do not agree to all the terms and conditions of this agreement, then you may not access the Website or use any services. If these terms and conditions are considered an offer by DevTable, acceptance is expressly limited to these terms. The Website is available only to individuals who are at least 13 years old.</p>
<ol>
<li>
<strong>Your Quay.io Account.</strong> If you create an account on the Website, you are responsible for maintaining the security of your account, and you are fully responsible for all activities that occur under the account and any other actions taken in connection with the account. You must immediately notify DevTable of any unauthorized uses of your account or any other breaches of security. DevTable will not be liable for any acts or omissions by You, including any damages of any kind incurred as a result of such acts or omissions.
</li>
<li>
<strong>Responsibility of Contributors.</strong> If you share your repository, publish images, code or content, or otherwise make (or allow any third party to make) material available by means of the Website (any such material, “Content”), You are entirely responsible for the content of, and any harm resulting from, that Content. That is the case regardless of whether the Content in question constitutes text, graphics, an audio file, or computer software. By making Content available, you represent and warrant that:
<ul>
<li>
the downloading, copying and use of the Content will not infringe the proprietary rights, including but not limited to the copyright, patent, trademark or trade secret rights, of any third party;
</li>
<li>
if your employer has rights to intellectual property you create, you have either (i) received permission from your employer to post or make available the Content, including but not limited to any software, or (ii) secured from your employer a waiver as to all rights in or to the Content;
</li>
<li>
you have fully complied with any third-party licenses relating to the Content, and have done all things necessary to successfully pass through to end users any required terms;
</li>
<li>
the Content does not contain or install any viruses, worms, malware, Trojan horses or other harmful or destructive content;
</li>
<li>
the Content is not spam, is not randomly-generated, and does not contain unethical or unwanted commercial content designed to drive traffic to third party sites or boost the search engine rankings of third party sites, or to further unlawful acts (such as phishing) or mislead recipients as to the source of the material (such as spoofing);
</li>
<li>
the Content does not contain threats or incite violence, and does not violate the privacy or publicity rights of any third party;
</li>
<li>
your Content is not getting advertised via unwanted electronic messages such as spam links on newsgroups, email lists, other blogs and web sites, and similar unsolicited promotional methods;
</li>
<li>
your Content is not named in a manner that misleads your readers into thinking that you are another person or company. For example, your Contents URL or name is not the name of a person other than yourself or company other than your own; and
</li>
<li>
you have, in the case of Content that includes computer code, accurately categorized and/or described the type, nature, uses and effects of the materials, whether requested to do so by DevTable or otherwise.
</li>
</ul>
</li>
<li>
By submitting Content or computer code to DevTable for inclusion in your Repositories, you grant DevTable a world-wide, royalty-free, and non-exclusive license to reproduce, modify, adapt and publish the Content solely for the purpose of providing the services you request. If you delete Content, DevTable will use reasonable efforts to remove it from the Service, but you acknowledge that caching or references to the Content may not be made immediately unavailable.
</li>
<li>
Without limiting any of those representations or warranties, DevTable has the right (though not the obligation) to, in DevTables sole discretion (i) refuse or remove any content that, in DevTables reasonable opinion, violates any DevTable policy or is in any way harmful or objectionable, or (ii) terminate or deny access to and use of the Website to any individual or entity for any reason, in DevTables sole discretion. DevTable will have no obligation to provide a refund of any amounts previously paid.
</li>
<li>
<strong>Payment and Renewal.</strong>
<dl>
<dt>General Terms.</dt>
<dd>Paid services beyond the initial trial are available on the Website (any such services, an “Account”). By maintaining an Account you agree to pay DevTable the monthly or annual subscription fees indicated for that service. Payments will be charged on a pre-pay basis on the day you sign up for a plan and will cover the use of that service for a monthly or annual subscription period as indicated. Account fees are not refundable.</dd>
<dt>Automatic Renewal.</dt>
<dd>Unless you notify DevTable before the end of the applicable subscription period that you want to cancel an Account, your Account subscription will automatically renew and you authorize us to collect the then-applicable annual or monthly subscription fee for such Account (as well as any taxes) using any credit card or other payment mechanism we have on record for you. Accounts can be canceled at any time in the Payment Information section of your User Settings.</dd>
</dl>
</li>
<li>
<strong>Responsibility of Website Visitors.</strong> DevTable has not reviewed, and cannot review, all of the material, including computer software, submitted to the Service, and cannot therefore be responsible for that materials content, use or effects. By operating the Website, DevTable does not represent or imply that it endorses the material there posted, or that it believes such material to be accurate, useful or non-harmful. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. The Website may contain content that is offensive, indecent, or otherwise objectionable, as well as content containing technical inaccuracies, typographical mistakes, and other errors. The Website may also contain material that violates the privacy or publicity rights, or infringes the intellectual property and other proprietary rights, of third parties, or the downloading, copying or use of which is subject to additional terms and conditions, stated or unstated. DevTable disclaims any responsibility for any harm resulting from the use by visitors of the Website, or from any downloading by those visitors of content there posted. </li>
</li>
<li>
<strong>Content Posted on Other Websites.</strong> We have not reviewed, and cannot review, all of the material, including computer software, made available through the websites and webpages to which Quay.io links, and that link to Quay.io. DevTable does not have any control over those non-DevTable websites and webpages, and is not responsible for their contents or their use. By linking to a non-DevTable website or webpage, DevTable does not represent or imply that it endorses such website or webpage. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. DevTable disclaims any responsibility for any harm resulting from your use of non-DevTable websites and webpages. </li>
</li>
<li>
<strong>Copyright Infringement and DMCA Policy.</strong> As DevTable asks others to respect its intellectual property rights, it respects the intellectual property rights of others. If you believe that material located on or linked to by Quay.io violates your copyright, you are encouraged to notify DevTable in accordance with the provisions of the Digital Millennium Copyright Act (“DMCA”). DevTable will respond to all such notices, including as required or appropriate by removing the infringing material or disabling all links to the infringing material. DevTable will terminate a visitors access to and use of the Website if, under appropriate circumstances, the visitor is determined to be a repeat infringer of the copyrights or other intellectual property rights of DevTable or others. In the case of such termination, DevTable will have no obligation to provide a refund of any amounts previously paid to DevTable. </li>
</li>
<li>
<strong>Intellectual Property.</strong> This Agreement does not transfer from DevTable to you any DevTable or third party intellectual property, and all right, title and interest in and to such property will remain (as between the parties) solely with DevTable. DevTable, Quay.io, the Quay.io logo, and all other trademarks, service marks, graphics and logos used in connection with Quay.io, or the Website are trademarks or registered trademarks of DevTable or DevTables licensors. Other trademarks, service marks, graphics and logos used in connection with the Website may be the trademarks of other third parties. Your use of the Website grants you no right or license to reproduce or otherwise use any DevTable or third-party trademarks.
</li>
<li>
<strong>Changes.</strong> DevTable reserves the right, at its sole discretion, to modify or replace any part of this Agreement. It is your responsibility to check this Agreement periodically for changes. Your continued use of or access to the Website following the posting of any changes to this Agreement constitutes acceptance of those changes. DevTable may also, in the future, offer new services and/or features through the Website (including, the release of new tools and resources). Such new features and/or services shall be subject to the terms and conditions of this Agreement.
</li>
<li>
<strong>Termination.</strong> DevTable may terminate your access to all or any part of the Website at any time, with or without cause, with or without notice, effective immediately. If you wish to terminate this Agreement or your Quay.io account (if you have one), you may simply discontinue using the Website. All provisions of this Agreement which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity and limitations of liability.
</li>
<li>
<strong>Disclaimer of Warranties.</strong> The Website is provided “as is”. DevTable and its suppliers and licensors hereby disclaim all warranties of any kind, express or implied, including, without limitation, the warranties of merchantability, fitness for a particular purpose and non-infringement. Neither DevTable nor its suppliers and licensors, makes any warranty that the Website will be error free or that access thereto will be continuous or uninterrupted. You understand that you download from, or otherwise obtain content or services through, the Website at your own discretion and risk.
</li>
<li>
<strong>Limitation of Liability.</strong> In no event will DevTable, or its suppliers or licensors, be liable with respect to any subject matter of this agreement under any contract, negligence, strict liability or other legal or equitable theory for: (i) any special, incidental or consequential damages; (ii) the cost of procurement for substitute products or services; (iii) for interruption of use or loss or corruption of data; or (iv) for any amounts that exceed the fees paid by you to DevTable under this agreement during the twelve (12) month period prior to the cause of action. DevTable shall have no liability for any failure or delay due to matters beyond their reasonable control. The foregoing shall not apply to the extent prohibited by applicable law.
</li>
<li>
<strong>General Representation and Warranty.</strong> You represent and warrant that (i) your use of the Website will be in strict accordance with the Quay.io Privacy Policy, with this Agreement and with all applicable laws and regulations (including without limitation any local laws or regulations in your country, state, city, or other governmental area, regarding online conduct and acceptable content, and including all applicable laws regarding the transmission of technical data exported from the United States or the country in which you reside) and (ii) your use of the Website will not infringe or misappropriate the intellectual property rights of any third party.
</li>
<li>
<strong>Indemnification.</strong> You agree to indemnify and hold harmless DevTable, its contractors, and its licensors, and their respective directors, officers, employees and agents from and against any and all claims and expenses, including attorneys fees, arising out of your use of the Website, including but not limited to your violation of this Agreement.
</li>
<li>
<strong>Miscellaneous.</strong> This Agreement constitutes the entire agreement between DevTable and you concerning the subject matter hereof, and they may only be modified by a written amendment signed by an authorized executive of DevTable, or by the posting by DevTable of a revised version. Except to the extent applicable law, if any, provides otherwise, this Agreement, any access to or use of the Website will be governed by the laws of the state of New York, U.S.A., excluding its conflict of law provisions, and the proper venue for any disputes arising out of or relating to any of the same will be the state and federal courts located in New York County, New York. The prevailing party in any action or proceeding to enforce this Agreement shall be entitled to costs and attorneys fees. If any part of this Agreement is held invalid or unenforceable, that part will be construed to reflect the parties original intent, and the remaining portions will remain in full force and effect. A waiver by either party of any term or condition of this Agreement or any breach thereof, in any one instance, will not waive such term or condition or any subsequent breach thereof. You may assign your rights under this Agreement to any party that consents to, and agrees to be bound by, its terms and conditions; DevTable may assign its rights under this Agreement without condition. This Agreement will be binding upon and will inure to the benefit of the parties, their successors and permitted assigns.
</li>
</ol>
</div>
</nav>
<div class="tos container">
<h2>Terms of Service</h2>
<p>The following terms and conditions govern all use of the Quay.io website and all content, services and products available at or through the website. The Website is owned and operated by DevTable, LLC. (“DevTable”). The Website is offered subject to your acceptance without modification of all of the terms and conditions contained herein and all other operating rules, policies (including, without limitation, Quay.ios Privacy Policy) and procedures that may be published from time to time on this Site by DevTable (collectively, the “Agreement”).</p>
<p>Please read this Agreement carefully before accessing or using the Website. By accessing or using any part of the web site, you agree to become bound by the terms and conditions of this agreement. If you do not agree to all the terms and conditions of this agreement, then you may not access the Website or use any services. If these terms and conditions are considered an offer by DevTable, acceptance is expressly limited to these terms. The Website is available only to individuals who are at least 13 years old.</p>
<ol>
<li>
<strong>Your Quay.io Account.</strong> If you create an account on the Website, you are responsible for maintaining the security of your account, and you are fully responsible for all activities that occur under the account and any other actions taken in connection with the account. You must immediately notify DevTable of any unauthorized uses of your account or any other breaches of security. DevTable will not be liable for any acts or omissions by You, including any damages of any kind incurred as a result of such acts or omissions.
</li>
<li>
<strong>Responsibility of Contributors.</strong> If you share your repository, publish images, code or content, or otherwise make (or allow any third party to make) material available by means of the Website (any such material, “Content”), You are entirely responsible for the content of, and any harm resulting from, that Content. That is the case regardless of whether the Content in question constitutes text, graphics, an audio file, or computer software. By making Content available, you represent and warrant that:
<ul>
<li>
the downloading, copying and use of the Content will not infringe the proprietary rights, including but not limited to the copyright, patent, trademark or trade secret rights, of any third party;
</li>
<li>
if your employer has rights to intellectual property you create, you have either (i) received permission from your employer to post or make available the Content, including but not limited to any software, or (ii) secured from your employer a waiver as to all rights in or to the Content;
</li>
<li>
you have fully complied with any third-party licenses relating to the Content, and have done all things necessary to successfully pass through to end users any required terms;
</li>
<li>
the Content does not contain or install any viruses, worms, malware, Trojan horses or other harmful or destructive content;
</li>
<li>
the Content is not spam, is not randomly-generated, and does not contain unethical or unwanted commercial content designed to drive traffic to third party sites or boost the search engine rankings of third party sites, or to further unlawful acts (such as phishing) or mislead recipients as to the source of the material (such as spoofing);
</li>
<li>
the Content does not contain threats or incite violence, and does not violate the privacy or publicity rights of any third party;
</li>
<li>
your Content is not getting advertised via unwanted electronic messages such as spam links on newsgroups, email lists, other blogs and web sites, and similar unsolicited promotional methods;
</li>
<li>
your Content is not named in a manner that misleads your readers into thinking that you are another person or company. For example, your Contents URL or name is not the name of a person other than yourself or company other than your own; and
</li>
<li>
you have, in the case of Content that includes computer code, accurately categorized and/or described the type, nature, uses and effects of the materials, whether requested to do so by DevTable or otherwise.
</li>
</ul>
</li>
<li>
By submitting Content or computer code to DevTable for inclusion in your Repositories, you grant DevTable a world-wide, royalty-free, and non-exclusive license to reproduce, modify, adapt and publish the Content solely for the purpose of providing the services you request. If you delete Content, DevTable will use reasonable efforts to remove it from the Service, but you acknowledge that caching or references to the Content may not be made immediately unavailable.
</li>
<li>
Without limiting any of those representations or warranties, DevTable has the right (though not the obligation) to, in DevTables sole discretion (i) refuse or remove any content that, in DevTables reasonable opinion, violates any DevTable policy or is in any way harmful or objectionable, or (ii) terminate or deny access to and use of the Website to any individual or entity for any reason, in DevTables sole discretion. DevTable will have no obligation to provide a refund of any amounts previously paid.
</li>
<li>
<strong>Payment and Renewal.</strong>
<dl>
<dt>General Terms.</dt>
<dd>Paid services beyond the initial trial are available on the Website (any such services, an “Account”). By maintaining an Account you agree to pay DevTable the monthly or annual subscription fees indicated for that service. Payments will be charged on a pre-pay basis on the day you sign up for a plan and will cover the use of that service for a monthly or annual subscription period as indicated. Account fees are not refundable.</dd>
<dt>Automatic Renewal.</dt>
<dd>Unless you notify DevTable before the end of the applicable subscription period that you want to cancel an Account, your Account subscription will automatically renew and you authorize us to collect the then-applicable annual or monthly subscription fee for such Account (as well as any taxes) using any credit card or other payment mechanism we have on record for you. Accounts can be canceled at any time in the Payment Information section of your User Settings.</dd>
</dl>
</li>
<li>
<strong>Responsibility of Website Visitors.</strong> DevTable has not reviewed, and cannot review, all of the material, including computer software, submitted to the Service, and cannot therefore be responsible for that materials content, use or effects. By operating the Website, DevTable does not represent or imply that it endorses the material there posted, or that it believes such material to be accurate, useful or non-harmful. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. The Website may contain content that is offensive, indecent, or otherwise objectionable, as well as content containing technical inaccuracies, typographical mistakes, and other errors. The Website may also contain material that violates the privacy or publicity rights, or infringes the intellectual property and other proprietary rights, of third parties, or the downloading, copying or use of which is subject to additional terms and conditions, stated or unstated. DevTable disclaims any responsibility for any harm resulting from the use by visitors of the Website, or from any downloading by those visitors of content there posted. </li>
</li>
<li>
<strong>Content Posted on Other Websites.</strong> We have not reviewed, and cannot review, all of the material, including computer software, made available through the websites and webpages to which Quay.io links, and that link to Quay.io. DevTable does not have any control over those non-DevTable websites and webpages, and is not responsible for their contents or their use. By linking to a non-DevTable website or webpage, DevTable does not represent or imply that it endorses such website or webpage. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. DevTable disclaims any responsibility for any harm resulting from your use of non-DevTable websites and webpages. </li>
</li>
<li>
<strong>Copyright Infringement and DMCA Policy.</strong> As DevTable asks others to respect its intellectual property rights, it respects the intellectual property rights of others. If you believe that material located on or linked to by Quay.io violates your copyright, you are encouraged to notify DevTable in accordance with the provisions of the Digital Millennium Copyright Act (“DMCA”). DevTable will respond to all such notices, including as required or appropriate by removing the infringing material or disabling all links to the infringing material. DevTable will terminate a visitors access to and use of the Website if, under appropriate circumstances, the visitor is determined to be a repeat infringer of the copyrights or other intellectual property rights of DevTable or others. In the case of such termination, DevTable will have no obligation to provide a refund of any amounts previously paid to DevTable. </li>
</li>
<li>
<strong>Intellectual Property.</strong> This Agreement does not transfer from DevTable to you any DevTable or third party intellectual property, and all right, title and interest in and to such property will remain (as between the parties) solely with DevTable. DevTable, Quay.io, the Quay.io logo, and all other trademarks, service marks, graphics and logos used in connection with Quay.io, or the Website are trademarks or registered trademarks of DevTable or DevTables licensors. Other trademarks, service marks, graphics and logos used in connection with the Website may be the trademarks of other third parties. Your use of the Website grants you no right or license to reproduce or otherwise use any DevTable or third-party trademarks.
</li>
<li>
<strong>Changes.</strong> DevTable reserves the right, at its sole discretion, to modify or replace any part of this Agreement. It is your responsibility to check this Agreement periodically for changes. Your continued use of or access to the Website following the posting of any changes to this Agreement constitutes acceptance of those changes. DevTable may also, in the future, offer new services and/or features through the Website (including, the release of new tools and resources). Such new features and/or services shall be subject to the terms and conditions of this Agreement.
</li>
<li>
<strong>Termination.</strong> DevTable may terminate your access to all or any part of the Website at any time, with or without cause, with or without notice, effective immediately. If you wish to terminate this Agreement or your Quay.io account (if you have one), you may simply discontinue using the Website. All provisions of this Agreement which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity and limitations of liability.
</li>
<li>
<strong>Disclaimer of Warranties.</strong> The Website is provided “as is”. DevTable and its suppliers and licensors hereby disclaim all warranties of any kind, express or implied, including, without limitation, the warranties of merchantability, fitness for a particular purpose and non-infringement. Neither DevTable nor its suppliers and licensors, makes any warranty that the Website will be error free or that access thereto will be continuous or uninterrupted. You understand that you download from, or otherwise obtain content or services through, the Website at your own discretion and risk.
</li>
<li>
<strong>Limitation of Liability.</strong> In no event will DevTable, or its suppliers or licensors, be liable with respect to any subject matter of this agreement under any contract, negligence, strict liability or other legal or equitable theory for: (i) any special, incidental or consequential damages; (ii) the cost of procurement for substitute products or services; (iii) for interruption of use or loss or corruption of data; or (iv) for any amounts that exceed the fees paid by you to DevTable under this agreement during the twelve (12) month period prior to the cause of action. DevTable shall have no liability for any failure or delay due to matters beyond their reasonable control. The foregoing shall not apply to the extent prohibited by applicable law.
</li>
<li>
<strong>General Representation and Warranty.</strong> You represent and warrant that (i) your use of the Website will be in strict accordance with the Quay.io Privacy Policy, with this Agreement and with all applicable laws and regulations (including without limitation any local laws or regulations in your country, state, city, or other governmental area, regarding online conduct and acceptable content, and including all applicable laws regarding the transmission of technical data exported from the United States or the country in which you reside) and (ii) your use of the Website will not infringe or misappropriate the intellectual property rights of any third party.
</li>
<li>
<strong>Indemnification.</strong> You agree to indemnify and hold harmless DevTable, its contractors, and its licensors, and their respective directors, officers, employees and agents from and against any and all claims and expenses, including attorneys fees, arising out of your use of the Website, including but not limited to your violation of this Agreement.
</li>
<li>
<strong>Miscellaneous.</strong> This Agreement constitutes the entire agreement between DevTable and you concerning the subject matter hereof, and they may only be modified by a written amendment signed by an authorized executive of DevTable, or by the posting by DevTable of a revised version. Except to the extent applicable law, if any, provides otherwise, this Agreement, any access to or use of the Website will be governed by the laws of the state of New York, U.S.A., excluding its conflict of law provisions, and the proper venue for any disputes arising out of or relating to any of the same will be the state and federal courts located in New York County, New York. The prevailing party in any action or proceeding to enforce this Agreement shall be entitled to costs and attorneys fees. If any part of this Agreement is held invalid or unenforceable, that part will be construed to reflect the parties original intent, and the remaining portions will remain in full force and effect. A waiver by either party of any term or condition of this Agreement or any breach thereof, in any one instance, will not waive such term or condition or any subsequent breach thereof. You may assign your rights under this Agreement to any party that consents to, and agrees to be bound by, its terms and conditions; DevTable may assign its rights under this Agreement without condition. This Agreement will be binding upon and will inure to the benefit of the parties, their successors and permitted assigns.
</li>
</ol>
</div>
</body>
</html>
{% endblock %}

View file

@ -5,16 +5,37 @@ from app import mail, app
CONFIRM_MESSAGE = """
This email address was recently used to register the username '%s'
at <a href="https://quay.io">Quay</a>.<br>
at <a href="https://quay.io">Quay.io</a>.<br>
<br>
To confirm this email address, please click the following link:<br>
<a href="https://quay.io/confirm?code=%s">https://quay.io/confirm?code=%s</a>
"""
RECOVERY_MESSAGE = """
A user at <a href="https://quay.io">Quay.io</a> has attempted to recover their account
using this email.<br>
<br>
If you made this request, please click the following link to recover your account and
change your password:
<a href="https://quay.io/recovery?code=%s">https://quay.io/recovery?code=%s</a><br>
<br>
If you did not make this request, your account has not been compromised and the user was
not given access. Please disregard this email.<br>
"""
def send_confirmation_email(username, email, token):
msg = Message('Welcome to Quay! Please confirm your email.',
msg = Message('Welcome to Quay.io! Please confirm your email.',
sender='support@quay.io', # Why do I need this?
recipients=[email])
msg.html = CONFIRM_MESSAGE % (username, token, token)
mail.send(msg)
def send_recovery_email(email, token):
msg = Message('Quay.io account recovery.',
sender='support@quay.io', # Why do I need this?
recipients=[email])
msg.html = RECOVERY_MESSAGE % (token, token)
mail.send(msg)