From 5cf890d3ae3f7dab1c64fb3c80494dd34d7b8dc6 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Thu, 23 Dec 2021 17:45:21 +0000 Subject: [PATCH] Initial commit --- .github/renovate.json | 5 + .github/workflows/build-container.yaml | 32 ++ .github/workflows/lint.yaml | 21 + .github/workflows/release.yaml | 27 ++ .gitignore | 152 +++++++ Dockerfile | 6 + ohayodash/__init__.py | 0 ohayodash/app.py | 5 + ohayodash/base.py | 71 +++ ohayodash/static/css/styles.css | 601 +++++++++++++++++++++++++ ohayodash/static/js/search.js | 95 ++++ ohayodash/static/js/themer.js | 139 ++++++ ohayodash/templates/index.j2 | 115 +++++ requirements.txt | 4 + 14 files changed, 1273 insertions(+) create mode 100644 .github/renovate.json create mode 100644 .github/workflows/build-container.yaml create mode 100644 .github/workflows/lint.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 ohayodash/__init__.py create mode 100644 ohayodash/app.py create mode 100644 ohayodash/base.py create mode 100755 ohayodash/static/css/styles.css create mode 100755 ohayodash/static/js/search.js create mode 100755 ohayodash/static/js/themer.js create mode 100644 ohayodash/templates/index.j2 create mode 100644 requirements.txt diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..1ded03d --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "config:base" + ] +} \ No newline at end of file diff --git a/.github/workflows/build-container.yaml b/.github/workflows/build-container.yaml new file mode 100644 index 0000000..ae164a9 --- /dev/null +++ b/.github/workflows/build-container.yaml @@ -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 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..66dd3d6 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -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 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..420fec9 --- /dev/null +++ b/.github/workflows/release.yaml @@ -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/* \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9005f2 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a62c844 --- /dev/null +++ b/Dockerfile @@ -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"] \ No newline at end of file diff --git a/ohayodash/__init__.py b/ohayodash/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ohayodash/app.py b/ohayodash/app.py new file mode 100644 index 0000000..a93c227 --- /dev/null +++ b/ohayodash/app.py @@ -0,0 +1,5 @@ +from flask import Flask +from ohayodash.base import base + +app = Flask(__name__) +app.register_blueprint(base) diff --git a/ohayodash/base.py b/ohayodash/base.py new file mode 100644 index 0000000..86926cb --- /dev/null +++ b/ohayodash/base.py @@ -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(), + }) diff --git a/ohayodash/static/css/styles.css b/ohayodash/static/css/styles.css new file mode 100755 index 0000000..b2f6844 --- /dev/null +++ b/ohayodash/static/css/styles.css @@ -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; + } +} diff --git a/ohayodash/static/js/search.js b/ohayodash/static/js/search.js new file mode 100755 index 0000000..76f6cda --- /dev/null +++ b/ohayodash/static/js/search.js @@ -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); +}; diff --git a/ohayodash/static/js/themer.js b/ohayodash/static/js/themer.js new file mode 100755 index 0000000..9c19380 --- /dev/null +++ b/ohayodash/static/js/themer.js @@ -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; + + } + }) +} \ No newline at end of file diff --git a/ohayodash/templates/index.j2 b/ohayodash/templates/index.j2 new file mode 100644 index 0000000..a7194d0 --- /dev/null +++ b/ohayodash/templates/index.j2 @@ -0,0 +1,115 @@ + + + + + {{ title | default("Hajimari") }} + + + + + + + + + + + + + + + + +
+ + + + + {% if applications %} +
+

Applications

+
+ {% for app in applications %} +
+
+ +
+
+ {{ app.name }} + {% if app.show_url %} + {{ app.url }} + {% endif %} +
+
+ {% endfor %} +
+
+ {% endif %} + + {% if bookmarks %} + + {% endif %} +
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1a7dde4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask==2.0.2 +kubernetes==21.7.0 +pyyaml==6.0 +gunicorn==20.1.0 \ No newline at end of file