mirror of
https://github.com/nikdoof/ohayodash.git
synced 2025-12-13 10:02:17 +00:00
Initial commit
This commit is contained in:
5
.github/renovate.json
vendored
Normal file
5
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
||||
32
.github/workflows/build-container.yaml
vendored
Normal file
32
.github/workflows/build-container.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Build Container
|
||||
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+.[0-9]+"
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v1
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository_owner }}/ohayodash:${{ github.ref_name }}
|
||||
ghcr.io/${{ github.repository_owner }}/ohayodash:latest
|
||||
21
.github/workflows/lint.yaml
vendored
Normal file
21
.github/workflows/lint.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Lint
|
||||
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
run-linters:
|
||||
name: Run linters
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: wemake-python-styleguide
|
||||
uses: wemake-services/wemake-python-styleguide@0.16.0
|
||||
27
.github/workflows/release.yaml
vendored
Normal file
27
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+.[0-9]+"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
- run: pip install -r requirements-dev.txt
|
||||
|
||||
- name: Build Assets
|
||||
run: python setup.py sdist bdist_wheel
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
name: "Version ${{ github.ref_name }}"
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
dist/*
|
||||
152
.gitignore
vendored
Normal file
152
.gitignore
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# 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/
|
||||
6
Dockerfile
Normal file
6
Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM python:3.9
|
||||
WORKDIR /app
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
COPY ./ohayodash /app/ohayodash
|
||||
CMD ["gunicorn", "--bind", "0.0.0.0:80", "ohayodash.app:app"]
|
||||
0
ohayodash/__init__.py
Normal file
0
ohayodash/__init__.py
Normal file
5
ohayodash/app.py
Normal file
5
ohayodash/app.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from flask import Flask
|
||||
from ohayodash.base import base
|
||||
|
||||
app = Flask(__name__)
|
||||
app.register_blueprint(base)
|
||||
71
ohayodash/base.py
Normal file
71
ohayodash/base.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from flask import Blueprint, render_template, abort
|
||||
from jinja2 import TemplateNotFound
|
||||
import datetime
|
||||
import kubernetes
|
||||
import yaml
|
||||
|
||||
ANNOTATION_BASE = 'ohayodash.github.io'
|
||||
|
||||
base = Blueprint('base', __name__, template_folder='templates')
|
||||
|
||||
def get_k8s_applications():
|
||||
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':
|
||||
continue
|
||||
|
||||
values = {
|
||||
'name': ingress.metadata.name,
|
||||
'namespace': ingress.metadata.namespace,
|
||||
'url': f'https://{ingress.spec.rules[0].host}',
|
||||
'show_url': False,
|
||||
}
|
||||
|
||||
for key in ingress.metadata.annotations:
|
||||
if key.startswith(ANNOTATION_BASE):
|
||||
val = key.split('/')[1]
|
||||
values[val] = ingress.metadata.annotations[key]
|
||||
|
||||
results.append(values)
|
||||
return sorted(results, key=lambda i: i['appName'])
|
||||
|
||||
|
||||
def get_bookmarks():
|
||||
kubernetes.config.load_kube_config()
|
||||
v1 = kubernetes.client.CoreV1Api()
|
||||
ret = v1.list_config_map_for_all_namespaces(watch=False)
|
||||
|
||||
bookmarks = {}
|
||||
for cm in ret.items:
|
||||
|
||||
# Skip if
|
||||
if not cm.metadata.annotations or f'{ANNOTATION_BASE}/bookmarks' not in cm.metadata.annotations:
|
||||
continue
|
||||
|
||||
data = yaml.safe_load(cm.data['bookmarks'])
|
||||
for bookmark in data:
|
||||
if 'group' not in bookmark:
|
||||
group = 'default'
|
||||
else:
|
||||
group = bookmark['group'].lower()
|
||||
if group not in bookmarks:
|
||||
bookmarks[group] = []
|
||||
bookmarks[group].append(bookmark)
|
||||
|
||||
return bookmarks
|
||||
|
||||
|
||||
@base.route('/')
|
||||
def index():
|
||||
return render_template(f'index.j2', **{
|
||||
'now': datetime.datetime.utcnow(),
|
||||
'applications': get_k8s_applications(),
|
||||
'bookmarks': get_bookmarks(),
|
||||
})
|
||||
601
ohayodash/static/css/styles.css
Executable file
601
ohayodash/static/css/styles.css
Executable file
@@ -0,0 +1,601 @@
|
||||
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); }
|
||||
|
||||
html,
|
||||
body{
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-text-pri);
|
||||
scrollbar-color: var(--color-text-acc) var(--color-background);
|
||||
scrollbar-width: thin;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
height: auto;
|
||||
letter-spacing: -.012em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
webkit-font-smoothing: antialiased;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after{
|
||||
box-sizing: inherit;
|
||||
moz-box-sizing: inherit;
|
||||
webkit-box-sizing: inherit;
|
||||
}
|
||||
|
||||
:root{
|
||||
module-spacing: 3vh;
|
||||
}
|
||||
|
||||
|
||||
/* TEXT STYLES */
|
||||
|
||||
h1, h2{
|
||||
font-weight: 300;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h2, h3, h4{
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
h1{
|
||||
font-size: 4em;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-size: 16px;
|
||||
height: 30px;
|
||||
|
||||
}
|
||||
|
||||
h3{
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
h4{
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
a{
|
||||
color: var(--color-text-pri);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover{
|
||||
text-decoration: underline;
|
||||
webkit-text-decoration-color: var(--color-text-acc);
|
||||
webkit-text-decoration-skip: true;
|
||||
}
|
||||
|
||||
.icon{
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
|
||||
/* FORMS */
|
||||
|
||||
input{
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-bottom: thin solid var(--color-text-acc);
|
||||
color: var(--color-text-pri);
|
||||
font-size: 0.8em;
|
||||
height: 3.5em;
|
||||
transition: all 0.4s ease;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input:focus{
|
||||
color-border: var(--color-text-pri);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input:focus{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
/* TABLES */
|
||||
|
||||
table{
|
||||
border: thin solid #e4e4e4;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table td:nth-of-type(2){
|
||||
padding-right: 5em;
|
||||
}
|
||||
|
||||
table td{
|
||||
border: thin solid #e4e4e4;
|
||||
color: #333333;
|
||||
font-size: 1em;
|
||||
overflow: hidden;
|
||||
padding: 10px 5px;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
table th{
|
||||
border: thin solid #e4e4e4;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
|
||||
table a{
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
|
||||
/* ANIMATION */
|
||||
|
||||
.fade{
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fadeseq{
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.fade{
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fade{
|
||||
animation: fadeseq .3s forwards;
|
||||
}
|
||||
|
||||
.fade:nth-child(2){
|
||||
animation-delay: .4s;
|
||||
}
|
||||
|
||||
|
||||
/* LAYOUT */
|
||||
|
||||
#container{
|
||||
align-items: stretch;
|
||||
display: grid;
|
||||
grid-column-gap: 20px;
|
||||
grid-row-gap: 3vh;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 8vh auto;
|
||||
justify-items: stretch;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 5vh;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* SECTIONS */
|
||||
|
||||
#header{
|
||||
border-bottom: 0px solid var(--color-text-acc);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#apps_loop{
|
||||
border-bottom: 0px solid var(--color-text-acc);
|
||||
display: grid;
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
grid-template-rows: 64px;
|
||||
padding-bottom: var(--module-spacing);
|
||||
}
|
||||
|
||||
.apps_icon{
|
||||
height: 64px;
|
||||
margin-right: 1em;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.apps_icon span{
|
||||
font-size: 2.5em;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.apps_item{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
height: 64px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.apps_text{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.apps_text a{
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.apps_text span{
|
||||
color: var(--color-text-acc);
|
||||
font-size: 0.8em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
||||
#links_loop{
|
||||
display: grid;
|
||||
flex-wrap: nowrap;
|
||||
grid-column-gap: 20px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
|
||||
#links_item{
|
||||
line-height: 1.5rem;
|
||||
margin-bottom: 2em;
|
||||
webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
#links_item h4{
|
||||
color: var(--color-text-acc);
|
||||
}
|
||||
|
||||
#links_item a{
|
||||
display: block;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* MODAL */
|
||||
|
||||
|
||||
#modal{
|
||||
overflow-y: auto;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition: all 0.3s;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
#modal:target{
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#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;
|
||||
padding: 2em;
|
||||
margin-top: 5vh;
|
||||
width: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#modal h1{
|
||||
color: #333333;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#modal h2{
|
||||
margin-top:1.5em;
|
||||
}
|
||||
|
||||
#modal-header{
|
||||
display:flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#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-close{
|
||||
color: #000000;
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.modal-close:hover{
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#modal_init a{
|
||||
bottom: 1vh;
|
||||
color: var(--color-text-acc);
|
||||
left: 1vw;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#modal_init a:hover{
|
||||
color: var(--color-text-pri);
|
||||
}
|
||||
|
||||
#modal-theme{
|
||||
border-bottom: 0px solid var(--color-text-acc);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
#providers{
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
#editor {
|
||||
display: flex;
|
||||
margin-bottom: 2em;
|
||||
border: 4px solid #5c5c5c;
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
#save-config {
|
||||
float: right;
|
||||
margin-top: 1em;
|
||||
color: var(--color-text-acc);
|
||||
text-transform: uppercase;
|
||||
padding: 5px 10px;
|
||||
background-color: var(--color-background);
|
||||
border: 4px solid var(--color-text-acc) !important;
|
||||
transition: all 0.3s ease 0s;
|
||||
}
|
||||
|
||||
#save-config:hover {
|
||||
background-color: var(--color-text-acc);
|
||||
color: var(--color-background);
|
||||
border: 4px solid var(--color-background) !important;
|
||||
}
|
||||
|
||||
#config .CodeMirror {
|
||||
border: 4px solid #5c5c5c !important;
|
||||
}
|
||||
|
||||
#config .CodeMirror-vscrollbar {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
#config .CodeMirror-hscrollbar {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
/* THEMING */
|
||||
|
||||
.theme-button{
|
||||
font-size: 0.8em;
|
||||
margin: 2px;
|
||||
width:128px;
|
||||
line-height: 3em;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.theme-blackboard{
|
||||
background-color: #000000;
|
||||
border: 4px solid #5c5c5c;
|
||||
color: #FFFDEA;
|
||||
}
|
||||
|
||||
.theme-gazette{
|
||||
background-color: #F2F7FF;
|
||||
border: 4px solid #5c5c5c;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.theme-espresso{
|
||||
background-color: #21211F;
|
||||
border: 4px solid #4E4E4E;
|
||||
color: #D1B59A;
|
||||
}
|
||||
|
||||
.theme-cab{
|
||||
background-color: #FEED01;
|
||||
border: 4px solid #424242;
|
||||
color: #1F1F1F;
|
||||
}
|
||||
|
||||
.theme-cloud{
|
||||
background-color: #f1f2f0;
|
||||
border: 4px solid #35342f;
|
||||
color: #37bbe4;
|
||||
}
|
||||
|
||||
.theme-lime{
|
||||
background-color: #263238;
|
||||
border: 4px solid #AABBC3;
|
||||
color: #aeea00;
|
||||
}
|
||||
|
||||
.theme-passion{
|
||||
background-color: #f5f5f5;
|
||||
border: 4px solid #8e24aa;
|
||||
color: #12005e;
|
||||
}
|
||||
|
||||
.theme-blues{
|
||||
background-color: #2B2C56;
|
||||
border: 4px solid #6677EB;
|
||||
color: #EFF1FC;
|
||||
}
|
||||
|
||||
.theme-chalk{
|
||||
background-color: #263238;
|
||||
border: 4px solid #FF869A;
|
||||
color: #AABBC3;
|
||||
}
|
||||
|
||||
.theme-tron{
|
||||
background-color: #242B33;
|
||||
border: 4px solid #6EE2FF;
|
||||
color: #EFFBFF;
|
||||
}
|
||||
|
||||
.theme-paper{
|
||||
background-color: #F8F6F1;
|
||||
border: 4px solid #F5E1A4;
|
||||
color: #4C432E;
|
||||
}
|
||||
|
||||
|
||||
/* MEDIA QUERIES */
|
||||
|
||||
@media screen and (max-width: 1260px)
|
||||
{
|
||||
#container
|
||||
{
|
||||
align-items: stretch;
|
||||
display: grid;
|
||||
grid-column-gap: 10px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 80px auto;
|
||||
justify-items: stretch;
|
||||
margin-bottom: 1vh;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#apps_loop{
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
width: 90vw;
|
||||
}
|
||||
|
||||
#links_loop {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
#modal>div{
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 5vh;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 667px)
|
||||
{
|
||||
html{
|
||||
font-size: calc(16px + 6 * ((100vw - 320px) / 680));
|
||||
}
|
||||
|
||||
#container{
|
||||
align-items: stretch;
|
||||
display: grid;
|
||||
grid-column-gap: 20px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 80px auto;
|
||||
justify-items: stretch;
|
||||
margin-bottom: 1vh;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
h1{
|
||||
font-size: 4em;
|
||||
height: auto;
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-size: 1em;
|
||||
height: auto;
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
h3{
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
#apps_loop{
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 90vw;
|
||||
}
|
||||
|
||||
.apps_icon{
|
||||
height: 64px;
|
||||
margin-right: 0.8em;
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.apps_icon span{
|
||||
font-size: 2em;
|
||||
line-height: 2.5rem;
|
||||
}
|
||||
|
||||
#links_loop{
|
||||
display: grid;
|
||||
flex-wrap: nowrap;
|
||||
grid-column-gap: 20px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Small Screens */
|
||||
@media only screen and (max-width: 400px) {
|
||||
#app-address {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
95
ohayodash/static/js/search.js
Executable file
95
ohayodash/static/js/search.js
Executable file
@@ -0,0 +1,95 @@
|
||||
var sindex = 0;
|
||||
var cycle = false;
|
||||
|
||||
function start() {
|
||||
var query = getParameterByName('q');
|
||||
if (query) search(query.replaceAll("+", "%2B"));
|
||||
|
||||
document.getElementById('keywords').focus();
|
||||
|
||||
window.setInterval(function () {
|
||||
updatetime();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function handleKeyPress(e) {
|
||||
var key = e.keyCode || e.which;
|
||||
var text = document.getElementById("keywords").value.replaceAll("+", "%2B");
|
||||
var option = text.substr(1, text.indexOf(' ') - 1) || text.substr(1);
|
||||
var subtext = text.substr(2 + option.length);
|
||||
if (key == 13) { // Search functions
|
||||
search(text);
|
||||
}
|
||||
if(key == 32){ //Space to go to search
|
||||
document.getElementById("keywords").focus();
|
||||
}
|
||||
sindex = 0;
|
||||
cycle = false;
|
||||
}
|
||||
|
||||
function search(text) {
|
||||
var option = text.substr(1, text.indexOf(' ') - 1) || text.substr(1);
|
||||
var subtext = text.substr(2 + option.length);
|
||||
if (text[0] === '/') {
|
||||
if (text.indexOf(' ') > -1) {
|
||||
switch (option) {
|
||||
case "d":
|
||||
window.location = "https://duckduckgo.com/?q=" + subtext;
|
||||
break;
|
||||
case "i":
|
||||
window.location = "https://www.imdb.com/find?q=" + subtext;
|
||||
break;
|
||||
case "r":
|
||||
window.location = "https://www.reddit.com/search?q=" + subtext;
|
||||
break;
|
||||
case "y":
|
||||
window.location = "https://www.youtube.com/results?search_query=" + subtext;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
var option = text.substr(1);
|
||||
switch (option) {
|
||||
case "d":
|
||||
window.location = "https://www.duckduckgo.com";
|
||||
break;
|
||||
case "y":
|
||||
window.location = "https://www.youtube.com";
|
||||
break;
|
||||
case "r":
|
||||
window.location = "https://reddit.com";
|
||||
break;
|
||||
case "s":
|
||||
window.location = "https://open.spotify.com";
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (validURL(text)) {
|
||||
if (containsProtocol(text))
|
||||
window.location = text;
|
||||
else
|
||||
window.location = "https://" + text;
|
||||
} else {
|
||||
window.location = "https://www.google.com/search?q=" + text;
|
||||
}
|
||||
}
|
||||
|
||||
// Source: https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url
|
||||
function validURL(str) {
|
||||
var pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
|
||||
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
|
||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
|
||||
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
|
||||
'(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
|
||||
return !!pattern.test(str);
|
||||
}
|
||||
|
||||
function containsProtocol(str) {
|
||||
var pattern = new RegExp('^(https?:\\/\\/){1}.*', 'i');
|
||||
return !!pattern.test(str);
|
||||
}
|
||||
|
||||
String.prototype.replaceAll = function(search, replacement) {
|
||||
var target = this;
|
||||
return target.split(search).join(replacement);
|
||||
};
|
||||
139
ohayodash/static/js/themer.js
Executable file
139
ohayodash/static/js/themer.js
Executable file
@@ -0,0 +1,139 @@
|
||||
const setValue = (property, value) => {
|
||||
if (value) {
|
||||
document.documentElement.style.setProperty(`--${property}`, value);
|
||||
|
||||
const input = document.querySelector(`#${property}`);
|
||||
if (input) {
|
||||
value = value.replace('px', '');
|
||||
input.value = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setValueFromLocalStorage = property => {
|
||||
let value = localStorage.getItem(property);
|
||||
setValue(property, value);
|
||||
};
|
||||
|
||||
const setTheme = options => {
|
||||
for (let option of Object.keys(options)) {
|
||||
const property = option;
|
||||
const value = options[option];
|
||||
|
||||
setValue(property, value);
|
||||
localStorage.setItem(property, value);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
setValueFromLocalStorage('color-background');
|
||||
setValueFromLocalStorage('color-text-pri');
|
||||
setValueFromLocalStorage('color-text-acc');
|
||||
});
|
||||
|
||||
const dataThemeButtons = document.querySelectorAll('[data-theme]');
|
||||
|
||||
for (let i = 0; i < dataThemeButtons.length; i++) {
|
||||
dataThemeButtons[i].addEventListener('click', () => {
|
||||
const theme = dataThemeButtons[i].dataset.theme;
|
||||
|
||||
switch (theme) {
|
||||
case 'blackboard':
|
||||
setTheme({
|
||||
'color-background': '#1a1a1a',
|
||||
'color-text-pri': '#FFFDEA',
|
||||
'color-text-acc': '#5c5c5c'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'gazette':
|
||||
setTheme({
|
||||
'color-background': '#F2F7FF',
|
||||
'color-text-pri': '#000000',
|
||||
'color-text-acc': '#5c5c5c'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'espresso':
|
||||
setTheme({
|
||||
'color-background': '#21211F',
|
||||
'color-text-pri': '#D1B59A',
|
||||
'color-text-acc': '#4E4E4E'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'cab':
|
||||
setTheme({
|
||||
'color-background': '#F6D305',
|
||||
'color-text-pri': '#1F1F1F',
|
||||
'color-text-acc': '#424242'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'cloud':
|
||||
setTheme({
|
||||
'color-background': '#f1f2f0',
|
||||
'color-text-pri': '#35342f',
|
||||
'color-text-acc': '#37bbe4'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'lime':
|
||||
setTheme({
|
||||
'color-background': '#263238',
|
||||
'color-text-pri': '#AABBC3',
|
||||
'color-text-acc': '#aeea00'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'white':
|
||||
setTheme({
|
||||
'color-background': '#ffffff',
|
||||
'color-text-pri': '#222222',
|
||||
'color-text-acc': '#dddddd'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'tron':
|
||||
setTheme({
|
||||
'color-background': '#242B33',
|
||||
'color-text-pri': '#EFFBFF',
|
||||
'color-text-acc': '#6EE2FF'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'blues':
|
||||
setTheme({
|
||||
'color-background': '#2B2C56',
|
||||
'color-text-pri': '#EFF1FC',
|
||||
'color-text-acc': '#6677EB'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'passion':
|
||||
setTheme({
|
||||
'color-background': '#f5f5f5',
|
||||
'color-text-pri': '#12005e',
|
||||
'color-text-acc': '#8e24aa'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'chalk':
|
||||
setTheme({
|
||||
'color-background': '#263238',
|
||||
'color-text-pri': '#AABBC3',
|
||||
'color-text-acc': '#FF869A'
|
||||
});
|
||||
return;
|
||||
|
||||
case 'paper':
|
||||
setTheme({
|
||||
'color-background': '#F8F6F1',
|
||||
'color-text-pri': '#4C432E',
|
||||
'color-text-acc': '#AA9A73'
|
||||
});
|
||||
return;
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
115
ohayodash/templates/index.j2
Normal file
115
ohayodash/templates/index.j2
Normal file
@@ -0,0 +1,115 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>{{ title | default("Hajimari") }}</title>
|
||||
<meta charset="utf-8">
|
||||
<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 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="//code.iconify.design/1/1.0.7/iconify.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<section id="modal">
|
||||
<div>
|
||||
<header id="modal-header">
|
||||
<h1>Options</h1>
|
||||
<a href="#" title="Close" class="modal-close">
|
||||
<span class="iconify" data-icon="mdi-close"></span>
|
||||
</a>
|
||||
</header>
|
||||
|
||||
<h2>Color themes</h2>
|
||||
|
||||
<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="chalk" class="theme-button theme-chalk">Chalk</button>
|
||||
<button data-theme="tron" class="theme-button theme-tron">Tron</button>
|
||||
<button data-theme="paper" class="theme-button theme-paper">Paper</button>
|
||||
</div>
|
||||
|
||||
</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>
|
||||
</section>
|
||||
|
||||
{% if applications %}
|
||||
<section id="apps">
|
||||
<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>
|
||||
</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 %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if bookmarks %}
|
||||
<section id="links">
|
||||
<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 %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
</main>
|
||||
|
||||
<div id="modal_init">
|
||||
<a class="btn" href="#modal">
|
||||
<span class="iconify icon" data-icon="mdi-xbox-controller-menu"></span>
|
||||
</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>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
flask==2.0.2
|
||||
kubernetes==21.7.0
|
||||
pyyaml==6.0
|
||||
gunicorn==20.1.0
|
||||
Reference in New Issue
Block a user