work
This commit is contained in:
parent
4dd014136a
commit
b844baef81
23
README.md
23
README.md
|
@ -1,16 +1,31 @@
|
||||||
# cdadmin
|
# cdadmin
|
||||||
|
|
||||||
## Features
|
**cdadmin** is administration app for continuous deployment of static websites.
|
||||||
|
|
||||||
|
**cdadmin** is intended to be used with static websites that are generated after
|
||||||
|
changes are pushed to a Git repository. This works with sites that are being
|
||||||
|
edited in code and tracked in Git, as well as with headless CMSs that are
|
||||||
|
pushing changes via Git.
|
||||||
|
|
||||||
|
cdadmin starts a web server that provides an admin interface as well as an API.
|
||||||
|
|
||||||
|
No authentication is provided, so you'll want to reverse proxy through something
|
||||||
|
that protects the endpoints.
|
||||||
|
|
||||||
|
## Admin
|
||||||
|
|
||||||
|
The main page of the admin show the staging and production rebuild status. It
|
||||||
|
allows the user to queue a rebuild of the staging and production environments.
|
||||||
|
It also allows the user to merge staging into production.
|
||||||
|
|
||||||
- Create a webserver that has an API and an admin interface
|
|
||||||
- Passwordless authentication using email, sign-in available for users who are on a list
|
|
||||||
- Handles webhooks that rebuild staging
|
- Handles webhooks that rebuild staging
|
||||||
- Gives admin a button to rebuild staging
|
- Gives admin a button to rebuild staging
|
||||||
- Gives users feedback on the staging rebuild status
|
- Gives users feedback on the staging rebuild status
|
||||||
- Handles webhooks that rebuild production
|
- Handles webhooks that rebuild production
|
||||||
- Gives admin a button to rebuild production
|
- Gives admin a button to rebuild production
|
||||||
- Gives users feedback on the production rebuild status
|
- Gives users feedback on the production rebuild status
|
||||||
- Gives users a button to rebase production on staging (did I say that correctly?)
|
- Gives users a button to rebase production on staging (did I say that
|
||||||
|
correctly?)
|
||||||
|
|
||||||
## Email
|
## Email
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"node": true,
|
|
||||||
"esversion": 8
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
title: "{{ replace .Name "-" " " | title }}"
|
|
||||||
date: {{ .Date }}
|
|
||||||
draft: true
|
|
||||||
---
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +0,0 @@
|
||||||
baseURL = "http://example.org/"
|
|
||||||
languageCode = "en-us"
|
|
||||||
title = "cdadmin"
|
|
||||||
disableKinds = ["RSS"]
|
|
||||||
|
|
||||||
[permalinks]
|
|
||||||
pages = "/:filename/"
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>configure cdadmin</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css"
|
||||||
|
/>
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="app">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>cdadmin</h1>
|
||||||
|
<p>
|
||||||
|
<a href="status.html">Status</a>
|
||||||
|
<a href="merge.html">Merge</a>
|
||||||
|
<a href="configure.html">Configure</a>
|
||||||
|
</p>
|
||||||
|
<h2>Configure</h2>
|
||||||
|
</div>
|
||||||
|
<pre>{{configuration}}</pre>
|
||||||
|
<vue-form-generator
|
||||||
|
:schema="schema"
|
||||||
|
:model="configuration"
|
||||||
|
></vue-form-generator>
|
||||||
|
<button class="btn btn-primary">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://unpkg.com/vue@2.6.12/dist/vue.js"></script>
|
||||||
|
<script src="https://unpkg.com/vue-form-generator@2.3.4/dist/vfg.js"></script>
|
||||||
|
<script src="https://unpkg.com/axios@0.20.0/dist/axios.min.js"></script>
|
||||||
|
<script>
|
||||||
|
new Vue({
|
||||||
|
el: '.app',
|
||||||
|
components: {
|
||||||
|
'vue-form-generator': VueFormGenerator.component
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
configuration: {
|
||||||
|
repository: '',
|
||||||
|
production: '',
|
||||||
|
staging: ''
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'text',
|
||||||
|
label: 'Repository',
|
||||||
|
model: 'repository',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'text',
|
||||||
|
label: 'Production Branch',
|
||||||
|
model: 'production',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'text',
|
||||||
|
label: 'Staging Branch',
|
||||||
|
model: 'staging',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
formOptions: {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,119 +0,0 @@
|
||||||
---
|
|
||||||
title: Sign In
|
|
||||||
type: page
|
|
||||||
---
|
|
||||||
|
|
||||||
<style>
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: flex;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
-ms-flex-pack: center;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
align-items: center;
|
|
||||||
-webkit-box-pack: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding-top: 40px;
|
|
||||||
padding-bottom: 40px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
.app {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 330px;
|
|
||||||
padding: 15px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.form-signin .checkbox {
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
.form-signin .form-control {
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: auto;
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.form-signin .form-control:focus {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
.form-signin input[type='email'] {
|
|
||||||
margin-bottom: -1px;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
}
|
|
||||||
.form-signin input[type='password'] {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="app">
|
|
||||||
<form class="form-signin text-center" @submit="submit($event)">
|
|
||||||
<label for="inputEmail" class="sr-only">Email address</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="inputEmail"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Email address"
|
|
||||||
required
|
|
||||||
autofocus
|
|
||||||
v-model="email"
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<button class="btn btn-lg btn-primary btn-block" type="submit">
|
|
||||||
Sign in
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/axios.js"></script>
|
|
||||||
<script src="/vue.js"></script>
|
|
||||||
<script src="/messages.js"></script>
|
|
||||||
<script>
|
|
||||||
var app = new Vue({
|
|
||||||
data: {
|
|
||||||
email: ''
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
submit: function (ev) {
|
|
||||||
var _this = this;
|
|
||||||
if (ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
axios
|
|
||||||
.post('/api/authentication/signin', {
|
|
||||||
email: _this.email
|
|
||||||
})
|
|
||||||
.then(function (response) {})
|
|
||||||
.catch(function (error) {
|
|
||||||
console.log(_this.email);
|
|
||||||
window.postMessage('Something went wrong. Please try again.');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var token = window.localStorage.getItem('token');
|
|
||||||
if (token) {
|
|
||||||
axios
|
|
||||||
.get('/api/authentication/me', {
|
|
||||||
headers: {
|
|
||||||
Authorization: 'Bearer ' + token
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(function (response) {
|
|
||||||
window.location.href = '/';
|
|
||||||
})
|
|
||||||
.catch(function (error) {
|
|
||||||
window.localStorage.removeItem('token');
|
|
||||||
app.$mount('.app');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
app.$mount('.app');
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="{{ .Site.Language.Lang }}">
|
|
||||||
{{ partial "head.html" . }}
|
|
||||||
<body>
|
|
||||||
<!-- prettier-ignore -->
|
|
||||||
{{ block "main" . }}
|
|
||||||
{{ end }}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1 +0,0 @@
|
||||||
list
|
|
|
@ -1 +0,0 @@
|
||||||
default single
|
|
|
@ -1 +0,0 @@
|
||||||
{{ define "main" }} {{ .Content }} {{ end }}
|
|
|
@ -1 +0,0 @@
|
||||||
<link rel="stylesheet" href="/bootstrap.min.css" />
|
|
|
@ -1,5 +0,0 @@
|
||||||
<header class="header">
|
|
||||||
<div class="container">
|
|
||||||
<h1>cdadmin</h1>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>cdadmin</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css"
|
||||||
|
/>
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="app">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>cdadmin</h1>
|
||||||
|
<p>
|
||||||
|
<a href="status.html">Status</a>
|
||||||
|
<a href="merge.html">Merge</a>
|
||||||
|
<a href="configure.html">Configure</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<h2>Production</h2>
|
||||||
|
<p>
|
||||||
|
URL: <a href="#">URL</a><br />
|
||||||
|
Status: <br />
|
||||||
|
Branch: <br />
|
||||||
|
Commit:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<button class="btn btn-primary">Rebuild</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<section>
|
||||||
|
<h2>Staging</h2>
|
||||||
|
<p>
|
||||||
|
URL: <a href="#">URL</a><br />
|
||||||
|
Status: <br />
|
||||||
|
Branch: <br />
|
||||||
|
Commit:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<button class="btn btn-primary">Rebuild</button>
|
||||||
|
<button class="btn btn-primary">
|
||||||
|
Merge Staging Into Production
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://unpkg.com/vue@2.6.12/dist/vue.js"></script>
|
||||||
|
<script src="https://unpkg.com/vue-form-generator@2.3.4/dist/vfg.js"></script>
|
||||||
|
<script src="https://unpkg.com/axios@0.20.0/dist/axios.min.js"></script>
|
||||||
|
<script>
|
||||||
|
new Vue({
|
||||||
|
el: '.app',
|
||||||
|
components: {
|
||||||
|
'vue-form-generator': VueFormGenerator.component
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
configuration: {
|
||||||
|
repository: '',
|
||||||
|
production: '',
|
||||||
|
staging: ''
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'text',
|
||||||
|
label: 'Repository',
|
||||||
|
model: 'repository',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'text',
|
||||||
|
label: 'Production Branch',
|
||||||
|
model: 'production',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'text',
|
||||||
|
label: 'Staging Branch',
|
||||||
|
model: 'staging',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
formOptions: {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"name": "html",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^0.20.0",
|
|
||||||
"bootstrap": "^4.5.2",
|
|
||||||
"vue": "^2.6.12"
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
||||||
list
|
|
|
@ -1 +0,0 @@
|
||||||
list
|
|
|
@ -1,2 +0,0 @@
|
||||||
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
|
||||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
||||||
|
|
||||||
<url>
|
|
||||||
<loc>http://example.org/categories/</loc>
|
|
||||||
</url>
|
|
||||||
|
|
||||||
<url>
|
|
||||||
<loc>http://example.org/</loc>
|
|
||||||
</url>
|
|
||||||
|
|
||||||
<url>
|
|
||||||
<loc>http://example.org/signin/</loc>
|
|
||||||
</url>
|
|
||||||
|
|
||||||
<url>
|
|
||||||
<loc>http://example.org/tags/</loc>
|
|
||||||
</url>
|
|
||||||
|
|
||||||
</urlset>
|
|
|
@ -1 +0,0 @@
|
||||||
list
|
|
1742
html/static/axios.js
1742
html/static/axios.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,14 +0,0 @@
|
||||||
(function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var app = new Vue({
|
|
||||||
data: {
|
|
||||||
messages: []
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.cdadmin = window.cdadmin || {};
|
|
||||||
window.cdadmin.pushMessage = function (str) {
|
|
||||||
window.alert(str);
|
|
||||||
};
|
|
||||||
})();
|
|
11965
html/static/vue.js
11965
html/static/vue.js
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,94 @@
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>cdadmin</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css"
|
||||||
|
/>
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="app">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>cdadmin</h1>
|
||||||
|
<p>
|
||||||
|
<a href="status.html">Status</a>
|
||||||
|
<a href="merge.html">Merge</a>
|
||||||
|
<a href="configure.html">Configure</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<h2>Production</h2>
|
||||||
|
<p>
|
||||||
|
URL: <a href="#">URL</a><br />
|
||||||
|
Status: <br />
|
||||||
|
Branch: <br />
|
||||||
|
Commit:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<button class="btn btn-primary">Rebuild</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<section>
|
||||||
|
<h2>Staging</h2>
|
||||||
|
<p>
|
||||||
|
URL: <a href="#">URL</a><br />
|
||||||
|
Status: <br />
|
||||||
|
Branch: <br />
|
||||||
|
Commit:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<button class="btn btn-primary">Rebuild</button>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://unpkg.com/vue@2.6.12/dist/vue.js"></script>
|
||||||
|
<script src="https://unpkg.com/vue-form-generator@2.3.4/dist/vfg.js"></script>
|
||||||
|
<script src="https://unpkg.com/axios@0.20.0/dist/axios.min.js"></script>
|
||||||
|
<script>
|
||||||
|
new Vue({
|
||||||
|
el: '.app',
|
||||||
|
components: {
|
||||||
|
'vue-form-generator': VueFormGenerator.component
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
configuration: {
|
||||||
|
repository: '',
|
||||||
|
production: '',
|
||||||
|
staging: ''
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'text',
|
||||||
|
label: 'Repository',
|
||||||
|
model: 'repository',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'text',
|
||||||
|
label: 'Production Branch',
|
||||||
|
model: 'production',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'text',
|
||||||
|
label: 'Staging Branch',
|
||||||
|
model: 'staging',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
formOptions: {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,17 @@
|
||||||
|
.header {
|
||||||
|
padding: 3rem 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.header h2 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.header p {
|
||||||
|
margin: 2rem 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.header p a {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"axios": {
|
||||||
|
"version": "0.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz",
|
||||||
|
"integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"follow-redirects": {
|
||||||
|
"version": "1.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
|
||||||
|
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
|
||||||
|
},
|
||||||
|
"vue": {
|
||||||
|
"version": "2.6.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
|
||||||
|
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue