Initial commit

This commit is contained in:
Bastien Wirtz 2018-06-13 22:14:05 -07:00
parent b21bf8b1af
commit 09763dbf6a
18 changed files with 651 additions and 2 deletions

View File

@ -1,2 +1,81 @@
# homer
A very simple static homepage for your server.
# Homer
A very simple static HOMepage for your servER.
Add all your useful service, external links, notes... or anything.
If you need authentication support, you're on your own (it can be secured using a web server auth module or exposing it only through a VPN network / SSH tunneling, ...)
![screenshot](https://github.com/bastienwirtz/homer/blob/master/screenshot.png)
**How to build / install it? Where is the webpack config?**
There is no build system (😱), use it like that! It'meant to be stupid simple & zero maintenance required. just copy the static files somewhere, and visit the `index.html`.
## configuration
Title, icons, links, colors, and services can be configured in the `config.yml` file, using [yaml](http://yaml.org/) format.
```yaml
---
# Homepage configuration
# See https://fontawesome.com/v4.7.0/icons/ for icons options
title: "Simple homepage"
subtitle: "Homer"
logo: "assets/homer.png"
# Optional message
message:
title: "Optional message!"
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque risus mi, tempus quis placerat ut, porta nec nulla. Vestibulum rhoncus ac ex sit amet fringilla. Nullam gravida purus diam, et dictum felis venenatis efficitur. Aenean ac eleifend lacus, in mollis lectus. Donec sodales, arcu et sollicitudin porttitor, tortor urna tempor ligula."
# Optional navbar
links:
- name: "ansible"
icon: "fa-github"
url: "https://github.com/xxxxx/ansible/"
- name: "Wiki"
icon: "fa-book"
url: "https://wiki.xxxxxx.com/"
# Services
# First level array represent a group.
# Leave only a "items" key if not using group (group name & icon are optional, section separation will not be displayed).
services:
- name: "DevOps"
icon: "fa-code-fork"
items:
- name: "Jenkins"
logo: "/assets/tools/jenkins.png"
subtitle: "Continuous integration server"
tag: "CI"
url: "#"
- name: "RabbitMQ Management"
logo: "/assets/tools/rabbitmq.png"
subtitle: "Manage & monitor RabbitMQ server"
tag: "haproxy"
url: "#"
- name: "Monitoring"
icon: "fa-heartbeat"
items:
- name: "M/Monit"
logo: "/assets/tools/monit.png"
subtitle: "Monitor & manage all monit enabled hosts"
tag: "monit"
url: "#"
- name: "Grafana"
logo: "/assets/tools/grafana.png"
subtitle: "Metric analytics & dashboards"
url: "#"
- name: "Kibana"
logo: "/assets/tools/elastic.png"
subtitle: "Explore & visualize logs"
tag: "elk"
url: "#"
- name: "Website monitoring"
logo: "/assets/tools/pingdom.png"
subtitle: "Pingdom public reports overview"
tag: "CI"
url: "#"
```

130
app.css Normal file
View File

@ -0,0 +1,130 @@
body {
font-family: 'Raleway', sans-serif;
background-color: #F5F5F5;
height: 100%; }
body h1, body h2, body h3, body h4, body h5, body h6 {
font-family: 'Lato', sans-serif; }
body h1 {
font-size: 2rem; }
body h2 {
font-size: 1.7rem;
margin-top: 3rem;
margin-bottom: 1rem; }
body h2 .fa {
margin-right: 10px;
color: #4285f4; }
body h2 span {
font-weight: bold;
color: #4285f4; }
body [v-cloak] {
display: none; }
body #bighead {
color: #ffffff; }
body #bighead .dashboard-title {
padding: 6px 0 0 80px; }
body #bighead .first-line {
height: 100px;
vertical-align: center;
background-color: #3367d6; }
body #bighead .first-line h1 {
margin-top: -12px;
font-size: 2rem; }
body #bighead .first-line .headline {
margin-top: 5px;
font-size: 0.9rem; }
body #bighead .first-line .container {
height: 80px;
padding: 10px 0; }
body #bighead .first-line img {
float: left;
max-height: 70px;
max-width: 70px;
padding: 10px; }
body #bighead .navbar {
background-color: #4285f4; }
body #bighead .navbar a {
color: #152138; }
body #bighead .navbar a:hover {
background-color: #5a95f5; }
body #main-section {
margin-bottom: 3rem;
padding: 0; }
body #main-section h2 {
border-bottom: 1px dashed #ccc;
padding-bottom: 10px; }
body #main-section .title {
font-size: 1.1em; }
body #main-section .subtitle {
font-size: .9em; }
body #main-section .column {
padding: 1.2rem .75rem; }
body #main-section .message {
margin-top: 45px;
box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); }
body #main-section .message .message-header {
font-weight: bold; }
body #main-section .message .message-body {
border: none; }
body .media-content {
overflow: inherit; }
body .tag {
color: #4285f4;
background-color: #4285f4;
position: absolute;
top: 1rem;
right: -0.3rem;
width: 3px;
overflow: hidden;
transition: all 0.2s ease-out;
padding: 0; }
body .card {
border-radius: 5px;
border: none;
box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1);
transition: cubic-bezier(0.165, 0.84, 0.44, 1) 300ms; }
body .card:hover {
background-color: #FFFFFF;
transform: translate(0, -3px); }
body .card:hover .tag {
width: auto;
color: #ffffff;
padding: 0 0.75em; }
body .card-content {
height: 110px; }
body .footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 1rem 0.5rem;
text-align: left;
background-color: #fafafa;
border-top: 1px solid #F5F5F5; }
body .search-bar {
position: relative; }
body .search-bar #search {
border: none;
background-color: #5f98f6;
border-radius: 5px;
padding: 2px 12px 2px 30px;
margin: 10px 0;
transition: all 100ms linear;
color: #ffffff;
height: 30px;
width: 100px; }
body .search-bar #search:focus {
color: #000000;
width: 250px;
background-color: #ffffff; }
body .search-bar .search-label::before {
font-family: 'FontAwesome';
position: absolute;
top: 12px;
left: 8px;
content: "\f002";
width: 20px;
height: 20px; }
body .search-bar:focus-within .search-label::before {
color: #4a4a4a; }
/*# sourceMappingURL=app.css.map */

7
app.css.map Normal file
View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAGA,IAAK;EACH,WAAW,EAAE,qBAAqB;EAClC,gBAAgB,EAAE,OAAO;EACzB,MAAM,EAAE,IAAI;EAEZ,oDAAuB;IACrB,WAAW,EAAE,kBAAkB;EAGjC,OAAG;IACD,SAAS,EAAE,IAAI;EAGjB,OAAG;IACD,SAAS,EAAE,MAAM;IACjB,UAAU,EAAE,IAAI;IAChB,aAAa,EAAE,IAAI;IAEnB,WAAI;MACF,YAAY,EAAE,IAAI;MAClB,KAAK,EAtBO,OAAO;IAyBrB,YAAK;MACH,WAAW,EAAE,IAAI;MACjB,KAAK,EA3BO,OAAO;EA+BvB,cAAU;IACR,OAAO,EAAE,IAAI;EAGf,aAAS;IACP,KAAK,EAAE,OAAO;IAEd,8BAAiB;MACf,OAAO,EAAE,YAAY;IAGvB,yBAAY;MACV,MAAM,EAAE,KAAK;MACb,cAAc,EAAE,MAAM;MACtB,gBAAgB,EA9CN,OAAO;MAgDjB,4BAAG;QACD,UAAU,EAAE,KAAK;QACjB,SAAS,EAAE,IAAI;MAGjB,mCAAU;QACR,UAAU,EAAE,GAAG;QACf,SAAS,EAAE,MAAM;MAGnB,oCAAW;QACT,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,MAAM;MAGjB,6BAAI;QACF,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,IAAI;QAChB,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,IAAI;IAGjB,qBAAQ;MACN,gBAAgB,EAtEJ,OAAO;MAwEnB,uBAAE;QACA,KAAK,EAAE,OAAO;QAEd,6BAAQ;UACN,gBAAgB,EAAE,OAA+B;EAMzD,kBAAc;IACZ,aAAa,EAAE,IAAI;IACnB,OAAO,EAAE,CAAC;IAEV,qBAAG;MACD,aAAa,EAAE,eAAe;MAC9B,cAAc,EAAE,IAAI;IAGtB,yBAAO;MACL,SAAS,EAAE,KAAK;IAGlB,4BAAU;MACR,SAAS,EAAE,IAAI;IAGjB,0BAAQ;MACN,OAAO,EAAE,aAAa;IAGxB,2BAAS;MACP,UAAU,EAAE,IAAI;MAChB,UAAU,EAAE,+BAA+B;MAE3C,2CAAgB;QACd,WAAW,EAAE,IAAI;MAGnB,yCAAc;QACZ,MAAM,EAAE,IAAI;EAKlB,mBAAe;IACb,QAAQ,EAAE,OAAO;EAGnB,SAAK;IACH,KAAK,EA1HS,OAAO;IA2HrB,gBAAgB,EA3HF,OAAO;IA4HrB,QAAQ,EAAE,QAAQ;IAClB,GAAG,EAAE,IAAI;IACT,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,GAAG;IACV,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,iBAAiB;IAC7B,OAAO,EAAE,CAAC;EAGZ,UAAM;IACJ,aAAa,EAAE,GAAG;IAClB,MAAM,EAAE,IAAI;IACZ,UAAU,EAAE,+BAA+B;IAC3C,UAAU,EAAE,wCACd;EAEA,gBAAY;IACV,gBAAgB,EAAE,OAAO;IACzB,SAAS,EAAE,kBAAkB;IAE7B,qBAAK;MACH,KAAK,EAAE,IAAI;MACX,KAAK,EAAE,OAAO;MACd,OAAO,EAAE,QAAQ;EAIrB,kBAAc;IACZ,MAAM,EAAE,KAAK;EAGf,YAAQ;IACN,QAAQ,EAAE,KAAK;IACf,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,WAAW;IACpB,UAAU,EAAE,IAAI;IAChB,gBAAgB,EAAE,OAAO;IACzB,UAAU,EAAE,iBAAiB;EAG/B,gBAAY;IACV,QAAQ,EAAE,QAAQ;IAClB,wBAAQ;MACN,MAAM,EAAE,IAAI;MACZ,gBAAgB,EAAE,OAA+B;MACjD,aAAa,EAAE,GAAG;MAClB,OAAO,EAAE,iBAAiB;MAC1B,MAAM,EAAE,MAAM;MACd,UAAU,EAAE,gBAAgB;MAC5B,KAAK,EAAE,OAAO;MACd,MAAM,EAAE,IAAI;MACZ,KAAK,EAAE,KAAK;MAGZ,8BAAQ;QACN,KAAK,EAAE,OAAO;QACd,KAAK,EAAE,KAAK;QACZ,gBAAgB,EAAE,OAAO;IAI7B,sCAAsB;MACpB,WAAW,EAAE,aAAa;MAC1B,QAAQ,EAAE,QAAQ;MAClB,GAAG,EAAE,IAAI;MACT,IAAI,EAAE,GAAG;MACT,OAAO,EAAE,OAAO;MAChB,KAAK,EAAE,IAAI;MACX,MAAM,EAAE,IAAI;IAGZ,mDAAqC;MACjC,KAAK,EAAE,OAAO",
"sources": ["app.scss"],
"names": [],
"file": "app.css"
}

58
app.js Normal file
View File

@ -0,0 +1,58 @@
var app = new Vue({
el: '#app',
data: {
config: null,
filter: ''
},
beforeCreate () {
var that = this;
return getConfig().then(function (config) {
// Splice services list into groups of 3 for flex column display
var size = 3;
config.services.forEach(function(service) {
service.rows = [];
items = service.items;
while (items.length) {
service.rows.push(items.splice(0, size));
}
if (service.rows.length) {
var last = service.rows.length-1;
service.rows[last] = service.rows[last].concat(Array(size - service.rows[last].length));
}
});
that.config = config;
});
}
});
function getConfig() {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'config.yml');
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
try {
var data = jsyaml.load(xhr.response);
resolve(data);
} catch (e) {
console.error('fail to parse config file');
reject();
}
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send();
});
}

204
app.scss Normal file
View File

@ -0,0 +1,204 @@
$primary-color: #3367d6;
$secondary-color: #4285f4;
body {
font-family: 'Raleway', sans-serif;
background-color: #F5F5F5;
height: 100%;
h1, h2, h3, h4, h5, h6 {
font-family: 'Lato', sans-serif;
}
h1 {
font-size: 2rem;
}
h2 {
font-size: 1.7rem;
margin-top: 3rem;
margin-bottom: 1rem;
.fa {
margin-right: 10px;
color: $secondary-color;
}
span {
font-weight: bold;
color: $secondary-color;
}
}
[v-cloak] {
display: none
}
#bighead {
color: #ffffff;
.dashboard-title {
padding: 6px 0 0 80px;
}
.first-line {
height: 100px;
vertical-align: center;
background-color: $primary-color;
h1 {
margin-top: -12px;
font-size: 2rem;
}
.headline {
margin-top: 5px;
font-size: 0.9rem;
}
.container {
height: 80px;
padding: 10px 0;
}
img {
float: left;
max-height: 70px;
max-width: 70px;
padding: 10px;
}
}
.navbar {
background-color: $secondary-color;
a {
color: #152138;
&:hover {
background-color: lighten( $secondary-color, 5% );
}
}
}
}
#main-section {
margin-bottom: 3rem;
padding: 0;
h2 {
border-bottom: 1px dashed #ccc;
padding-bottom: 10px;
}
.title {
font-size: 1.1em;
}
.subtitle {
font-size: .9em;
}
.column {
padding: 1.2rem .75rem;
}
.message {
margin-top: 45px;
box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1);
.message-header {
font-weight: bold;
}
.message-body {
border: none;
}
}
}
.media-content {
overflow: inherit;
}
.tag {
color: $secondary-color;
background-color: $secondary-color;
position: absolute;
top: 1rem;
right: -0.3rem;
width: 3px;
overflow: hidden;
transition: all 0.2s ease-out;
padding: 0;
}
.card {
border-radius: 5px;
border: none;
box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1);
transition: cubic-bezier(0.165, 0.84, 0.44, 1) 300ms
}
.card:hover {
background-color: #FFFFFF;
transform: translate(0, -3px);
.tag {
width: auto;
color: #ffffff;
padding: 0 0.75em;
}
}
.card-content {
height: 110px;
}
.footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 1rem 0.5rem;
text-align: left;
background-color: #fafafa;
border-top: 1px solid #F5F5F5;
}
.search-bar {
position: relative;
#search {
border: none;
background-color: lighten( $secondary-color, 6% );
border-radius: 5px;
padding: 2px 12px 2px 30px;
margin: 10px 0;
transition: all 100ms linear;
color: #ffffff;
height: 30px;
width: 100px;
&:focus {
color: #000000;
width: 250px;
background-color: #ffffff;
}
}
.search-label::before {
font-family: 'FontAwesome';
position: absolute;
top: 12px;
left: 8px;
content: "\f002";
width: 20px;
height: 20px;
}
&:focus-within .search-label::before {
color: #4a4a4a;
}
}
}

BIN
assets/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
assets/homer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
assets/tools/elastic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/tools/grafana.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
assets/tools/jenkins.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
assets/tools/kibana.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
assets/tools/monit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets/tools/pingdom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/tools/rabbitmq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

63
config.yml Normal file
View File

@ -0,0 +1,63 @@
---
# Homepage configuration
# See https://fontawesome.com/v4.7.0/icons/ for icons options
title: "Simple homepage"
subtitle: "Homer"
logo: "assets/homer.png"
# Optional message
# See https://bulma.io/documentation/components/message/#colors for styling options.
message:
style: "is-warning"
title: "Optional message!"
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque risus mi, tempus quis placerat ut, porta nec nulla. Vestibulum rhoncus ac ex sit amet fringilla. Nullam gravida purus diam, et dictum felis venenatis efficitur. Aenean ac eleifend lacus, in mollis lectus. Donec sodales, arcu et sollicitudin porttitor, tortor urna tempor ligula."
# Optional navbar
links:
- name: "ansible"
icon: "fa-github"
url: "https://github.com/xxxxx/ansible/"
- name: "Wiki"
icon: "fa-book"
url: "https://wiki.xxxxxx.com/"
# Services
# First level array represent a group.
# Leave only a "items" key if not using group (group name & icon are optional, section separation will not be displayed).
services:
- name: "DevOps"
icon: "fa-code-fork"
items:
- name: "Jenkins"
logo: "/assets/tools/jenkins.png"
subtitle: "Continuous integration server"
tag: "CI"
url: "#"
- name: "RabbitMQ Management"
logo: "/assets/tools/rabbitmq.png"
subtitle: "Manage & monitor RabbitMQ server"
tag: "haproxy"
url: "#"
- name: "Monitoring"
icon: "fa-heartbeat"
items:
- name: "M/Monit"
logo: "/assets/tools/monit.png"
subtitle: "Monitor & manage all monit enabled hosts"
tag: "monit"
url: "#"
- name: "Grafana"
logo: "/assets/tools/grafana.png"
subtitle: "Metric analytics & dashboards"
url: "#"
- name: "Kibana"
logo: "/assets/tools/elastic.png"
subtitle: "Explore & visualize logs"
tag: "elk"
url: "#"
- name: "Website monitoring"
logo: "/assets/tools/pingdom.png"
subtitle: "Pingdom public reports overview"
tag: "CI"
url: "#"

107
index.html Normal file
View File

@ -0,0 +1,107 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<link rel="icon" type="image/png" href="assets/favicon.png">
<title>Homer</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
<link href="https://fonts.googleapis.com/css?family=Lato|Raleway" rel="stylesheet">
<link rel="stylesheet" href="app.css">
</head>
<body>
<div id="app" v-if="config">
<div id="bighead">
<section class="first-line">
<div class="container">
<img v-if="config.logo" :src="config.logo" />
<div class="dashboard-title">
<span v-cloak class="headline">{{ config.subtitle }}</span>
<h1 v-cloak>{{ config.title }}</h1>
</div>
</div>
</section>
<div v-if="config.links" class="container-fluid">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-menu">
<div class="navbar-start">
<a v-for="link in config.links" class="navbar-item" :href="link.url">
<i v-if="link.icon" style="margin-right: 6px;" class="fa" :class="link.icon"></i>
{{ link.name }}
</a>
</div>
<div class="end">
<div class="search-bar">
<label for="search" class="search-label"></label>
<input type="text" id="search" v-model="filter"/>
</div>
</div>
</div>
</div>
</nav>
</div>
</div>
<section id="main-section" class="section">
<div class="container">
<!-- Optional messages -->
<article v-cloak v-if="config && config.message" class="message" :class="config.message.style">
<div v-if="config.message.title" class="message-header">
<p>{{ config.message.title }}</p>
</div>
<div v-if="config.message.content" class="message-body">
{{ config.message.content }}
</div>
</article>
<h2 v-cloak v-if="filter"><i class="fa fa-search"></i> Search</h2>
<div v-for="(group, index) in config.services">
<h2 v-if="!filter && group.name"><i v-if="group.icon" class="fa" :class="group.icon"></i><span v-else>#</span> {{ group.name }}</h2>
<div v-for="items in group.rows">
<div class="columns">
<div v-for="item in items" v-if="!filter || (item && (item.name.toLowerCase().includes(filter.toLowerCase()) || (item.tag && item.tag.toLowerCase().includes(filter.toLowerCase()))))" class="column">
<div>
<div v-if='item' class="card">
<a :href="item.url">
<div class="card-content">
<div class="media">
<div v-if="item.logo" class="media-left">
<figure class="image is-48x48">
<img :src="item.logo" />
</figure>
</div>
<div class="media-content">
<p class="title is-4">{{ item.name }}</p>
<p class="subtitle is-6">{{ item.subtitle }}</p>
</div>
</div>
<strong v-if="item.tag" class="tag">#{{ item.tag }}</strong>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
<footer class="footer">
<div class="container">
<div class="content has-text-centered">
<p>Created with <span class="has-text-danger">❤️</span> with <a href="#">bulma</a>, <a href="#">vuejs</a> & <a href="#">font awesome</a>// Fork me on <a href="https://github.com/bastienwirtz/homer"><i class="fa fa-github-alt"></i></a>.</p>
</div>
</div>
</footer>
<script src="https://unpkg.com/vue"></script>
<script src="vendors/js-yaml.min.js"></script>
<script src="app.js"></script>
</body>
</html>

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

1
vendors/js-yaml.min.js vendored Normal file

File diff suppressed because one or more lines are too long