Compare commits

...

26 Commits
0.0.1 ... 0.2.0

Author SHA1 Message Date
fd030640ed Version 0.2.0 2022-01-08 15:41:43 +00:00
47b1cd36b5 Lint cleanup 2022-01-08 15:35:59 +00:00
243c2aa3fd Use client side rendering 2022-01-08 15:25:19 +00:00
a9414663e0 Format the CSS 2021-12-24 13:01:45 +00:00
47fb1b013e Add preview colours for Nord 2021-12-24 13:00:12 +00:00
04646d23b8 Add "Nord" style 2021-12-24 12:56:15 +00:00
2daacd3bde Strip down templates and JS 2021-12-24 12:51:28 +00:00
04617350d8 Add support for tagging Apps/Bookmarks 2021-12-24 12:51:18 +00:00
96224f8a70 Chart 0.1.2 2021-12-24 07:12:09 +00:00
4c6234e2db Version 0.1.2 2021-12-24 07:10:25 +00:00
99a7dfde6e Add basic API endpoints 2021-12-24 07:02:25 +00:00
17dadcc39e Add a subtext to the greeting 2021-12-24 06:57:31 +00:00
4f0d4e0313 Make icons clickable 2021-12-24 06:52:28 +00:00
30543409e0 Make the date format user configurable 2021-12-24 06:49:37 +00:00
0fdfe811f8 Version 0.1.1 2021-12-24 06:35:45 +00:00
5a45c4c0d4 Fix k8s applications name sorting 2021-12-24 06:35:31 +00:00
67b5acddc5 Bump chart version 2021-12-24 06:29:11 +00:00
90737c3b1b Version 0.1.0 2021-12-24 06:24:05 +00:00
1c4e8a524f Switch to Alpine docker base 2021-12-24 06:20:35 +00:00
5c2c66290a Bump common chart version 2021-12-24 06:16:54 +00:00
bd9a1c1570 Fix RBAC 2021-12-24 06:16:15 +00:00
9c01a9342f Only release the chart on Chart.yaml changes 2021-12-24 06:16:05 +00:00
5aa7734a9b Cleanup and greetings 2021-12-24 06:15:47 +00:00
82312c56d6 Full packaging setup 2021-12-24 06:15:19 +00:00
4bee0d3fd8 Always pull the image while using latest 2021-12-23 18:25:49 +00:00
1592d49ed7 Load in-cluster config when detected 2021-12-23 18:24:43 +00:00
24 changed files with 796 additions and 244 deletions

View File

@@ -1,11 +1,11 @@
name: release chart
name: Release Helm Chart
"on":
push:
branches:
- main
paths:
- "charts/ohayodash/**"
- "charts/**/Chart.yaml"
jobs:
release:

3
.gitignore vendored
View File

@@ -150,3 +150,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
charts/**/charts/*
Chart.lock

View File

@@ -1,4 +1,4 @@
FROM python:3.9
FROM python:3.9-alpine
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright 2021 Andrew Williams <andy@tensixtyone.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,3 +1,5 @@
# Ohayo Dash
Ohayo Dash is a Kubernetes driven start page and dashboard. All configuration is done by standard Kubernetes objects and ConfigMaps.
Ohayo Dash is a Kubernetes driven start page and dashboard. All configuration is done by standard Kubernetes objects and ConfigMaps.
This is inspired by [Hajimari](https://github.com/toboshii/hajimari) and [SUI](https://github.com/jeroenpardon/sui) projects.

View File

@@ -1,14 +1,14 @@
apiVersion: v2
appVersion: 0.0.1
appVersion: 0.1.2
description: Ohayo Dash is a Kubernetes driven start page and dashboard. All configuration is done by standard Kubernetes objects and ConfigMaps.
name: ohayodash
version: 0.0.1
version: 0.1.2
kubeVersion: ">=1.16.0-0"
keywords:
- ohayodash
- startpage
- dashboard
home: https://github.com/nikdoof/ohayodash/tree/main/charts/hajimari
home: https://github.com/nikdoof/ohayodash/tree/main/charts/ohayodash
sources:
- https://github.com/nikdoof/ohayodash
maintainers:
@@ -17,4 +17,4 @@ maintainers:
dependencies:
- name: common
repository: https://library-charts.k8s-at-home.com
version: 4.0.0
version: 4.2.0

113
charts/ohayodash/README.md Normal file
View File

@@ -0,0 +1,113 @@
# ohayodash
![Version: 0.1.2](https://img.shields.io/badge/Version-0.1.2-informational?style=flat-square) ![AppVersion: 0.1.2](https://img.shields.io/badge/AppVersion-0.1.2-informational?style=flat-square)
Ohayo Dash is a Kubernetes driven start page and dashboard. All configuration is done by standard Kubernetes objects and ConfigMaps.
**This chart is not maintained by the upstream project and any issues with the chart should be raised [here](https://github.com/nikdoof/helm-charts/issues/new/choose)**
## Source Code
* <https://github.com/nikdoof/ohayodash>
## Requirements
Kubernetes: `>=1.16.0-0`
## Dependencies
| Repository | Name | Version |
|------------|------|---------|
| https://library-charts.k8s-at-home.com | common | 4.2.0 |
## TL;DR
```console
helm repo add ohayodash https://nikdoof.github.io/ohayodash/
helm repo update
helm install ohayodash ohayodash/ohayodash
```
## Installing the Chart
To install the chart with the release name `ohayodash`
```console
helm install ohayodash ohayodash/ohayodash
```
## Uninstalling the Chart
To uninstall the `ohayodash` deployment
```console
helm uninstall ohayodash
```
The command removes all the Kubernetes components associated with the chart **including persistent volumes** and deletes the release.
## Configuration
Read through the [values.yaml](./values.yaml) file. It has several commented out suggested values.
Other values may be used from the [values.yaml](https://github.com/k8s-at-home/library-charts/tree/main/charts/stable/common/values.yaml) from the [common library](https://github.com/k8s-at-home/library-charts/tree/main/charts/stable/common).
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`.
```console
helm install ohayodash \
--set env.TZ="Europe/London"\
ohayodash/ohayodash
```
Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart.
```console
helm install ohayodash ohayodash/ohayodash -f values.yaml
```
## Custom configuration
## Values
**Important**: When deploying an application Helm chart you can add more values from our common library chart [here](https://github.com/k8s-at-home/library-charts/tree/main/charts/stable/common)
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| env | object | See below | environment variables. |
| env.DATE_FORMAT | string | `"%Y-%m-%d %H:%M"` | Python date format string to use for rendering the date/time |
| env.TZ | string | `"UTC"` | Set the container timezone |
| image.pullPolicy | string | `"Always"` | image pull policy |
| image.repository | string | `"ghcr.io/nikdoof/ohayodash"` | image repository |
| image.tag | string | `"latest"` | image tag |
| ingress.main | object | See values.yaml | Enable and configure ingress settings for the chart under this key. |
| service | object | See values.yaml | Configures service settings for the chart. |
| serviceAccount | object | See below | Configures service account needed for reading k8s ingress objects |
| serviceAccount.create | bool | `true` | Create service account |
## Changelog
All notable changes to this application Helm chart will be documented in this file but does not include changes from our common library. To read those click [here](https://github.com/k8s-at-home/library-charts/tree/main/charts/stable/common#changelog).
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### [0.1.2]
#### Added
- Started the Changelog
- Pinned to 0.1.2 rather than latest
#### Changed
- N/A
#### Removed
- N/A
## Support
- Open an [issue](https://github.com/nikdoof/ohayodash/issues/new/choose)
----------------------------------------------
Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0)

View File

@@ -0,0 +1,143 @@
{{- define "custom.repository.organization" -}}
ohayodash
{{- end -}}
{{- define "custom.repository.url" -}}
https://github.com/nikdoof/ohayodash
{{- end -}}
{{- define "custom.helm.url" -}}
https://nikdoof.github.io/ohayodash/
{{- end -}}
{{- define "custom.helm.path" -}}
{{ template "custom.repository.organization" . }}/{{ template "chart.name" . }}
{{- end -}}
{{- define "custom.notes" -}}
**This chart is not maintained by the upstream project and any issues with the chart should be raised [here](https://github.com/nikdoof/helm-charts/issues/new/choose)**
{{- end -}}
{{- define "custom.requirements" -}}
## Requirements
{{ template "chart.kubeVersionLine" . }}
{{- end -}}
{{- define "custom.dependencies" -}}
## Dependencies
{{ template "chart.requirementsTable" . }}
{{- end -}}
{{- define "custom.install.tldr" -}}
## TL;DR
```console
helm repo add {{ template "custom.repository.organization" . }} {{ template "custom.helm.url" . }}
helm repo update
helm install {{ template "chart.name" . }} {{ template "custom.helm.path" . }}
```
{{- end -}}
{{- define "custom.install" -}}
## Installing the Chart
To install the chart with the release name `{{ template "chart.name" . }}`
```console
helm install {{ template "chart.name" . }} {{ template "custom.helm.path" . }}
```
{{- end -}}
{{- define "custom.uninstall" -}}
## Uninstalling the Chart
To uninstall the `{{ template "chart.name" . }}` deployment
```console
helm uninstall {{ template "chart.name" . }}
```
The command removes all the Kubernetes components associated with the chart **including persistent volumes** and deletes the release.
{{- end -}}
{{- define "custom.configuration.header" -}}
## Configuration
{{- end -}}
{{- define "custom.configuration.readValues" -}}
Read through the [values.yaml](./values.yaml) file. It has several commented out suggested values.
Other values may be used from the [values.yaml](https://github.com/k8s-at-home/library-charts/tree/main/charts/stable/common/values.yaml) from the [common library](https://github.com/k8s-at-home/library-charts/tree/main/charts/stable/common).
{{- end -}}
{{- define "custom.configuration.example.set" -}}
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`.
```console
helm install {{ template "chart.name" . }} \
--set env.TZ="Europe/London"\
{{ template "custom.helm.path" . }}
```
{{- end -}}
{{- define "custom.configuration.example.file" -}}
Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart.
```console
helm install {{ template "chart.name" . }} {{ template "custom.helm.path" . }} -f values.yaml
```
{{- end -}}
{{- define "custom.valuesSection" -}}
## Values
**Important**: When deploying an application Helm chart you can add more values from our common library chart [here](https://github.com/k8s-at-home/library-charts/tree/main/charts/stable/common)
{{ template "chart.valuesTable" . }}
{{- end -}}
{{- define "custom.support" -}}
## Support
- Open an [issue](https://github.com/nikdoof/ohayodash/issues/new/choose)
{{- end -}}
{{ template "chart.header" . }}
{{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }}
{{ template "chart.description" . }}
{{ template "custom.notes" . }}
{{ template "chart.sourcesSection" . }}
{{ template "custom.requirements" . }}
{{ template "custom.dependencies" . }}
{{ template "custom.install.tldr" . }}
{{ template "custom.install" . }}
{{ template "custom.uninstall" . }}
{{ template "custom.configuration.header" . }}
{{ template "custom.configuration.readValues" . }}
{{ template "custom.configuration.example.set" . }}
{{ template "custom.configuration.example.file" . }}
{{ template "custom.custom.configuration" . }}
{{ template "custom.valuesSection" . }}
{{ template "custom.changelog" . }}
{{ template "custom.support" . }}
{{ template "helm-docs.versionFooter" . }}
{{ "" }}

View File

@@ -0,0 +1,27 @@
{{- define "custom.changelog.header" -}}
## Changelog
{{- end -}}
{{- define "custom.changelog" -}}
{{ template "custom.changelog.header" . }}
All notable changes to this application Helm chart will be documented in this file but does not include changes from our common library. To read those click [here](https://github.com/k8s-at-home/library-charts/tree/main/charts/stable/common#changelog).
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### [0.1.2]
#### Added
- Started the Changelog
- Pinned to 0.1.2 rather than latest
#### Changed
- N/A
#### Removed
- N/A
{{- end -}}

View File

@@ -0,0 +1,7 @@
{{- define "custom.custom.configuration.header" -}}
## Custom configuration
{{- end -}}
{{- define "custom.custom.configuration" -}}
{{ template "custom.custom.configuration.header" . }}
{{- end -}}

View File

@@ -6,7 +6,7 @@ metadata:
labels: {{- include "common.labels" . | nindent 4 }}
rules:
- apiGroups: ["", "extensions", "networking.k8s.io"]
resources: ["ingresses", "namespaces"]
resources: ["ingresses", "namespaces", "configmaps"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1

View File

@@ -11,13 +11,15 @@ image:
# -- image pull policy
pullPolicy: IfNotPresent
# -- image tag
tag: latest
tag: 0.1.2
# -- environment variables.
# @default -- See below
env:
# -- Set the container timezone
TZ: UTC
# -- Python date format string to use for rendering the date/time
DATE_FORMAT: "%Y-%m-%d %H:%M"
# -- Configures service settings for the chart.
# @default -- See values.yaml

View File

@@ -1,4 +1,5 @@
from flask import Flask
from ohayodash.base import base
app = Flask(__name__)

View File

@@ -1,71 +1,145 @@
from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound
import datetime
import os
import kubernetes
import yaml
from flask import Blueprint, jsonify, render_template
ANNOTATION_BASE = 'ohayodash.github.io'
base = Blueprint('base', __name__, template_folder='templates')
def get_k8s_applications():
if 'KUBERNETES_SERVICE_HOST' in os.environ:
kubernetes.config.load_incluster_config()
else:
kubernetes.config.load_kube_config()
v1 = kubernetes.client.NetworkingV1Api()
ret = v1.list_ingress_for_all_namespaces(watch=False)
results = []
for ingress in ret.items:
# Skip if
if f'{ANNOTATION_BASE}/enable' not in ingress.metadata.annotations or \
ingress.metadata.annotations[f'{ANNOTATION_BASE}/enable'] == 'false':
def check_tags(tag, kubeobj):
# Skip if we're limited to a tag, and the CM has tags but not that one.
tags = kubeobj.metadata.annotations.get('{0}/tags'.format(ANNOTATION_BASE), '')
obj_tags = {tagname for tagname in tags.split(',') if tagname != ''}
# If its not tagged, allow
if not obj_tags:
return True
# If tag is on the object, allow
return tag in obj_tags
def get_k8s_applications(tag: str = None) -> list:
"""Get all ingresses from the cluster and produce a application list."""
api = kubernetes.client.NetworkingV1Api()
applications = []
for ingress in api.list_ingress_for_all_namespaces(watch=False).items:
# Skip if not enabled
enable_annotation = '{0}/enable'.format(ANNOTATION_BASE)
if enable_annotation not in ingress.metadata.annotations:
continue
if ingress.metadata.annotations[enable_annotation] == 'false':
continue
values = {
# Skip if we're limited to a tag, and the Ingress has tags but not that one.
if not check_tags(tag, ingress):
continue
# Set to some basic values from the ingress
application_values = {
'name': ingress.metadata.name,
'namespace': ingress.metadata.namespace,
'url': f'https://{ingress.spec.rules[0].host}',
'url': 'https://{0}'.format(ingress.spec.rules[0].host),
'show_url': False,
}
for key in ingress.metadata.annotations:
# Read annotations and override the values if defined
for key, value in ingress.metadata.annotations.items():
if key.startswith(ANNOTATION_BASE):
val = key.split('/')[1]
values[val] = ingress.metadata.annotations[key]
annotation_key = key.split('/')[1]
application_values[annotation_key] = value
results.append(values)
return sorted(results, key=lambda i: i['appName'])
applications.append(application_values)
return sorted(applications, key=lambda item: item['name'])
def get_bookmarks():
kubernetes.config.load_kube_config()
def get_bookmarks(tag: str = None) -> list:
"""Get all 'bookmark' ConfigMaps from the cluster and produce a bookmark list."""
v1 = kubernetes.client.CoreV1Api()
ret = v1.list_config_map_for_all_namespaces(watch=False)
bookmarks = {}
bookmarks = []
for cm in ret.items:
# Skip if
if not cm.metadata.annotations or f'{ANNOTATION_BASE}/bookmarks' not in cm.metadata.annotations:
# Skip if the CM has no annotations
if cm.metadata.annotations is None:
continue
data = yaml.safe_load(cm.data['bookmarks'])
for bookmark in data:
# Skip if its not tagged as bookmark CM
if '{0}/bookmarks'.format(ANNOTATION_BASE) not in cm.metadata.annotations:
continue
# Skip if we're limited to a tag, and the CM has tags but not that one.
if not check_tags(tag, cm):
continue
# Load bookmark data
bookmark_data = yaml.safe_load(cm.data['bookmarks'])
# Iterate each bookmark
for bookmark in bookmark_data:
if 'group' not in bookmark:
group = 'default'
else:
group = bookmark['group'].lower()
if group not in bookmarks:
bookmarks[group] = []
bookmarks[group].append(bookmark)
# Find category dict and append or create
for cat in bookmarks:
if cat['category'] == group:
cat['links'].append(bookmark)
break
else:
bookmarks.append({'category': group, 'links': [bookmark]})
return bookmarks
@base.route('/')
def index():
return render_template(f'index.j2', **{
'now': datetime.datetime.utcnow(),
'applications': get_k8s_applications(),
'bookmarks': get_bookmarks(),
@base.route('/<tag>/')
def index(tag=None):
return render_template('index.j2')
@base.route('/providers.json')
@base.route('/<tag>/providers.json')
def providers(tag=None):
return jsonify({
'providers': [
{'name': 'Allmusic', 'url': 'https://www.allmusic.com/search/all/', 'prefix': '/a'},
{'name': 'Discogs', 'url': 'https://www.discogs.com/search/?q=', 'prefix': '/di'},
{'name': 'Duck Duck Go', 'url': 'https://duckduckgo.com/?q=', 'prefix': '/d'},
{'name': 'iMDB', 'url': 'https://www.imdb.com/find?q=', 'prefix': '/i'},
{'name': 'TheMovieDB', 'url': 'https://www.themoviedb.org/search?query=', 'prefix': '/m'},
{'name': 'Reddit', 'url': 'https://www.reddit.com/search?q=', 'prefix': '/r'},
{'name': 'Qwant', 'url': 'https://www.qwant.com/?q=', 'prefix': '/q'},
{'name': 'Soundcloud', 'url': 'https://soundcloud.com/search?q=', 'prefix': '/so'},
{'name': 'Spotify', 'url': 'https://open.spotify.com/search/results/', 'prefix': '/s'},
{'name': 'TheTVDB', 'url': 'https://www.thetvdb.com/search?query=', 'prefix': '/tv'},
{'name': 'Trakt', 'url': 'https://trakt.tv/search?query=', 'prefix': '/t'},
],
})
@base.route('/apps.json')
@base.route('/<tag>/apps.json')
def applications(tag=None):
return jsonify({
'apps': get_k8s_applications(tag),
})
@base.route('/links.json')
@base.route('/<tag>/links.json')
def bookmarks(tag=None):
return jsonify({
'bookmarks': get_bookmarks(tag),
})

View File

@@ -1,21 +1,35 @@
html{
html {
box-sizing: border-box;
moz-box-sizing: border-box;
webkit-box-sizing: border-box;
webkit-text-size-adjust: none;
}
::-webkit-scrollbar-thumb {background: var(--color-text-acc); border: 5px solid var(--color-background); border-radius: 10px;}
::-webkit-scrollbar-track {background: var(--color-text-acc); border: 7px solid var(--color-background);}
::-webkit-scrollbar {width: 15px;}
::-webkit-scrollbar-corner { background: var(--color-background); }
::-webkit-scrollbar-thumb {
background: var(--color-text-acc);
border: 5px solid var(--color-background);
border-radius: 10px;
}
::-webkit-scrollbar-track {
background: var(--color-text-acc);
border: 7px solid var(--color-background);
}
::-webkit-scrollbar {
width: 15px;
}
::-webkit-scrollbar-corner {
background: var(--color-background);
}
html,
body{
body {
background-color: var(--color-background);
color: var(--color-text-pri);
scrollbar-color: var(--color-text-acc) var(--color-background);
scrollbar-width: thin;
scrollbar-width: thin;
font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif;
font-size: 14px;
font-weight: 400;
@@ -29,73 +43,72 @@ body{
*,
*:before,
*:after{
*:after {
box-sizing: inherit;
moz-box-sizing: inherit;
webkit-box-sizing: inherit;
}
:root{
:root {
module-spacing: 3vh;
}
/* TEXT STYLES */
h1, h2{
h1, h2 {
font-weight: 300;
margin: 0;
padding: 0;
text-align: left;
}
h2, h3, h4{
text-transform: uppercase;
h2, h3, h4 {
text-transform: uppercase;
}
h1{
h1 {
font-size: 4em;
font-weight: 700;
margin-bottom: 0.5em;
}
h2{
h2 {
font-size: 16px;
height: 30px;
}
h3{
h3 {
font-size: 20px;
font-weight: 900;
height: 10px;
}
h4{
h4 {
font-size: 1.1em;
font-weight: 400;
height: 10px;
}
a{
a {
color: var(--color-text-pri);
text-decoration: none;
}
a:hover{
a:hover {
text-decoration: underline;
webkit-text-decoration-color: var(--color-text-acc);
webkit-text-decoration-skip: true;
}
.icon{
.icon {
font-size: 2.5em;
}
/* FORMS */
input{
input {
background-color: transparent;
border: 0;
border-bottom: thin solid var(--color-text-acc);
@@ -106,19 +119,19 @@ input{
width: 100%;
}
input:focus{
input:focus {
color-border: var(--color-text-pri);
outline: none;
}
input:focus{
input:focus {
opacity: 1;
}
/* TABLES */
table{
table {
border: thin solid #e4e4e4;
border-collapse: collapse;
border-spacing: 0;
@@ -127,11 +140,11 @@ table{
width: 100%;
}
table td:nth-of-type(2){
table td:nth-of-type(2) {
padding-right: 5em;
}
table td{
table td {
border: thin solid #e4e4e4;
color: #333333;
font-size: 1em;
@@ -140,46 +153,45 @@ table td{
word-break: normal;
}
table th{
table th {
border: thin solid #e4e4e4;
color: #333333;
font-weight: bold;
padding: 10px 5px;
}
table a{
table a {
color: #333333;
}
/* ANIMATION */
.fade{
.fade {
opacity: 0;
}
@keyframes fadeseq{
100% {
@keyframes fadeseq {
100% {
opacity: 1;
}
}
.fade{
.fade {
opacity: 0;
}
.fade{
.fade {
animation: fadeseq .3s forwards;
}
.fade:nth-child(2){
.fade:nth-child(2) {
animation-delay: .4s;
}
/* LAYOUT */
#container{
#container {
align-items: stretch;
display: grid;
grid-column-gap: 20px;
@@ -193,16 +205,14 @@ table a{
width: 60%;
}
/* SECTIONS */
#header{
#header {
border-bottom: 0px solid var(--color-text-acc);
z-index: 1;
}
#apps_loop{
#apps_loop {
border-bottom: 0px solid var(--color-text-acc);
display: grid;
grid-column-gap: 0px;
@@ -212,18 +222,18 @@ table a{
padding-bottom: var(--module-spacing);
}
.apps_icon{
.apps_icon {
height: 64px;
margin-right: 1em;
padding-top: 15px;
}
.apps_icon span{
.apps_icon span {
font-size: 2.5em;
line-height: 3rem;
}
.apps_item{
.apps_item {
display: flex;
flex-direction: row;
flex-wrap: wrap;
@@ -231,7 +241,7 @@ table a{
margin: 0;
}
.apps_text{
.apps_text {
display: flex;
flex-direction: column;
justify-content: center;
@@ -239,20 +249,20 @@ table a{
overflow: hidden;
}
.apps_text a{
.apps_text a {
font-size: 1em;
font-weight: 500;
text-transform: uppercase;
}
.apps_text span{
.apps_text span {
color: var(--color-text-acc);
font-size: 0.8em;
text-transform: uppercase;
}
#links_loop{
#links_loop {
display: grid;
flex-wrap: nowrap;
grid-column-gap: 20px;
@@ -261,39 +271,24 @@ table a{
grid-template-rows: auto;
}
#links_item{
#links_item {
line-height: 1.5rem;
margin-bottom: 2em;
webkit-font-smoothing: antialiased;
}
#links_item h4{
#links_item h4 {
color: var(--color-text-acc);
}
#links_item a{
#links_item a {
display: block;
line-height: 2;
}
/* MODAL */
#modal{
#modal {
overflow-y: auto;
bottom: 0;
left: 0;
@@ -306,79 +301,79 @@ table a{
z-index: 20;
}
#modal:target{
#modal:target {
opacity: 1;
pointer-events: auto;
}
#modal>div{
#modal>div {
background-color: #ffffff;
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.25);
margin-left: auto;
margin-right: auto;
margin-right: auto;
padding: 2em;
margin-top: 5vh;
width: 50%;
display: flex;
flex-direction: column;
display: flex;
flex-direction: column;
}
#modal h1{
#modal h1 {
color: #333333;
font-size: 2em;
}
#modal h2{
margin-top:1.5em;
#modal h2 {
margin-top: 1.5em;
}
#modal-header{
display:flex;
justify-content: space-between;
#modal-header {
display: flex;
justify-content: space-between;
}
#modal-footer{
display:flex;
font-size:2em;
justify-content: flex-start;
#modal-footer {
display: flex;
font-size: 2em;
justify-content: flex-start;
}
#modal-footer a{
margin-right:0.25em;
color:rgba(0, 0, 0, 0.35)
#modal-footer a {
margin-right: 0.25em;
color: rgba(0, 0, 0, 0.35)
}
.modal-close{
.modal-close {
color: #000000;
font-size: 1.5em;
text-align: center;
text-decoration: none;
}
.modal-close:hover{
.modal-close:hover {
color: #000;
}
#modal_init a{
#modal_init a {
bottom: 1vh;
color: var(--color-text-acc);
left: 1vw;
position: fixed;
}
#modal_init a:hover{
#modal_init a:hover {
color: var(--color-text-pri);
}
#modal-theme{
#modal-theme {
border-bottom: 0px solid var(--color-text-acc);
display: flex;
flex-wrap: wrap;
margin-bottom: 2em;
margin-bottom: 2em;
}
#providers{
margin-bottom: 2em;
#providers {
margin-bottom: 2em;
}
#editor {
@@ -419,88 +414,92 @@ table a{
/* THEMING */
.theme-button{
.theme-button {
font-size: 0.8em;
margin: 2px;
width:128px;
width: 128px;
line-height: 3em;
text-align: center;
text-transform: uppercase;
}
.theme-blackboard{
.theme-blackboard {
background-color: #000000;
border: 4px solid #5c5c5c;
color: #FFFDEA;
color: #FFFDEA;
}
.theme-gazette{
.theme-gazette {
background-color: #F2F7FF;
border: 4px solid #5c5c5c;
color: #000000;
color: #000000;
}
.theme-espresso{
.theme-espresso {
background-color: #21211F;
border: 4px solid #4E4E4E;
color: #D1B59A;
color: #D1B59A;
}
.theme-cab{
.theme-cab {
background-color: #FEED01;
border: 4px solid #424242;
color: #1F1F1F;
color: #1F1F1F;
}
.theme-cloud{
.theme-cloud {
background-color: #f1f2f0;
border: 4px solid #35342f;
color: #37bbe4;
color: #37bbe4;
}
.theme-lime{
.theme-lime {
background-color: #263238;
border: 4px solid #AABBC3;
color: #aeea00;
color: #aeea00;
}
.theme-passion{
.theme-passion {
background-color: #f5f5f5;
border: 4px solid #8e24aa;
color: #12005e;
color: #12005e;
}
.theme-blues{
.theme-blues {
background-color: #2B2C56;
border: 4px solid #6677EB;
color: #EFF1FC;
color: #EFF1FC;
}
.theme-chalk{
.theme-chalk {
background-color: #263238;
border: 4px solid #FF869A;
color: #AABBC3;
color: #AABBC3;
}
.theme-tron{
.theme-tron {
background-color: #242B33;
border: 4px solid #6EE2FF;
color: #EFFBFF;
color: #EFFBFF;
}
.theme-paper{
.theme-paper {
background-color: #F8F6F1;
border: 4px solid #F5E1A4;
color: #4C432E;
color: #4C432E;
}
.theme-nord {
background-color: #2E3440;
border: 4px solid #8FBCBB;
color: #E5E9F0;
}
/* MEDIA QUERIES */
@media screen and (max-width: 1260px)
{
#container
{
@media screen and (max-width: 1260px) {
#container {
align-items: stretch;
display: grid;
grid-column-gap: 10px;
@@ -513,31 +512,30 @@ table a{
margin-right: auto;
width: 90%;
}
#apps_loop{
#apps_loop {
grid-template-columns: 1fr 1fr 1fr;
width: 90vw;
}
#links_loop {
#links_loop {
grid-template-columns: 1fr 1fr 1fr;
}
#modal>div{
margin-left: auto;
margin-right: auto;
margin-top: 5vh;
width: 90%;
}
#modal>div {
margin-left: auto;
margin-right: auto;
margin-top: 5vh;
width: 90%;
}
}
@media screen and (max-width: 667px)
{
html{
@media screen and (max-width: 667px) {
html {
font-size: calc(16px + 6 * ((100vw - 320px) / 680));
}
#container{
#container {
align-items: stretch;
display: grid;
grid-column-gap: 20px;
@@ -549,41 +547,41 @@ table a{
width: 90%;
}
h1{
h1 {
font-size: 4em;
height: auto;
margin-bottom: 0em;
}
h2{
h2 {
font-size: 1em;
height: auto;
margin-bottom: 0em;
}
h3{
h3 {
font-size: 1em;
}
#apps_loop{
#apps_loop {
grid-column-gap: 0px;
grid-row-gap: 0px;
grid-template-columns: 1fr 1fr;
width: 90vw;
}
.apps_icon{
.apps_icon {
height: 64px;
margin-right: 0.8em;
padding-top: 14px;
}
.apps_icon span{
.apps_icon span {
font-size: 2em;
line-height: 2.5rem;
}
#links_loop{
#links_loop {
display: grid;
flex-wrap: nowrap;
grid-column-gap: 20px;
@@ -598,4 +596,4 @@ table a{
#app-address {
display: none;
}
}
}

View File

@@ -0,0 +1,16 @@
function fetchAndRender (name) {
fetch(name + '.json')
.then(response => response.json())
.then(data => {
const mysource = document.getElementById(name + '-template').innerHTML;
const mytemplate = Handlebars.compile(mysource);
const myresult = mytemplate(data);
document.getElementById(name).innerHTML = myresult;
});
}
document.addEventListener('DOMContentLoaded', () => {
fetchAndRender('apps');
fetchAndRender('links');
fetchAndRender('providers');
});

View File

@@ -0,0 +1,38 @@
function date() {
let currentDate = new Date();
let dateOptions = {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
};
let date = currentDate.toLocaleDateString("en-GB", dateOptions);
document.getElementById("header_date").innerHTML = date;
}
function greet() {
let currentTime = new Date();
let greet = Math.floor(currentTime.getHours() / 6);
let greeting = "こんにちは!";
switch (greet) {
case 0:
greeting = "おやすみなさい!";
break;
case 1:
greeting = "おはようございます!";
break;
case 2:
greeting = "こんにちは!";
break;
case 3:
greeting = "こんばんは!";
break;
}
document.getElementById("header_greet").innerHTML = greeting;
document.title = greeting;
}
function loadFunctions() {
date();
greet();
}

View File

@@ -1,5 +1,6 @@
var sindex = 0;
var cycle = false;
var sengine = "https://www.google.com/?q="; // Default search engine
function start() {
var query = getParameterByName('q');
@@ -20,6 +21,29 @@ function handleKeyPress(e) {
if (key == 13) { // Search functions
search(text);
}
if (key == 9) { // Tab Completion Functions
e.preventDefault();
e.stopPropagation();
if (text[0] === ';') {
switch (option) {
case 't':
var streamers = ['admiralbahroo', 'moonmoon_ow', 'witwix'];
if (!subtext || cycle) {
cycle = true;
if (sindex > streamers.length - 1) sindex = 0;
document.getElementById("keywords").value = ';t ' + streamers[sindex++];
return;
}
for (var streamer of streamers) {
if (subtext === streamer.substr(0, subtext.length)) {
document.getElementById("keywords").value = ';t ' + streamer;
return;
}
}
break;
}
}
}
if(key == 32){ //Space to go to search
document.getElementById("keywords").focus();
}
@@ -33,18 +57,45 @@ function search(text) {
if (text[0] === '/') {
if (text.indexOf(' ') > -1) {
switch (option) {
case "am":
window.location = "https://www.allmusic.com/search/all/" + subtext;
break;
case "d":
window.location = "https://duckduckgo.com/?q=" + subtext;
break;
case "di":
window.location = "https://www.discogs.com/search/?q=" + subtext;
break;
case "i":
window.location = "https://www.imdb.com/find?q=" + subtext;
break;
case "m":
window.location = "https://www.themoviedb.org/search?query=" + subtext;
break;
case "r":
window.location = "https://www.reddit.com/search?q=" + subtext;
break;
case "q":
window.location = "https://www.qwant.com/?q=" + subtext;
break;
case "so":
window.location = "https://soundcloud.com/search?q=" + subtext;
break;
case "s":
window.location = "https://open.spotify.com/search/results/" + subtext;
break;
case "t":
window.location = "https://trakt.tv/search?query=" + subtext;
break;
case "tv":
window.location = "https://www.thetvdb.com/search?query=" + subtext;
break;
case "y":
window.location = "https://www.youtube.com/results?search_query=" + subtext;
break;
case "g":
window.location = "https://www.google.com/?q=" + subtext;
break;
}
} else {
var option = text.substr(1);
@@ -69,7 +120,7 @@ function search(text) {
else
window.location = "https://" + text;
} else {
window.location = "https://www.google.com/search?q=" + text;
window.location = sengine + text;
}
}
@@ -92,4 +143,4 @@ function containsProtocol(str) {
String.prototype.replaceAll = function(search, replacement) {
var target = this;
return target.split(search).join(replacement);
};
};

View File

@@ -101,7 +101,7 @@ for (let i = 0; i < dataThemeButtons.length; i++) {
'color-text-acc': '#6EE2FF'
});
return;
case 'blues':
setTheme({
'color-background': '#2B2C56',
@@ -109,7 +109,7 @@ for (let i = 0; i < dataThemeButtons.length; i++) {
'color-text-acc': '#6677EB'
});
return;
case 'passion':
setTheme({
'color-background': '#f5f5f5',
@@ -117,7 +117,7 @@ for (let i = 0; i < dataThemeButtons.length; i++) {
'color-text-acc': '#8e24aa'
});
return;
case 'chalk':
setTheme({
'color-background': '#263238',
@@ -125,7 +125,7 @@ for (let i = 0; i < dataThemeButtons.length; i++) {
'color-text-acc': '#FF869A'
});
return;
case 'paper':
setTheme({
'color-background': '#F8F6F1',
@@ -134,6 +134,15 @@ for (let i = 0; i < dataThemeButtons.length; i++) {
});
return;
case 'nord':
setTheme({
'color-background': '#2E3440',
'color-text-pri': '#E5E9F0',
'color-text-acc': '#8FBCBB'
});
return;
}
})
}

View File

@@ -2,21 +2,19 @@
<html lang="en">
<head>
<title>{{ title | default("Hajimari") }}</title>
<title>おはよう!</title>
<meta charset="utf-8">
<meta name="description" content="a startpage for your server and / or new tab page">
<meta http-equiv="Default-Style" content="">
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" />
<link type="text/css" rel="stylesheet" href="./static/css/styles.css" media="screen,projection" />
<link type="text/css" rel="stylesheet" href="/static/css/styles.css" media="screen,projection" />
<link href="//fonts.googleapis.com/css?family=Roboto:400,500,700,900" rel="stylesheet">
<link href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/codemirror.min.css" rel="stylesheet" type="text/css">
<link href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/addon/lint/lint.min.css" rel="stylesheet" type="text/css">
<link href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/theme/dracula.min.css" rel="stylesheet" type="text/css">
<link href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/addon/fold/foldgutter.min.css" rel="stylesheet" type="text/css">
<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"></script>
<script src="//code.iconify.design/1/1.0.7/iconify.min.js"></script>
</head>
<body>
<body onload="loadFunctions()">
<section id="modal">
<div>
<header id="modal-header">
@@ -30,68 +28,98 @@
<div id="modal-theme">
<button data-theme="blackboard" class="theme-button theme-blackboard">Blackboard</button>
<button data-theme="gazette" class="theme-button theme-gazette">Gazette</button>
<button data-theme="espresso" class="theme-button theme-espresso">Espresso</button>
<button data-theme="cab" class="theme-button theme-cab">Cab</button>
<button data-theme="cloud" class="theme-button theme-cloud">Cloud</button>
<button data-theme="lime" class="theme-button theme-lime">Lime</button>
<button data-theme="passion" class="theme-button theme-passion">Passion</button>
<button data-theme="blues" class="theme-button theme-blues">Blues</button>
<button data-theme="cab" class="theme-button theme-cab">Cab</button>
<button data-theme="chalk" class="theme-button theme-chalk">Chalk</button>
<button data-theme="tron" class="theme-button theme-tron">Tron</button>
<button data-theme="cloud" class="theme-button theme-cloud">Cloud</button>
<button data-theme="espresso" class="theme-button theme-espresso">Espresso</button>
<button data-theme="gazette" class="theme-button theme-gazette">Gazette</button>
<button data-theme="lime" class="theme-button theme-lime">Lime</button>
<button data-theme="nord" class="theme-button theme-nord">Nord</button>
<button data-theme="paper" class="theme-button theme-paper">Paper</button>
<button data-theme="passion" class="theme-button theme-passion">Passion</button>
<button data-theme="tron" class="theme-button theme-tron">Tron</button>
</div>
<h2>Search options</h2>
<section id="providers">
{% raw %}
<script type="text/handlebars-template" id="providers-template">
<table>
<tr>
<th>Website</th>
<th>Prefix</th>
</tr>
{{#providers}}
<tr>
<td><a href="{{url}}">{{name}}</a></td>
<td>{{prefix}}</td>
</tr>
{{/providers}}
</table>
</script>
{% endraw %}
</section>
<header id="modal-footer">
<a href="https://github.com/nikdoof/ohayodash/"><span class="iconify" data-icon="mdi-github-box"></span></a>
<a href="https://materialdesignicons.com/"><span class="iconify"
data-icon="mdi-material-design"></span></a>
</header>
</div>
</section>
<main id="container" class="fade">
<section id="search">
<input name="keywords" type="text" id="keywords" size="50" spellcheck="false" autofocus="true"
onkeydown="handleKeyPress(event)">
</section>
<section id="header">
<h1>{{ greeting | default("Welcome")}}</h1>
<h2>{{ now }}</h2>
<h2 id="header_date"></h2>
<h1 id="header_greet"></h1>
</section>
{% if applications %}
<section id="apps">
{% raw %}
<script type="text/handlebars-template" id="apps-template">
<h3>Applications</h3>
<div id="apps_loop">
{% for app in applications %}
<div class="apps_item">
<div class="apps_icon">
<span class="iconify icon" data-icon="mdi-{{ app.icon | default("application") }}"></span>
{{#apps}}
<div class="apps_item">
<div class="apps_icon">
<span class="iconify icon" data-icon="mdi-{{icon}}"></span>
</div>
<div class="apps_text">
<a href="http://{{url}}" {{#if target}}target="{{target}}"{{/if}} >{{name}}</a>
{{#if show_url}}<span id="app-address">{{url}}</span>{{/if}}
</div>
</div>
<div class="apps_text">
<a href="{{ app.url }}">{{ app.name }}</a>
{% if app.show_url %}
<span id="app-address">{{ app.url }}</span>
{% endif %}
</div>
</div>
{% endfor %}
{{/apps}}
</div>
</script>
{% endraw %}
</section>
{% endif %}
{% if bookmarks %}
<section id="links">
{% raw %}
<script type="text/handlebars-template" id="links-template">
<h3>Bookmarks</h3>
<div id="links_loop">
{% for group, value in bookmarks|dictsort %}
<div id="links_item">
<h4>{{ group }}</h4>
{% for link in bookmarks[group] %}
<a href="{{ link.url }}" class="theme_color-border theme_text-select">{{ link.name }}</a>
{% endfor %}
</div>
{% endfor %}
{{#bookmarks}}
<div id="links_item">
<h4>{{category}}</h4>
{{#links}}
<a href="{{url}}" target="{{target}}" class="theme_color-border theme_text-select">{{name}}</a>
{{/links}}
</div>
{{/bookmarks}}
</div>
</script>
{% endraw %}
</section>
{% endif %}
</main>
<div id="modal_init">
@@ -100,16 +128,11 @@
</a>
</div>
<script src="./static/js/themer.js" type="text/javascript"></script>
<script src="./static/js/search.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/codemirror.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/mode/yaml/yaml.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/addon/lint/yaml-lint.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/addon/lint/lint.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/addon/fold/foldcode.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/addon/fold/foldgutter.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/addon/fold/indent-fold.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js" type="text/javascript"></script>
<script src="/static/js/data.js" type="text/javascript"></script>
<script src="/static/js/script.js" type="text/javascript"></script>
<script src="/static/js/themer.js" type="text/javascript"></script>
<script src="/static/js/search.js" type="text/javascript"></script>
</body>
</html>

4
pyproject.toml Normal file
View File

@@ -0,0 +1,4 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

1
requirements-dev.txt Normal file
View File

@@ -0,0 +1 @@
wheel

30
setup.cfg Normal file
View File

@@ -0,0 +1,30 @@
[metadata]
name = ohayodash
version = 0.2.0
description = A Kubernetes driven start page and dashboard. All configuration is done by standard Kubernetes objects and ConfigMaps.
long_description = file: README.md, LICENSE
license = MIT
license_file = LICENSE
classifiers =
Framework :: Flask
License :: OSI Approved :: MIT License
Programming Language :: Python :: 3
Programming Language :: Python :: 3.9
[options]
zip_safe = False
packages = ohayodash
install_requires =
flask
kubernetes
pyyaml
gunicorn
[flake8]
format = wemake
ignore = E501,D,WPS226,WPS110, WPS210,WPS231
max-line-length = 120
exclude = setup.py
[darglint]
docstring_style=sphinx

3
setup.py Normal file
View File

@@ -0,0 +1,3 @@
from setuptools import setup
setup()