This commit is contained in:
Ryan Burnette 2020-09-21 10:34:41 -04:00
parent b5006a117c
commit 466df90e7c
31 changed files with 6260 additions and 113 deletions

2
.gitignore vendored
View File

@ -0,0 +1,2 @@
node_modules/
dist/

View File

@ -1,6 +0,0 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

View File

@ -1,8 +0,0 @@
baseURL = "/"
languageCode = "en-us"
title = "git-deploy"
theme = "Arc"
disableKinds = ["RSS"]
[permalinks]
pages = "/:filename"

View File

@ -1,23 +0,0 @@
---
title: Dashboard
---
<div class="columns">
<div class="column">
<div class="box">
<h2 class="is-size-5">Staging</h2>
<p>
<button class="button">Build</button>
<button class="button">Promote</button>
</p>
</div>
</div>
<div class="column">
<div class="box">
<h2 class="is-size-5">Production</h2>
<p>
<button class="button">Build</button>
</p>
</div>
</div>
</div>

View File

@ -1,9 +0,0 @@
---
title: About
---
<div class="content">
<p class="has-text-centered is-size-5">
<b>cdadmin</b> is a continous deployment tool for static websites.
</p>
</div>

View File

@ -1 +0,0 @@
signin

View File

@ -1,23 +0,0 @@
<footer class="footer">
<div class="container">
<div class="columns">
<div class="column has-text-left">
<p>
<a
href="https://git.rootprojects.org/root/git-deploy"
target="_blank"
>
Git Repository
</a>
</p>
</div>
<div class="column has-text-right">
<p>
<span>Version: 0.1.0</span>
</p>
</div>
</div>
</div>
</footer>
<script src="/main.js"></script>

View File

@ -1,3 +0,0 @@
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/custom.css" />

View File

@ -1,8 +0,0 @@
<header class="header">
<div class="brand content">
<h1>
<a href="{{ .Site.BaseURL }}">{{ .Site.Title }}</a>
</h1>
</div>
{{ partial "navbar.html" }}
</header>

View File

@ -1,7 +0,0 @@
<nav class="navbar">
<ul>
<li><a href="/">Dashboard</a></li>
<!-- <li><a href="/sigin">Sign In</a></li> -->
<li><a href="/sigout">Sign Out</a></li>
</ul>
</nav>

5679
html/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
html/package.json Normal file
View File

@ -0,0 +1,28 @@
{
"dependencies": {
"@ryanburnette/html-remove-empty-lines": "^1.1.4",
"@ryanburnette/spawn-in-parallel": "^1.0.1",
"autoprefixer": "^8.6.5",
"axios": "^0.20.0",
"bulma": "^0.9.0",
"css-loader": "^3.5.3",
"http-server": "^0.12.3",
"jshint": "^2.12.0",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.14.1",
"postcss-loader": "^4.0.2",
"prettier": "^2.1.2",
"purgecss": "^2.3.0",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.1",
"vue": "^2.6.12",
"vue-router": "^3.4.3",
"webpack": "5.0.0-beta.31",
"webpack-cli": "^3.3.12",
"webpack-merge": "^5.1.4",
"@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"babel-loader": "^8.1.0"
},
"devDependencies": {}
}

5
html/postcss.config.js Normal file
View File

@ -0,0 +1,5 @@
'use strict';
module.exports = {
plugins: [require('autoprefixer')]
};

10
html/purgecss.config.js Normal file
View File

@ -0,0 +1,10 @@
'use strict';
module.exports = {
content: ['public/**/*.html', 'public/**/*.js'],
css: ['public/**/*.css'],
output: 'public/',
keyframes: true,
whitelist: [],
whitelistPatterns: []
};

0
html/scripts/build Executable file
View File

10
html/scripts/development Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env node
'use strict';
var spawn = require('@ryanburnette/spawn-in-parallel');
spawn([
'npx http-server dist/',
'npx webpack --config webpack.development.js --watch --colors',
'scripts/watch'
]);

33
html/scripts/watch Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env node
'use strict';
var fs = require('fs');
var { exec } = require('child_process');
var cmd = `rsync -av --exclude="**.js" --exclude="css/" --exclude="js/" src/ dist/`;
fs.watch(
'src',
{
recursive: true
},
function (eventType, filename) {
//console.log(eventType, filename);
if (filename.includes('.html')) {
rsync();
}
}
);
function rsync() {
exec(
cmd,
{
path: process.env.PATH,
cwd: process.cwd()
},
function (err, stdout, stderr) {
console.log(stdout);
}
);
}
rsync();

View File

@ -0,0 +1,3 @@
{
"email": "ryan.burnette@gmail.com"
}

104
html/src/css/style.scss Normal file
View File

@ -0,0 +1,104 @@
// numbers
$golden: 1.618;
// colors
$primary: #1c6ba0;
$link: $primary;
// fonts
$family-primary: 'Open Sans', Arial, sans-serif;
@import 'bulma/bulma';
html,
body {
background-color: darken($white, 2.5);
}
// sticky footer
html,
body,
.app {
height: 100%;
}
.app {
display: flex;
flex-direction: column;
}
.main {
flex: 1 0 auto;
}
.navbar {
background-color: darken($white, 7.5);
font-size: (14/16) * 1rem;
}
.navbar-item {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
a.navbar-item:focus,
a.navbar-item:focus-within,
a.navbar-item:hover,
a.navbar-item.is-active,
.navbar-link:focus,
.navbar-link:focus-within,
.navbar-link:hover,
.navbar-link.is-active {
background-color: darken($white, 2.5);
}
.x-footer {
flex-shrink: 0;
}
.x-footer {
padding-top: 1rem;
padding-bottom: 1rem;
background-color: darken($white, 7.5);
p {
font-size: (12/16) * 1rem;
line-height: 2rem;
}
.column.x-l {
text-align: center;
}
.column.x-r {
text-align: center;
}
@include tablet {
.column.x-l {
text-align: left;
}
.column.x-r {
text-align: right;
}
}
}
.hero.is-fullheight.signin {
background-color: darken($white, 2.5);
min-height: calc(100vh - 4rem);
}
.button.continue {
width: 100%;
}
.signin-info p {
font-size: (14/16) * 1rem;
}
.signin {
// .content {
// margin-top: -0.5rem;
// margin-bottom: 1rem;
// }
.box {
padding-right: $golden * 1rem;
padding-bottom: 2rem;
padding-left: $golden * 1rem;
}
.column {
// @media screen and (min-width: 600px) {
max-width: 26rem;
// }
}
}
[v-cloak] {
@extend .is-hidden;
}

47
html/src/index.html Normal file
View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>git-deploy Dashboard</title>
<link rel="stylesheet" href="style.css" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,600;1,400&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div class="app">
<div class="main">
<router-view name="header" :user="user"></router-view>
<router-view name="main" :user="user"></router-view>
</div>
<div class="x-footer">
<div class="container is-fluid">
<div class="columns">
<div class="column x-l">
<p>
<a
href="https://git.ryanburnette.com/ryanburnette/git-deploy"
target="_blank"
>
git-deploy
</a>
v0.1.0
</p>
</div>
<div class="column x-r">
<p>
<button v-if="signedIn" class="button is-small">
Sign Out
</button>
</p>
</div>
</div>
</div>
</div>
</div>
<!-- <script src="/vendor.js"></script> -->
<script src="/main.js"></script>
</body>
</html>

5
html/src/js/.jshintrc Normal file
View File

@ -0,0 +1,5 @@
{
"node": true,
"browser": true,
"esversion": 8
}

160
html/src/js/main.js Normal file
View File

@ -0,0 +1,160 @@
'use strict';
require('../css/style.scss');
var Vue = require('vue');
var VueRouter = require('vue-router').default;
var axios = require('axios');
Vue.use(VueRouter);
var components = {};
components.signin = Vue.component('signin', function (resolve, reject) {
return axios.get('/templates/signin.html').then(function (resp) {
resolve({
template: resp.data,
data: function () {
return {
email: '',
busy: false,
state: ''
};
},
methods: {
signin: function (ev) {
ev.preventDefault();
ev.stopPropagation();
var _this = this;
_this.busy = true;
//console.log(_this.email);
window.localStorage.setItem('token', _this.email);
}
},
mounted: function () {
var _this = this;
var token = window.localStorage.getItem('token');
if (token) {
getMe().then(function (result) {
if (result) {
router.push('/');
} else {
_this.state = 'signingIn';
}
});
} else {
_this.state = 'signingIn';
}
}
});
});
});
components.navbar = Vue.component('navbar', function (resolve, reject) {
return axios.get('/templates/navbar.html').then(function (resp) {
resolve({
template: resp.data,
data: function () {
return {
ready: false
};
},
mounted: function () {
var _this = this;
_this.$parent.getUser().then(function (user) {
if (user.email) {
_this.ready = true;
} else {
_this.$router.push('/signin');
}
});
}
});
});
});
components.dashboard = Vue.component('dashboard', function (resolve, reject) {
return axios.get('/templates/dashboard.html').then(function (resp) {
resolve({
template: resp.data,
data: function () {
return {};
},
props: ['user'],
computed: {
ready: function () {
return !!this.user.email;
}
},
methods: {}
});
});
});
var routes = [
{
path: '/',
components: {
header: components.navbar,
main: components.dashboard
}
},
{
path: '/signin',
components: {
main: components.signin
}
}
];
var router = new VueRouter({
routes
});
var app = new Vue({
data: {
user: {}
},
methods: {
getUser: function () {
var _this = this;
if (_this.user) {
return Promise.resolve(_this.user);
}
return getMe().then(function (result) {
_this.user = result.user;
return _this.user;
});
}
},
computed: {
signedIn: function () {
return Object.keys(this.user).length > 0;
}
},
router,
created: function () {}
});
app.$mount('.app');
function getMe() {
var token = window.localStorage.getItem('token');
if (!token) {
return Promise.resolve(false);
}
return axios
.get('/api/auth/me.json')
.then(function (resp) {
return {
token,
user: resp.data
};
})
.catch(function (err) {
if (err.resp.status === 401) {
return false;
}
throw err;
});
}

View File

@ -0,0 +1,47 @@
<div class="dashboard" v-if="ready">
<div class="section">
<div class="container is-fluid">
<div class="title">
<h2 class="has-text-weight-bold">Dashboard</h2>
</div>
<div class="content">
<p>
<b>git-deploy</b> is a tool for managing continuous deployment of
statically generated websites.
</p>
</div>
<div class="columns">
<div class="column">
<div class="box">
<div class="title is-size-5">
<h3 class="has-text-weight-bold">Staging</h3>
</div>
<div class="content">
<p>Staging...</p>
</div>
<div class="content">
<p>
<button class="button is-primary">Build</button>
</p>
</div>
</div>
</div>
<div class="column">
<div class="box">
<div class="title is-size-5">
<h3 class="has-text-weight-bold">Production</h3>
</div>
<div class="content">
<p>Production...</p>
</div>
<div class="content">
<p>
<button class="button is-primary">Build</button>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,16 @@
<nav class="navbar" v-if="ready">
<div class="container is-fluid">
<div class="navbar-brand">
<span class="navbar-item brand-text has-text-weight-bold">
git-deploy
</span>
</div>
<div id="navMenu" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="/">Dashboard</a>
<a class="navbar-item" href="/jobs">Jobs</a>
<a class="navbar-item" href="/promote">Promote</a>
</div>
</div>
</div>
</nav>

View File

@ -0,0 +1,40 @@
<section class="hero is-fullheight signin">
<div class="hero-body" v-if="state === 'signingIn'">
<div class="container">
<div class="columns is-mobile is-centered">
<div class="column">
<div class="box">
<div class="title">
<h1 class="is-size-4 has-text-centered has-text-weight-bold">
git-deploy
</h1>
</div>
<div class="content has-text-centered signin-info">
<p>To sign in, enter your email address click Continue.</p>
</div>
<form @submit="signin($event)">
<div class="field">
<label for="email" class="label">Email</label>
<div class="control">
<input
id="email"
type="email"
class="input"
required
v-model="email"
/>
</div>
</div>
<div class="field">
<button class="button is-primary continue">Continue</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="hero-body" v-if="state === ''">
<div class="has-text-centered" style="width: 100%">Loading...</div>
</div>
</section>

View File

@ -1,3 +0,0 @@
{
"status": "idle"
}

View File

@ -1,21 +0,0 @@
html,
body,
.footer {
background-color: #ecf0f3;
}
.footer p {
display: inline-block;
font-size: 0.75rem;
line-height: 2rem;
}
.header {
background-color: #ffffff;
padding-top: 0.75rem;
padding-bottom: 0.5rem;
margin-bottom: 2rem;
}
.horizontal-overflow-scroll {
overflow-y: scroll;
}
/* .horizontal-overflow-scroll pre {
} */

@ -1 +0,0 @@
Subproject commit 76d321b3f961e6017ca7d364b25c148d2735c5bb

54
html/webpack.common.js Normal file
View File

@ -0,0 +1,54 @@
'use strict';
var fs = require('fs');
var path = require('path');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
context: path.resolve(__dirname, 'src'),
entry: {
// vendor: ['vue'],
main: {
import: './js/main.js'
// dependOn: 'vendor'
}
},
output: {
filename: `[name].js`
},
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
// 'postcss-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: `style.css`
})
],
resolve: {
alias: {
bulma: path.resolve(__dirname, 'node_modules/bulma'),
axios: path.resolve(__dirname, 'node_modules/axios/dist/axios.js'),
vue: path.resolve(__dirname, 'node_modules/vue/dist/vue.js')
}
}
};

View File

@ -0,0 +1,9 @@
'use strict';
var { merge } = require('webpack-merge');
var common = require('./webpack.common');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map'
});

View File

@ -0,0 +1,8 @@
'use strict';
var { merge } = require('webpack-merge');
var common = require('./webpack.common');
module.exports = merge(common, {
mode: 'production'
});