diff options
28 files changed, 578 insertions, 456 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6d8daaa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# cState Updates + +## v1.0 (November 25, 2017) + +**cState is now ready for use in production! With a switch to the Hugo theming system, you can now independently manage incidents, content, settings, while seamlessly keeping cState up to date.** + ++ Whole new header design that lets you choose between a logo and just text ++ Updated incident history section and post design ++ Updated config file; added variables for changing the look and feel of your status page ++ Ensured proper legacy browser support by making JavaScript optional ++ Update incident view to include better mini header, clear issue type, and whether it is resolved ++ Updated footer design, added copyright notice +- Removed pinging, which was never properly implemented +- Removed notifications, as they never worked in the first place +- Fixed bug, where the main announcement box would clip, creating a small white 1px border +- Generally removed a lot of leftover cruft and ensured cState is in pristine condition for a stable release diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..1e5a647 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,21 @@ +# Code of Conduct + +> TL;DR is to be a good, sensible person. It does not matter who you are. + +This community as a whole, much like others, is made up of a mixture of professionals and volunteers from all over the world, working on every aspect of the project at hand. Diversity can be a strength, but it can also lead to communication issues and unhappiness. To that end, we have a few ground rules that we ask people to adhere to. This code applies to all spaces managed by the project creators equally. + +This isn’t an exhaustive list of things that you can’t do. Rather, take it in the spirit in which it’s intended — a guide to make it easier to enrich all of us and the technical communities in which we participate. + ++ **Be friendly and patient.** Use common sense, treat others like you would want to be treated. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. + * Violent threats or language directed against another person, especially personally. + * Discriminatory jokes and language. + * Posting violent material, such as sexually explicit content. + * Posting (or threatening to post) other people’s personally identifying information (“doxing”). + * Unwelcome sexual attention. + * Advocating for, or encouraging, any of the above behavior. + * Repeated harassment of others. In general, if someone asks you to stop, then stop. ++ **Be welcoming and considerate.** Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users, so you should take those consequences into account when making decisions. Remember that the internet stretches to almost all corners of the world and you might not be communicating in someone else’s primary language. ++ **Be respectful.** Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. Members of this community should be respectful when dealing with other members as well as with people outside the Django community. ++ **When there is disagreement, try to understand why.** Disagreements, both social and technical, can happen all the time and our community is no exception. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of Django comes from its varied community, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. + +*This Code of Conduct is based upon the [Django Code of Conduct](https://www.djangoproject.com/conduct/). If you want to use this Code of Conduct for your project, please feel free to do so!* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1675b87 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,19 @@ +# Contributing Guide + +Thanks for taking the time to contribute. Folks like you are more than welcome! + +## I want to suggest something, report a bug, etc. + +Cool, [just create an issue](https://github.com/mistermantas/cstate/issues)! + +## I want to help contribute a new feature. + +Great. Here are some things to keep in mind: + ++ Keep it as simple as possible. ++ Keep it as light as possible. ++ Try to make it readable. + +## Wait, my question has not been answered. + +At the time of writing, cState is such a small project that anyone contributing would be a downright miracle. However, if you are a miracle worker, please do redirect any concerns and they will addressed as soon as humanely possible. [Just create an issue](https://github.com/mistermantas/cstate/issues)! @@ -1,71 +1,127 @@ # cState -[![Discord](https://img.shields.io/badge/discord-join%20chat-7289DA.svg?style=flat-square)](http://discord.io/choraleapp) [![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![Twitter](https://img.shields.io/twitter/follow/choraleapp.svg?style=social&label=Follow)](https://twitter.com/polargalaxymc) +[![GitHub last commit](https://img.shields.io/github/last-commit/mistermantas/cstate.svg?style=flat-square)](https://github.com/mistermantas/cstate/commits/master) +[![GitHub repo size in bytes](https://img.shields.io/github/repo-size/mistermantas/cstate.svg?style=flat-square)](https://github.com/mistermantas/cstate/tree/master/) +[![Discord](https://img.shields.io/badge/discord-join%20chat-7289DA.svg?style=flat-square)](http://discord.io/choraleapp) [![Twitter](https://img.shields.io/twitter/follow/mistermantas.svg?style=social&label=Follow)](https://twitter.com/mistermantas) -> Blazing fast status page with excellent browser support. Built with Hugo. Work in progress, may have bugs and incomplete features. +> The fastest and most efficient status page on the market, beating even paid solutions. cState has outstanding browser support (IE8+) and can easily be managed with GitHub Pages or Netlify. Ready for production. -Is [statuspage.io](https://www.statuspage.io/) too expensive? Do you need an open source alternative for your project that is supported on archaic browsers like IE8 and never stops beating? cState is here to help. +[**See real-world example**](https://status.rabbitnode.com) -[Live demo](https://status.choraleapp.com) - -[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/mistermantas/cstate) - -## Table of Contents +## Contents + [Features](#features) -+ [Installation](#installation) - + On Netlify - + GitHub Pages ++ [Install](#install) ++ [FAQ](#faq) + [Contribute](#contribute) + [License](#license) ## Features -+ Built with [Hugo](https://gohugo.io), on a sturdy foundation -+ Works everywhere: all modern browsers, even IE8 and later -+ You can subscribe to web notifications for status updates -+ Simple, focused, and robust design -+ Easy to edit and deploy ++ Built with [Hugo](https://gohugo.io), a hyperfast Golang generator ++ Works not just on mobile browsers, but also on archaic browsers like Internet Explorer 8 ++ Comes with a simple, focused, and extremely light design ++ Edit your status page just from the config file ++ Comes pre-equipped with Netlify CMS for quick updates ++ Easy to edit and deploy on Netlify or GitHub Pages -## Installation +## Install -### On Netlify +We encourage you to use [Netlify](https://www.netlify.com) for cState. The build command is `hugo` and your site is compiled to `public`. -1. [Deploy to Netlify](https://app.netlify.com/start/deploy?repository=https://github.com/mistermantas/cstate). -2. Upload your favicon and logo to `/static/` and edit `config.yml`. +For this tutorial, it is assumed that you have Hugo and Git installed (check with `hugo version` & `git --version`). -### Everywhere Else +**This does not seem to work in one go on PowerShell, so enter each command individually.** +```bash +# !. First off, we initialize the Git repository +git init; +# 2. Then this creates all the necessary directories +mkdir -p content/issues themes static; -## Creating Incident +# 3. We get the config file +curl -o config.yml https://cdn.rawgit.com/mistermantas/cstate/4bc81871/config-example.yml; -Once in the project root directory, you can create a new post from the command line like this: +# 4. Download cState +cd themes; git submodule add https://github.com/mistermantas/cstate; +# 5. Last off, start the server locally +hugo serve ``` -hugo new incident/dns-killed-us.md + +And that is it; you have set up cState locally. + +Now is a good time to make cState look the way you want it to, so upload a favicon (and logo) to `/static/`. Edit `config.yml` to fit your needs. And so on, and so forth. + +**Do not change any files in the `themes` directory or its subdirectories. Everything is handled automatically by Git.** + +To make the status page public, you will need to connect to a remote GitHub repository much like this: + +```bash +# Create a remote origin like this (if you have not already) +git remote add origin https://github.com/username/example.git + +# Add all the files +git add -A + +# Then a message based on your changes +git commit -m "Testing out cState" + +# All done +git push -u origin master ``` -Essentially, just go into `content/incident` and add a new file. This will be an incident. The file name will indicate the URL of the incident. So, for example, if you create `dns-killed-us.md`, the URL will lead to `status.example.com/incident/dns-killed-us`. +For an example of a working status page, see [rabbitnode/status](https://github.com/rabbitnode/status). + +## FAQ -Then, go into `incident/dns-killed-us.md` and follow this format: +### Where do issues go? What is the frontmatter, how do I define metadata for issues? + +Create a file in `content/issues`. The name of the file will be the slug (what shows up in the URL bar). For example, this is what `i-am-an-issue.md` should look like: ```md --- -Title: Catastrophic DNS failure -Description: After moving from one server to another, DNS just kinda gave us the middle finger. Ugh. -Date: 2017-04-04T15:58:32 +Title: Give your issue a good title +Description: This description is here merely for metadata purposes and may show up in search results. It may be used as a summary. +Date: 2017-02-30 14:30 +Resolved: true +Severity: down +Affected: + - Client Area Section: post --- -##### Post-mortem - -On Monday, Amazon gave up on us. +Content goes here. ``` +Time to break that down. + +`Title`: This is the one of the most important parts of an incident. *(required)* +`Description`: This description is here merely for metadata purposes and may show up in search results. It may be used as a summary. +`Date`: An ISO-8601 formatted date. Does not include time zone. *(required)* +`Resolved`: Whether issue should affect overall status. Either `true` or `false`. *(boolean, required)* +`Severity`: If an issue is not resolved, it will have an applied severity. There are 3 levels of severity: `notice`, `disrupted`, and `down`. If there are multiple issues, the status page will take the appearance of the more drastic issue (such as `disrupted` instead of `notice`). *(required)* +`Affected`. Add the items that were present in the config file which should alter the status of each individual system (component). *(array, required)* +`Section`. This must be `issue`, so that Hugo treats it as one. *(required)* + +### Is there an admin panel or some easy way to change the state of each issue? + +If you use [Netlify](https://www.netlify.com), you can expect to see Netlify CMS integration very soon. Otherwise, you could fall back to [prose.io](http://prose.io) or something similiar. + +### How do I make this work on GitHub Pages? + +Compile locally, commit changes, and push them out. We do recommend using [Netlify](https://www.netlify.com), however. + +### My question was not answered! + +This part of the documentation still needs to finished. [Questions](https://github.com/mistermantas/cstate/issues) are more than welcome and you should get a pretty fast response as well. + ## Contribute -Feel free to open an issue or make a pull request, those should get answered pretty quickly on GitHub. ++ Glance over the [Code of Conduct](/CODE_OF_CONDUCT.md). ++ Before submitting a pull request, create an issue to [discuss the implications of your proposal](https://github.com/mistermantas/cstate/issues). ++ Write consistent, simple, and readable code. You can [join the Chorale Discord](http://discord.io/choraleapp) to discuss in `#cstate`. ## License diff --git a/archetypes/default.md b/archetypes/default.md new file mode 100644 index 0000000..193e817 --- /dev/null +++ b/archetypes/default.md @@ -0,0 +1,9 @@ +--- +Title: +Description: +Date: +Resolved: +Severity: +Affected: +Section: issue +--- diff --git a/config-example.yml b/config-example.yml new file mode 100644 index 0000000..39bae6e --- /dev/null +++ b/config-example.yml @@ -0,0 +1,67 @@ +# Welcome to the Hugo configuration file. +# Hugo is used for building the status page, +# so this file can be used to change how +# your status page should behave or look. + +# What is your status page called? +# Shows up in the browser bar and meta tags +title: Example Inc. Status + +# What language is this page in? +# Only alters the html[lang] attribute +languageCode: en-US + +# What is the hostname or path to the root? +# Where is the site hosted? +# Example: https://status.example.com/ +baseURL: https://status.example.com/ + +# Should posts, which have a publish date +# from the future, be built? Useful for +# sharing upcoming maintenance, etc. +# +# We recommend to keep this at `true`. +# BOOLEAN; `true`, `false` +buildFuture: true + +params: + # These are your systems. Change them to + # change the amount of components. + systems: + - Core + - EU + - US + + # Should we show the logo or the title + # of the status page? + useLogo: false + + # Where is the logo located, if one is + # present at all? + logo: /logo.png + + # This is the description that is shown + # in the footer and meta tags. + description: We continuously monitor the status of our services and if there are any interruptions, a note will be posted here. + + # Cplors throughout cState + # + # Defaults: + # + # ok: 228B22 + # warning: DC143C + # down: FF8C00 + # notice: 708090 + # border: dfdfdf + # faded: ccc + ok: 228B22 + disrupted: FF8C00 + down: DC143C + notice: 708090 + border: dfdfdf + faded: ccc + +# These options affect the core of cState. +# Please do not change them if you do not +# know what you are doing. +theme: cstate diff --git a/config.yml b/config.yml deleted file mode 100644 index 1f015b2..0000000 --- a/config.yml +++ /dev/null @@ -1,12 +0,0 @@ -# General -title: Chorale Status -languageCode: en-US -baseURL: / -permalinks: - incidents: /incidents/:year/:month/:day/:slug/ -params: - # Extra customization - logo: /logo.png - description: We continuously monitor the status of our services and if there are any interruptions, a note will be posted here. For the best experience, use a modern browser like Chrome. - # Current state - announcement: Everything looks fine, but if you see something wrong, give us a shout by using the chat box in the bottom right corner. diff --git a/content/incident/https-on-status-page.md b/content/incident/https-on-status-page.md deleted file mode 100644 index 36f476c..0000000 --- a/content/incident/https-on-status-page.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -Title: HTTPS on status page not working -Description: Cannot access status.choraleapp.com without getting a certificate error -Date: 2017-08-13T17:58:32 -Section: post ---- - -**Update as of 2017-08-14T19:15:59:** The issue has been resolved. - -Cannot access status.choraleapp.com without getting a certificate error. - -Contacted Netlify for support. diff --git a/layouts/incident/single.html b/layouts/_default/single.html index 92ead19..c0a9070 100644 --- a/layouts/incident/single.html +++ b/layouts/_default/single.html @@ -1,13 +1,10 @@ {{ partial "meta" . }} <body> - <div class="contain"> <a href="{{ .Site.BaseURL }}">← Go back</a> - - <br><br><hr> - - {{ .Render "post"}} + <hr> + {{ .Render "post" }} </div> {{ partial "footer" . }} diff --git a/layouts/incident/post.html b/layouts/incident/post.html deleted file mode 100644 index 593481a..0000000 --- a/layouts/incident/post.html +++ /dev/null @@ -1,8 +0,0 @@ -<div class="article"> - <h3> - <a href="{{ .Permalink }}">{{ .Title }} {{ if .Draft }}[DRAFT]{{ end }}</a> - </h3> - <h4>Began on {{ .Date }} ({{ .ReadingTime }} min read)</h4> - - {{ .Content }} -</div> diff --git a/layouts/index.html b/layouts/index.html index 85b800a..68a1265 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -1,244 +1,83 @@ {{ partial "meta" . }} - - <body> - <div class="header notice"> - <div class="contain"> - <a href="{{ .Site.BaseURL }}" class="logo"> - <img src="{{ .Site.Params.logo }}" alt="{{ .Site.Title }}"> +{{ $incidents := where .Site.RegularPages "Params.section" "issue" }} +{{ $active := where $incidents "Params.resolved" "=" false }} + +{{ $isNotice := where $active "Params.severity" "=" "notice" }} +{{ $isDisrupted := where $active "Params.severity" "=" "disrupted" }} +{{ $isDown := where $active "Params.severity" "=" "down" }} + + <body class="status-{{ if $isDown }}down{{ else }}{{ if $isDisrupted}}disrupted{{ else }}{{ if $isNotice }}notice{{ else }}ok{{ end }}{{ end }}{{ end }}"> + <div class="header"> + <div class="contain contain--more center"> + <a href="/" class="logo"> + {{ if .Site.Params.useLogo }} + <h1><img src="{{ .Site.Params.logo }}" alt="{{ .Site.Title }}"></h1> + {{ else }} + <h1>{{ .Site.Title }}</h1> + {{ end }} </a> - - <button class="subscribe">Subscribe</button> </div> </div> <!-- Main --> <div class="contain"> <noscript> - <p class="error">Uh oh! It looks like you have disabled JavaScript or your browser is a piece of garbage. This means we cannot fetch the neccesary data to show you information about our services. Please <a href="//enable-javascript.com">enable scripting</a> and try again.</p> + <p class="error">Uh oh! It looks like you have disabled JavaScript. Please <a href="//enable-javascript.com">enable scripting</a> to enhance your experience on this website.</p> + <div class="padding"></div> </noscript> - <div class="subscriber-box"> - <div class="subscriber-box--header"> - <div class="contain"> - <strong>Notifications</strong> - <span class="close"> - <img alt="Close" src="https://cdn4.iconfinder.com/data/icons/geomicons/32/672366-x-128.png"> - </span> - </div> - </div> - - <div class="contain"> - <p>Users of modern browsers such as Chrome and Firefox get access to web notifications, a feature that shows alerts for things such as Facebook mentions, new emails, or in our case, status updates. These updates can be seen even if the user is not actively looking at this status page, however, the tab has to stay open.</p> - - <p class="error">Only some users may be able to use this feature. This feature does not work on mobile devices and is not fully tested.</p> - - <div id="alert-init"> - <input type="checkbox" id="alerts"> - <label for="alerts">Ping me when the status changes</label> - </div> - - <p class="alert-status faded">You have not enabled notifications.</p> - </div> + <!-- Main info --> + <div class="summary"> + <strong> + {{ if $isDown }} + Experiencing major issues + {{ else }} + {{ if $isDisrupted}} + Experiencing disruptions + {{ else }} + {{ if $isNotice }} + Please read announcement + {{ else }} + All systems operational + {{ end }}{{ end }}{{ end }} + </strong> + + <span class="status summary__date" onclick="location.reload()"></span> </div> - <!-- Main info --> - <div class="summary notice"> - <div class="tldr notice"> - <strong>Checking status…</strong> + {{ range $active }} + <div class="padding"></div> + <small class="date">{{ .Date.Format "January 02, 2006 at 3:04 PM" }}</small><br> + <strong class="faded">{{ .Title }}</strong> + {{ .Content }} + <div class="padding"></div> + {{ else }}{{ end }} - <span class="status"></span> - </div> - <div class="details contain"> - <p>{{ .Site.Params.announcement }}</p> - </div> - </div><br> + <div class="padding"></div> <!-- Individual info --> <div class="components"> - <div class="component" data-status="" data-id="forums"> - API <small class="ping testing">Pinging…</small> - </div> - <div class="component" data-status="" data-id="website"> - Website <small class="ping testing">Pinging…</small> - </div> + {{ $systems := .Site.Params.systems }} + {{ range $index, $systems }} + <div class="component" data-status="ok"> + {{ . }} + <span class="component-status">Operational</span> + </div> + {{ end }} </div> - <br> - <small><a href="#disclaimer">Disclaimer</a></small> - <!-- End main --> - </div> + </div><div class="padding"></div><hr> <div class="contain"> - <h2>Incident history</h2><hr><br> + <h2 class="center">Incident history</h2> {{ range first 10 .Data.Pages }} - {{ .Render "post" }} + {{ .Render "issue" }} {{ end }} - - <aside id="meta"> </aside> </div> - <script async> - /** - * Dev toolset - */ - - console.log('Welcome to cState! https://github.com/mistermantas/cstate'); - - /** - * Notifications - */ - - // Notification toggle - document.querySelector('#alerts').addEventListener('click', enableNotifications) - - // Toggling logic - function enableNotifications() { - if(window.Notification && Notification.permission !== "denied") { - Notification.requestPermission(function(status) { - // status is "granted", if accepted by user - var n = new Notification('Great, you just enabled alerts!', { - body: 'You are now going to receive notifications (like this one) whenever the status changes so long as this tab is open. This feature is still being tested.', - icon: '/favicon.ico' - }) - - // Looks like we DO have permission now - // So let's mark that checkbox - document.querySelector('#alert-init').setAttribute('hidden', 'hidden') - document.querySelector('.alert-status').innerHTML = '<strong>Notifications are enabled. </strong>' - document.querySelector('.alert-status').className = 'alert-status' - }) - } - } - - if (Notification.permission === 'granted') { - // Looks like we DO have permission - // So let's mark that checkbox - document.querySelector('#alert-init').setAttribute('hidden', 'hidden') - document.querySelector('.alert-status').innerHTML = '<strong>Notifications are enabled. </strong>' - document.querySelector('.alert-status').className = 'alert-status' - } - - /** - * Subscribe button - */ - - function hasClass(element, cls) { - return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1; - } - - document.querySelector('.subscribe').addEventListener('click', pressSubscribeButton); - document.querySelector('.close').addEventListener('click', pressSubscribeButton); - - function pressSubscribeButton() { - if (document.querySelector('.subscriber-box').className === 'subscriber-box active') { - document.querySelector('.subscriber-box').className = 'subscriber-box'; - } else { - document.querySelector('.subscriber-box').className = 'subscriber-box active'; - } - } - - /** - * Apply data - */ - - document.querySelector('.component[data-id=forums]').setAttribute('data-status', 'ok'); - document.querySelector('.component[data-id=website]').setAttribute('data-status', 'ok'); - - /** - * Get elements - */ - - const header = document.querySelector('.header'); - const summary = document.querySelector('.summary'); - const summaryDetails = document.querySelector('.details'); - const tldr = document.querySelector('.tldr'); - - var summaryText = document.querySelector('.summary strong'); - var lastUpdated = document.querySelector('.summary span'); - - /** - * Prelimenary logic - */ - - var online = navigator.onLine; - - function updateStatus() { - var lastUpdate = new Date; - } - - /** - * Check for internet - */ - - var lastUpdate = new Date(); - - function timeSince(date) { - var seconds = Math.floor((new Date() - date) / 1000); - - var interval = Math.floor(seconds / 31536000); - - if (interval > 1) { - return interval + ' years'; - } - interval = Math.floor(seconds / 2592000); - if (interval > 1) { - return interval + ' months'; - } - interval = Math.floor(seconds / 86400); - if (interval > 1) { - return interval + 'd'; - } - interval = Math.floor(seconds / 3600); - if (interval > 1) { - return interval + 'h'; - } - interval = Math.floor(seconds / 60); - if (interval > 1) { - return interval + 'min'; - } - return Math.floor(seconds) + 's'; - } - var aDay = 24*60*60*1000; - - // Show second by second updates - window.setInterval(function() { - lastUpdated.innerHTML = 'Last updated ' + timeSince(lastUpdate) + ' ago'; - }, 1000); - - - /** - * Adaptive TLDR - */ - - const status = document.querySelector('.component[data-id=forums]').getAttribute('data-status') === 'ok' && - document.querySelector('.component[data-id=website]').getAttribute('data-status') === 'ok' - - if (status) { - // Change text - summaryText.innerHTML = 'All systems operational'; - // Change design - summary.className = 'summary ok'; - tldr.className = 'tldr ok'; - summaryDetails.className = 'details contain ok'; - header.className = 'header ok'; - } else if (!status) { - // Change text - summaryText.innerHTML = 'Experiencing downtime'; - // Change design - summary.className = 'summary down'; - tldr.className = 'tldr down'; - summaryDetails.className = 'details contain down'; - header.className = 'header down'; - - new Notification ( - 'We are experiencing downtime!', { - body : 'Please view the status page for more information. This alert was automatically triggered to let you know of this change.', - icon : '/favicon.ico' - } - ) - } - </script> - -{{ partial "footer" . }} + {{ partial "js" . }} + {{ partial "footer" . }} + </body> +</html> diff --git a/layouts/issues/issue.html b/layouts/issues/issue.html new file mode 100644 index 0000000..9b63b78 --- /dev/null +++ b/layouts/issues/issue.html @@ -0,0 +1,29 @@ +{{ $incidents := where .Site.RegularPages "Params.section" "issue" }} +{{ $active := where $incidents "Params.resolved" "=" false }} + +{{ $isNotice := where $active "Params.severity" "=" "notice" }} +{{ $isDisrupted := where $active "Params.severity" "=" "disrupted" }} +{{ $isDown := where $active "Params.severity" "=" "down" }} + +<div class="article"> + <span class="marker"> + {{ .Params.severity }} + </span> + + <small class="date"> + {{ .Date.Format "January 02, 2006 at 3:04 PM" }} + </small> + + {{ if .Params.Resolved }} + <span class="ok">✓</span> + {{ end }} + + + <h3> + <a href="{{ .Permalink }}">{{ .Title }}</a> + </h3> + + <hr> + + {{ .Content }} +</div> diff --git a/layouts/issues/single.html b/layouts/issues/single.html new file mode 100644 index 0000000..52c4145 --- /dev/null +++ b/layouts/issues/single.html @@ -0,0 +1,17 @@ +{{ partial "meta" . }} + +{{ $incidents := where .Site.RegularPages "Params.section" "issue" }} +{{ $active := where $incidents "Params.resolved" "=" false }} + +{{ $isNotice := where $active "Params.severity" "=" "notice" }} +{{ $isDisrupted := where $active "Params.severity" "=" "disrupted" }} +{{ $isDown := where $active "Params.severity" "=" "down" }} + + <body class="status-{{ if $isDown }}down{{ else }}{{ if $isDisrupted}}disrupted{{ else }}{{ if $isNotice }}notice{{ else }}ok{{ end }}{{ end }}{{ end }}"> + {{ partial "header-mini" . }} + + <div class="contain"> + {{ .Render "issue" }} + </div> + +{{ partial "footer" . }} diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html index 1773af9..e1314ce 100644 --- a/layouts/partials/footer.html +++ b/layouts/partials/footer.html @@ -1,14 +1,10 @@ - <div class="footer"> - <div class="contain"> +<div class="footer"> + <div class="contain"> + <p><strong>{{ now.Year }} © {{ .Site.Title }}</strong></p> - <p id="disclaimer">{{ .Site.Params.description }}</p> + <p>{{ .Site.Params.description }}</p> - <small class="copyright">Powered by <a href="https://github.com/mistermantas/cstate">cState</a></small> - - </div> - </div> - - - <script defer>window.$crisp=[];window.CRISP_WEBSITE_ID="1456a9bd-7292-4dee-b61d-5d9946f925b5";(function(){d=document;s=d.createElement("script");s.src="https://client.crisp.chat/l.js";s.async=1;d.getElementsByTagName("head")[0].appendChild(s);})();</script> - </body> -</html> + <!-- Please keep this one around to help cState grow <3 --> + <small class="copyright faded">Powered by <a href="https://github.com/mistermantas/cstate">cState</a></small> + </div> +</div> diff --git a/layouts/partials/header-mini.html b/layouts/partials/header-mini.html new file mode 100644 index 0000000..93a26c2 --- /dev/null +++ b/layouts/partials/header-mini.html @@ -0,0 +1,7 @@ +<div class="header"> + <div class="contain"> + <a href="/" class="no-underline">← + <strong>{{ .Site.Title }}</strong> + </a> + </div> +</div> diff --git a/layouts/partials/js.html b/layouts/partials/js.html new file mode 100644 index 0000000..9ef2a23 --- /dev/null +++ b/layouts/partials/js.html @@ -0,0 +1,62 @@ +<script type="javascript"> + /** + * Dev toolset + */ + + console.log('Welcome to cState! https://github.com/mistermantas/cstate'); + document.querySelector('html').className = 'js'; + + + /** + * Make theme color pretty + */ + + if (document.body.className === 'status-down') { + document.querySelector('meta[name=theme-color]').setAttribute('content', themeDownColor); + } else if (document.body.className === 'status-disrupted') { + document.querySelector('meta[name=theme-color]').setAttribute('content', themeDisruptedColor); + } else { + document.querySelector('meta[name=theme-color]').setAttribute('content', themeNoticeColor); + } + + + /** + * Check for internet + */ + + var lastUpdated = document.querySelector('.summary__date'); + var lastUpdate = new Date(); + + function timeSince(date) { + var seconds = Math.floor((new Date() - date) / 1000); + + var interval = Math.floor(seconds / 31536000); + + if (interval > 1) { + return interval + ' years'; + } + interval = Math.floor(seconds / 2592000); + if (interval > 1) { + return interval + ' months'; + } + interval = Math.floor(seconds / 86400); + if (interval > 1) { + return interval + 'd'; + } + interval = Math.floor(seconds / 3600); + if (interval > 1) { + return interval + 'h'; + } + interval = Math.floor(seconds / 60); + if (interval > 1) { + return interval + 'min'; + } + return Math.floor(seconds) + 's'; + } + var aDay = 24*60*60*1000; + + // Show second by second updates + window.setInterval(function() { + lastUpdated.innerHTML = 'Last checked ' + timeSince(lastUpdate) + ' ago'; + }, 1000); +</script> diff --git a/layouts/partials/meta.html b/layouts/partials/meta.html index cea1571..e3ebbf1 100644 --- a/layouts/partials/meta.html +++ b/layouts/partials/meta.html @@ -1,5 +1,5 @@ <!DOCTYPE html> -<html lang="{{ .Site.LanguageCode }}" class="nojs"> +<html lang="{{ .Site.LanguageCode }}" class="no-js"> <head> <!-- Basics --> <meta charset="utf-8"> @@ -9,13 +9,22 @@ <meta name="description" content="{{ if .Description }}{{ else }}{{ .Site.Params.description }}{{ end }}"> <title>{{ .Site.Title }}</title> <link rel="canonical" href="{{ .Permalink }}"> - <meta name="theme-color" content="#000000"> + {{ .Hugo.Generator }} + <meta name="theme-color" content="#{{ .Site.Params.ok }}"> + <script> + var themeOkColor = '#{{ .Site.Params.ok }}'; + var themeNoticeColor = '#{{ .Site.Params.notice }}'; + var themeDisruptedColor = '#{{ .Site.Params.disrupted }}'; + var themeDownColor = '#{{ .Site.Params.down }}'; + </script> <!-- Sources --> <style> /** * Color palette * + * Default: * white: #fff; + * whitesmoke: #f5f5f5; * forestgreen: #228B22; * crimson: #DC143C; * darkorange: #FF8C00; @@ -27,29 +36,49 @@ margin: 0; background: #fff; color: #444; - font: 16px sans-serif; + font: 100%/1.5 BlinkMacSystemFont, -apple-system, 'San Francisco Text', Helvetica, Arial, sans-serif; + box-sizing: border-box; + } + + *, *:before, *:after { + box-sizing: inherit; + } + + hr { + border: 0; + border-bottom: 1px solid #dedede; + margin: 24px 0; } a { text-decoration: none; - border-bottom: 0.2px dotted currentColor; + color: #000; + border-bottom: 1px dotted currentColor; } + a.no-underline { border-bottom: 0; } + a:hover { border-bottom-style: solid; } + a:active { position: relative; top: 2px; } + + h2 { font-size: 26px; } + h3 { font-size: 20px; } + h4 { font-size: 18px; } - h1, h2, h4 { + h2, h4 { font-weight: normal; color: #000; } h3 { - margin-bottom: 0; + margin: 0; color: #000; } - h4 { - margin-top: 0; - color: #999; + .date { + margin-bottom: 0; + color: #666; + font-variant: small-caps; } .faded { @@ -58,12 +87,17 @@ .header { padding: 16px; + } + + .header a { color: #fff; + font-size: 24px; + font-variant: small-caps; } img { - height: 16px; - padding: 4px; + width: 100%; + height: auto; } .contain { @@ -72,96 +106,79 @@ padding: 16px; } - .summary { - border: 2px solid #fff; + .contain--more { + max-width: 480px; } - .summary .tldr { + .center { text-align: center; } + .padding { padding: 12px; } + + .summary { padding: 16px; color: #fff; } - .components { - border: 2px solid #ccc; - border-bottom: 0; + .summary__date { + display: block; + cursor: pointer; } - .component { - color: #000; - padding: 16px; - border-bottom: 2px solid #ccc; + .summary__date:hover { + color: #{{ .Site.Params.faded }}; } - .ping.testing { - color: #dcdcdc; - font-style: italic; + .summary__date:hover:after { + content: ' ⟳ '; + color: #fff; } - .ping.done { - color: #aaa; + .components { + border: 2px solid #{{ .Site.Params.border }}; + border-bottom: 0; } - .error { - color: #DC143C; + .component { + color: #000; + padding: 16px; + border-bottom: 2px solid #{{ .Site.Params.border }}; } - /* .component:before { - content: '[+]'; - opacity: 0.4; - cursor: pointer; - } */ + .ok { color: #{{ .Site.Params.ok }}; } + .error { color: #{{ .Site.Params.down }}; } - .close { - float: right; + .footer { + margin-top: 64px; + padding: 20px 0; + background: #f5f5f5; } - .subscribe, .status { + .copyright { display: block; + font-variant: small-caps; } - .subscribe { - margin: 8px 0; - } - - button, .close { - cursor: pointer; - } - - .subscriber-box.active { - display: block; + .copyright a, a.logo { + border-bottom: 0; } - .subscriber-box { - display: none; - background: #fff; - position: fixed; + .logo img { + height: auto; width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: 1; - overflow-y: scroll; } - .subscriber-box--header { - color: #000; - border-bottom: 1px solid #aaa; - } - - .footer { - padding: 64px; - background: #F0F0F0; - text-align: center; + .logo--small img { + height: 32px; + width: auto; } - .copyright { - display: block; - font-variant: small-caps; - text-align: center; - } + .article h3 a { border: 0; } - .copyright a, a.logo { - border-bottom: 0; + .marker { + text-transform: uppercase; + font-size: smaller; + padding: 2px 8px; + padding-right: 4px; + border: 2px solid #{{ .Site.Params.border }}; } @@ -169,36 +186,35 @@ * Specific to the status */ - .header.ok, .tldr.ok { background: #228B22; } - .header.disrupted, .tldr.disrupted { background: #FF8C00; } - .header.down, .tldr.down { background: #DC143C; } - .header.notice, .tldr.notice { background: #708090; } - - .summary.ok { border-color: #228B22; } - .summary.disrupted { border-color: #FF8C00; } - .summary.down { border-color: #DC143C; } - .summary.notice { border-color: #708090; } + .status-ok .summary, .status-ok .header { background: #{{ .Site.Params.ok }}; } + .status-disrupted .summary, .status-disrupted .header { background: #{{ .Site.Params.disrupted }}; } + .status-down .summary, .status-down .header { background: #{{ .Site.Params.down }}; } + .status-notice .summary, .status-notice .header { background: #{{ .Site.Params.notice }}; } + .marker-ok { background: #{{ .Site.Params.ok }}; } + .marker-disrupted { background: #{{ .Site.Params.disrupted }}; } + .marker-down { background: #{{ .Site.Params.down }}; } + .marker-notice { background: #{{ .Site.Params.notice }}; } /** * Dynamically show individual component statuses */ - .component:after { float: right; } - .component[data-status="ok"]:after { content: "Operational"; color: #228B22; } - .component[data-status="disrupted"]:after { content: "Disrupted"; color: #FF8C00; } - .component[data-status="down"]:after { content: "Down"; color: #DC143C; } - .component[data-status="notice"]:after { content: "Maintenance"; color: #708090; } + .component-status { float: right; } + .component[data-status="ok"] .component-status { color: #{{ .Site.Params.ok }};; } + .component[data-status="disrupted"] .component-status { color: #{{ .Site.Params.disrupted }};; } + .component[data-status="down"] .component-status { color: #{{ .Site.Params.down }};; } + .component[data-status="notice"] .component-status { color: #{{ .Site.Params.notice }};; } /** - * Extend to desktops + * Responsiveness */ - @media (min-width: 560px) { - .status, .subscribe { + @media (min-width: 640px) { + .summary__date { float: right; - display: inline-block; + display: inline; } } </style> diff --git a/layouts/shortcodes/track.html b/layouts/shortcodes/track.html new file mode 100644 index 0000000..0c0c67f --- /dev/null +++ b/layouts/shortcodes/track.html @@ -0,0 +1 @@ +<p class="faded">{{ dateFormat "02 Jan 2006 15:04" (.Get 0) }}</p> diff --git a/public/index.xml b/public/index.xml deleted file mode 100644 index d2fbbc0..0000000 --- a/public/index.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> - <channel> - <title>Polar Galaxy Status</title> - <link>https://status.polargalaxy.com/index.xml</link> - <description>Recent content on Polar Galaxy Status</description> - <generator>Hugo -- gohugo.io</generator> - <language>en-us</language> - <lastBuildDate>Tue, 04 Apr 2017 00:00:00 +0000</lastBuildDate> - <atom:link href="https://status.polargalaxy.com/index.xml" rel="self" type="application/rss+xml" /> - - <item> - <title>Catastrophic DNS failure</title> - <link>https://status.polargalaxy.com/post/dns-catastrophy/</link> - <pubDate>Tue, 04 Apr 2017 00:00:00 +0000</pubDate> - - <guid>https://status.polargalaxy.com/post/dns-catastrophy/</guid> - <description><p>A DNS change of some sort occurred that removed critical metadata after the server move. We switched name servers—which takes a while—but this should ensure smoother sailing forward. There is nothing else we can do at the moment—however—you can join the server with this IP for the time being: pgmc.mcs4.me</p> -</description> - </item> - - </channel> -</rss>
\ No newline at end of file diff --git a/public/post/index.xml b/public/post/index.xml deleted file mode 100644 index 0e18ee8..0000000 --- a/public/post/index.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> - <channel> - <title>Posts on Polar Galaxy Status</title> - <link>https://status.polargalaxy.com/post/index.xml</link> - <description>Recent content in Posts on Polar Galaxy Status</description> - <generator>Hugo -- gohugo.io</generator> - <language>en-us</language> - <lastBuildDate>Tue, 04 Apr 2017 00:00:00 +0000</lastBuildDate> - <atom:link href="https://status.polargalaxy.com/post/index.xml" rel="self" type="application/rss+xml" /> - - <item> - <title>Catastrophic DNS failure</title> - <link>https://status.polargalaxy.com/post/dns-catastrophy/</link> - <pubDate>Tue, 04 Apr 2017 00:00:00 +0000</pubDate> - - <guid>https://status.polargalaxy.com/post/dns-catastrophy/</guid> - <description><p>A DNS change of some sort occurred that removed critical metadata after the server move. We switched name servers—which takes a while—but this should ensure smoother sailing forward. There is nothing else we can do at the moment—however—you can join the server with this IP for the time being: pgmc.mcs4.me</p> -</description> - </item> - - </channel> -</rss>
\ No newline at end of file diff --git a/public/sitemap.xml b/public/sitemap.xml deleted file mode 100644 index 75dcb7e..0000000 --- a/public/sitemap.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> - - <url> - <loc>https://status.polargalaxy.com/post/dns-catastrophy/</loc> - <lastmod>2017-04-04T00:00:00+00:00</lastmod> - </url> - - <url> - <loc>https://status.polargalaxy.com/</loc> - <lastmod>2017-04-04T00:00:00+00:00</lastmod> - <priority>0</priority> - </url> - - <url> - <loc>https://status.polargalaxy.com/post/</loc> - <lastmod>2017-04-04T00:00:00+00:00</lastmod> - <priority>0</priority> - </url> - -</urlset>
\ No newline at end of file diff --git a/static/admin/config.yml b/static/admin/config.yml new file mode 100644 index 0000000..487d969 --- /dev/null +++ b/static/admin/config.yml @@ -0,0 +1,38 @@ +# This is the main configuration file for Netlify CMS. +# Netlify CMS is used as the admin panel for cState. +# +# While some settings here can be kept as is, you may +# want to change some for a more customized solution. +# +# You can look at what each option does and how to +# toggle between them on the Netlify CMS repo docs: +# +# https://github.com/netlify/netlify-cms/blob/master/docs/quick-start.md + +backend: + name: git-gateway + branch: master + media_folder: "static/admin/img" + public_folder: "/admin/img" + # Do not change this! + collections: + # Used in routes, e.g., /admin/collections/blog + - name: "issue" + # Used in the UI + label: "Issues" + # The path to the folder where the documents are stored + folder: "content/issues" + # Allow users to create new documents in this collection + create: true + # Filename template, e.g., YYYY-MM-DD-title.md + slug: "{{slug}}.md" + # The fields for each document, usually in front matter + fields: + - {label: "Section", name: "Section", widget: "hidden", default: "issue"} + - {label: "Title", name: "Title", widget: "string"} + - {label: "Description", name: "Description", widget: "string"} + - {label: "Date", name: "Date", widget: "datetime"} + - {label: "Resolved?", name: "Resolved", widget: "boolean"} + - {label: "Severity", name: "Severity", widget: "string"} + - {label: "Affected", name: "Affected", widget: "string"} + - {label: "Body", name: "body", widget: "markdown"} diff --git a/static/admin/index.html b/static/admin/index.html new file mode 100644 index 0000000..ea5090b --- /dev/null +++ b/static/admin/index.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>cState Admin</title> + <link rel="stylesheet" href="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.css" /> +</head> +<body> + <!-- + To easily manage your status on Netlify, + much like on WordPress, you need to keep + this directory. Platforms which do not + support Netlify CMS do not need this + /admin/ directory. + --> + + <script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script> +</body> +</html> diff --git a/static/favicon-16x16.png b/static/favicon-16x16.png Binary files differnew file mode 100644 index 0000000..9dcdd35 --- /dev/null +++ b/static/favicon-16x16.png diff --git a/static/favicon-32x32.png b/static/favicon-32x32.png Binary files differnew file mode 100644 index 0000000..9037e59 --- /dev/null +++ b/static/favicon-32x32.png diff --git a/static/favicon.ico b/static/favicon.ico Binary files differindex b2bf69e..f335785 100644 --- a/static/favicon.ico +++ b/static/favicon.ico diff --git a/static/logo.png b/static/logo.png Binary files differdeleted file mode 100644 index fc914e5..0000000 --- a/static/logo.png +++ /dev/null diff --git a/theme.toml b/theme.toml new file mode 100644 index 0000000..f6623e8 --- /dev/null +++ b/theme.toml @@ -0,0 +1,11 @@ +name = "cstate" +license = "MIT" +licenselink = "https://github.com/mistermantas/cstate/blob/master/LICENSE.md" +description = "The best free status page on the market." +homepage = "https://github.com/mistermantas/cstate" +tags = ['hugo', 'netlify', 'status', 'statuspage', 'fast', 'light', 'ie8', 'ie9', 'ie10', 'ie11', 'github', 'github-pages', 'gh-pages', 'serverside', 'serverless', 'no-javascript', 'github-page', 'netlify-cms', 'gh-pages'] +min_version = "0.28" + +[author] + name = "mistermantas" + homepage = "https://github.com/mistermantas" |