├── .dockerignore ├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── Dockerfile ├── License.md ├── README.md ├── dashmachine ├── __init__.py ├── cssmin.py ├── error_pages │ ├── __init__.py │ └── routes.py ├── main │ ├── __init__.py │ ├── forms.py │ ├── models.py │ ├── read_config.py │ ├── routes.py │ └── utils.py ├── paths.py ├── platform │ ├── __init__.py │ ├── curl.py │ ├── deluge.py │ ├── docker.py │ ├── healthchecks.py │ ├── http_status.py │ ├── lidarr.py │ ├── pihole.py │ ├── ping.py │ ├── plex.py │ ├── radarr.py │ ├── rest.py │ ├── sonarr.py │ ├── tautulli.py │ ├── transmission.py │ └── weather.py ├── rest_api │ ├── __init__.py │ └── resources.py ├── settings_system │ ├── __init__.py │ ├── forms.py │ ├── models.py │ ├── routes.py │ └── utils.py ├── site.db ├── sources.py ├── static │ ├── cache │ │ └── .no │ ├── css │ │ ├── global │ │ │ ├── dashmachine-theme.css │ │ │ ├── dashmachine.css │ │ │ ├── style.css │ │ │ ├── tcdrop.css │ │ │ └── z-index-guide.txt │ │ ├── main │ │ │ ├── home.css │ │ │ ├── login.css │ │ │ └── page-404.css │ │ ├── settings_system │ │ │ └── settings.css │ │ └── vendors │ │ │ ├── fonts │ │ │ ├── gelasio-kh.woff2 │ │ │ ├── gelasio-yq.woff2 │ │ │ ├── material-icons-outlined.woff2 │ │ │ ├── material-icons.woff2 │ │ │ ├── montserrat-bi.woff2 │ │ │ └── montserrat-yw.woff2 │ │ │ ├── google-fonts.css │ │ │ ├── material-icons.css │ │ │ └── materialize.min.css │ ├── images │ │ ├── apps │ │ │ ├── Syncthing.png │ │ │ ├── airsonic.png │ │ │ ├── alertmanager.png │ │ │ ├── aria2.png │ │ │ ├── bazarr.png │ │ │ ├── bitwarden.png │ │ │ ├── bookstack.png │ │ │ ├── calibre-web.png │ │ │ ├── cloudcmd.png │ │ │ ├── cockpit-project.png │ │ │ ├── default.png │ │ │ ├── deluge.png │ │ │ ├── docker.png │ │ │ ├── dokuwiki.png │ │ │ ├── duplicati.png │ │ │ ├── emby.png │ │ │ ├── filerun.png │ │ │ ├── firefly_iii.png │ │ │ ├── gitea.png │ │ │ ├── glances.png │ │ │ ├── gogs.png │ │ │ ├── gotify.png │ │ │ ├── grafana.png │ │ │ ├── guacamole.png │ │ │ ├── handbrake.png │ │ │ ├── healthchecks.png │ │ │ ├── home-assistant.png │ │ │ ├── homebridge.png │ │ │ ├── hp-ilo.png │ │ │ ├── icon.png │ │ │ ├── jackett.png │ │ │ ├── jdownloader.png │ │ │ ├── jeedom.png │ │ │ ├── jellyfin.png │ │ │ ├── jenkins.png │ │ │ ├── karma.png │ │ │ ├── kodi.png │ │ │ ├── krusader.png │ │ │ ├── lidarr.png │ │ │ ├── mailcow.png │ │ │ ├── netdata.png │ │ │ ├── nextcloud.png │ │ │ ├── nginxproxymanager.png │ │ │ ├── nodered.png │ │ │ ├── ombi.png │ │ │ ├── openeats.png │ │ │ ├── openmediavault.png │ │ │ ├── openwrt.png │ │ │ ├── opnsense.png │ │ │ ├── pfsense.png │ │ │ ├── phoscon.png │ │ │ ├── phpmyadmin.png │ │ │ ├── pihole.png │ │ │ ├── piwigo.png │ │ │ ├── plex.png │ │ │ ├── portainer.png │ │ │ ├── privatebin.png │ │ │ ├── projectsend.png │ │ │ ├── prometheus.png │ │ │ ├── proxmox.png │ │ │ ├── qbittorrent.png │ │ │ ├── radarr.png │ │ │ ├── riot.png │ │ │ ├── sonarr.png │ │ │ ├── statping.png │ │ │ ├── synology-active-backup.png │ │ │ ├── synology-audio-station.png │ │ │ ├── synology-calendar.png │ │ │ ├── synology-download-station.png │ │ │ ├── synology-drive.png │ │ │ ├── synology-mailplus.png │ │ │ ├── synology-moments.png │ │ │ ├── synology-note-station.png │ │ │ ├── synology-office.png │ │ │ ├── synology-video-station.png │ │ │ ├── tasmoadmin.png │ │ │ ├── tautulli.png │ │ │ ├── terminal.png │ │ │ ├── thelounge.png │ │ │ ├── traefik.png │ │ │ ├── transmission.png │ │ │ ├── unifi.png │ │ │ ├── vmware-esxi.png │ │ │ ├── vscode.png │ │ │ ├── wallabag.png │ │ │ ├── wetty.png │ │ │ └── wikijs.png │ │ ├── favicon │ │ │ ├── Oapple-touch-icon-152x152.png │ │ │ ├── Ofavicon-32x32.png │ │ │ ├── Omstile-144x144.png │ │ │ ├── apple-touch-icon-152x152.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicons.gvdesign │ │ │ └── mstile-144x144.png │ │ └── logo │ │ │ ├── full.svg │ │ │ ├── logo.gvdesign │ │ │ ├── logo.png │ │ │ └── logo.svg │ ├── js │ │ ├── global │ │ │ ├── dashmachine.js │ │ │ └── tcdrop.js │ │ ├── main │ │ │ ├── home.js │ │ │ └── login.js │ │ ├── settings_system │ │ │ └── settings.js │ │ └── vendors │ │ │ ├── jquery.min.js │ │ │ ├── jqueryui.min.js │ │ │ └── materialize.min.js │ └── vendors │ │ └── animate-css │ │ └── animate.css ├── templates │ ├── error_pages │ │ ├── 403.html │ │ ├── 404.html │ │ ├── 500.html │ │ └── unauthorized.html │ ├── global_macros.html │ ├── main │ │ ├── app-view.html │ │ ├── base.html │ │ ├── breadcrumb.html │ │ ├── home.html │ │ ├── layout.html │ │ ├── macros.html │ │ ├── tcdrop-file-row.html │ │ ├── tcdrop.html │ │ ├── top-and-side-nav.html │ │ └── top-nav.html │ ├── page_template.html │ ├── settings_system │ │ ├── files.html │ │ └── settings.html │ └── user │ │ ├── add_payment_method.html │ │ ├── login.html │ │ └── register.html ├── user_system │ ├── __init__.py │ ├── forms.py │ ├── models.py │ ├── routes.py │ └── utils.py └── version.py ├── default_config.ini ├── migrations ├── README ├── alembic.ini ├── env.py ├── script.py.mako └── versions │ └── 7450bb0ce3ff_.py ├── pull_request_template.md ├── readme_cards.md ├── readme_data_sources.md ├── readme_settings.md ├── requirements.txt ├── run.py ├── screenshot1.png ├── screenshot2.png ├── screenshot3.png ├── screenshot4.png ├── template_apps ├── Airsonic.ini ├── Alertmanager.ini ├── Aria2.ini ├── Bazarr.ini ├── Bitwarden.ini ├── BookStack.ini ├── Calibre-Web.ini ├── CloudCMD.ini ├── Cockpit.ini ├── Deluge.ini ├── Docker.ini ├── DokuWiki.ini ├── Duplicati.ini ├── Emby.ini ├── FileRun.ini ├── Firefly-III.ini ├── Gitea.ini ├── Glances.ini ├── Gogs.ini ├── Gotify.ini ├── Grafana.ini ├── Guacamole.ini ├── HP iLO.ini ├── HandBrake.ini ├── Healthchecks.ini ├── Home Assistant.ini ├── Homebridge.ini ├── JDownloader.ini ├── Jackett.ini ├── Jeedom.ini ├── Jellyfin.ini ├── Jenkins.ini ├── Karma.ini ├── Kodi.ini ├── Krusader.ini ├── Lidarr.ini ├── Mailcow.ini ├── Netdata.ini ├── Nextcloud.ini ├── Nginx Proxy Manager.ini ├── Node-RED.ini ├── OPNsense.ini ├── Ombi.ini ├── OpenEats.ini ├── OpenMediaVault.ini ├── OpenWRT.ini ├── Phoscon.ini ├── Pi-hole.ini ├── Piwigo.ini ├── Plex.ini ├── Portainer.ini ├── PrivateBin.ini ├── ProjectSend.ini ├── Prometheus.ini ├── Proxmox.ini ├── Radarr.ini ├── Riot.ini ├── SSH.ini ├── Sonarr.ini ├── Statping.ini ├── Syncthing.ini ├── Synology ActiveBackup.ini ├── Synology Audio Station.ini ├── Synology Calendar.ini ├── Synology Download Station.ini ├── Synology Drive.ini ├── Synology MailPlus.ini ├── Synology Moments.ini ├── Synology Note Station.ini ├── Synology Office.ini ├── Synology Video Station.ini ├── TasmoAdmin.ini ├── Tautulli.ini ├── The Lounge.ini ├── Traefik.ini ├── Transmission.ini ├── Unifi.ini ├── VMware ESXi.ini ├── VS Code.ini ├── Wallabag.ini ├── WeTTy.ini ├── Wikijs.ini ├── pfSense.ini ├── phpMyAdmin.ini └── qBittorrent.ini ├── update_message.md └── wsgi.py /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .idea/ 3 | 4 | dashmachine/user_data/ 5 | dashmachine/static/images/icons 6 | dashmachine/static/images/backgrounds 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | liberapay: rmountjoy 3 | custom: https://www.bountysource.com/teams/dashmachine-app 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # IPython 77 | profile_default/ 78 | ipython_config.py 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | .dmypy.json 111 | dmypy.json 112 | 113 | # Pyre type checker 114 | .pyre/ 115 | .directory 116 | scheduler.db 117 | scheduler.db 118 | 119 | .idea/ 120 | 121 | dashmachine/user_data/ 122 | dashmachine/static/images/icons 123 | dashmachine/static/images/backgrounds -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim 2 | 3 | RUN apt-get update -q \ 4 | && apt-get install --no-install-recommends -qy \ 5 | inetutils-ping \ 6 | && rm -rf /var/lib/apt/lists/* 7 | 8 | COPY [ "requirements.txt", "/dashmachine/" ] 9 | 10 | WORKDIR /dashmachine 11 | 12 | RUN pip install --no-cache-dir --progress-bar off -r requirements.txt 13 | 14 | COPY [ ".", "/dashmachine/" ] 15 | 16 | ENV PRODUCTION=true 17 | EXPOSE 5000 18 | VOLUME /dashmachine/dashmachine/user_data 19 | CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "wsgi:app" ] 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DashMachine 2 | ### Another web application bookmark dashboard, with fun features. 3 | ![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/dashmachine?style=social) 4 | 5 | ![GitHub last commit](https://img.shields.io/github/last-commit/rmountjoy92/dashmachine) 6 | ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/rmountjoy/dashmachine) 7 | 8 | ![Docker Pulls](https://img.shields.io/docker/pulls/rmountjoy/dashmachine) 9 | ![GitHub Repo stars](https://img.shields.io/github/stars/rmountjoy92/dashmachine?style=social) 10 | 11 | ![GitHub repo size](https://img.shields.io/github/repo-size/rmountjoy92/dashmachine) 12 | ![Docker Image Size (tag)](https://img.shields.io/docker/image-size/rmountjoy/dashmachine/latest?label=Docker%20Image%20Size) 13 | ![Lines of code](https://img.shields.io/tokei/lines/github/rmountjoy92/dashmachine) 14 | 15 | [![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-yellow.svg)](https://opensource.org/licenses/) 16 | [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome) 17 | 18 | [![Donate](https://img.shields.io/badge/$-support-ff69b4.svg?style=flat)](https://liberapay.com/rmountjoy) 19 | ![Bountysource](https://img.shields.io/bountysource/team/dashmachine/activity) 20 | 21 | Want a feature added now? [Open a bounty](https://www.bountysource.com/teams/dashmachine-app) 22 | 23 | ## Screenshots 24 | 25 | ![screenshot](https://raw.githubusercontent.com/rmountjoy92/DashMachine/master/screenshot1.png) 26 | 27 | ![screenshot](https://raw.githubusercontent.com/rmountjoy92/DashMachine/master/screenshot2.png) 28 | 29 | ![screenshot](https://raw.githubusercontent.com/rmountjoy92/DashMachine/master/screenshot3.png) 30 | 31 | ![screenshot](https://raw.githubusercontent.com/rmountjoy92/DashMachine/master/screenshot4.png) 32 | 33 | 34 | ## Installation 35 | ### Docker 36 | ``` 37 | docker create \ 38 | --name=dashmachine \ 39 | -p 5000:5000 \ 40 | -v path/to/data:/dashmachine/dashmachine/user_data \ 41 | --restart unless-stopped \ 42 | rmountjoy/dashmachine:latest 43 | ``` 44 | To run in a subfolder, use a CONTEXT_PATH environment variable. For example, to run at localhost:5000/dash: 45 | ``` 46 | docker create \ 47 | --name=dashmachine \ 48 | -p 5000:5000 \ 49 | -e CONTEXT_PATH=/dash 50 | -v path/to/data:/dashmachine/dashmachine/user_data \ 51 | --restart unless-stopped \ 52 | rmountjoy/dashmachine:latest 53 | ``` 54 | ### Synology 55 | Check out this awesome guide: https://nashosted.com/manage-your-self-hosted-applications-using-dashmachine/ 56 | ### Python 57 | Instructions are for linux. 58 | ``` 59 | virtualenv --python=python3 DashMachineEnv 60 | cd DashMachineEnv && source bin/activate 61 | git clone https://github.com/rmountjoy92/DashMachine.git 62 | cd DashMachine && pip install -r requirements.txt 63 | python3 run.py 64 | ``` 65 | Then open a web browser and go to localhost:5000 66 | 67 | ## Default user/password 68 | ``` 69 | User: admin 70 | Password: admin 71 | ``` 72 | 73 | ## Updating 74 | For python, use git. For docker, just pull the latest image and recreate the container. 75 | 76 | ## Configuration 77 | The user data folder is located at DashMachine/dashmachine/user_data. This is where the config.ini, custom backgrounds/icons, and the database file live. A reference for what can go into the config.ini file can be found on the settings page of the dashmachine by clicking the info icon next to 'Config'. 78 | 79 | ### Note 80 | If you change the config.ini file, you either have to restart the container (or python script) or click the 'save' button in the config section of settings for the config to be applied. Pictures added to the backgrounds/icons folders are available immediately. 81 | 82 | ## Want to contribute? 83 | Please use the pull request template at: 84 | https://github.com/rmountjoy92/DashMachine/blob/master/pull_request_template.md 85 | 86 | See this link for how to create a pull request: 87 | https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request 88 | 89 | ## Tech used 90 | * Flask (Python 3) 91 | * SQLalchemy w/ SQLite database 92 | * HTML5/Jinja2 93 | * Materialize css 94 | * JavaScript/jQuery/jQueryUI 95 | * .ini (for configuration) 96 | 97 | ## FAQs 98 | 1. application does not work in iframe 99 | see https://github.com/rmountjoy92/DashMachine/issues/6 100 | -------------------------------------------------------------------------------- /dashmachine/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import uuid 4 | from flask import Flask 5 | from flask_caching import Cache 6 | from flask_sqlalchemy import SQLAlchemy 7 | from flask_bcrypt import Bcrypt 8 | from flask_login import LoginManager 9 | from flask_restful import Api 10 | from dashmachine.paths import user_data_folder 11 | 12 | if not os.path.isdir(user_data_folder): 13 | os.mkdir(user_data_folder) 14 | 15 | secret_file = os.path.join(user_data_folder, ".secret") 16 | if not os.path.isfile(secret_file): 17 | with open(secret_file, "w") as new_file: 18 | new_file.write(uuid.uuid4().hex) 19 | 20 | with open(secret_file, "r") as secret_file: 21 | secret_key = secret_file.read().encode("utf-8") 22 | if len(secret_key) < 32: 23 | secret_key = uuid.uuid4().hex 24 | 25 | context_path = os.getenv("CONTEXT_PATH", "") 26 | app = Flask(__name__, static_url_path=context_path + "/static") 27 | cache = Cache(app, config={"CACHE_TYPE": "simple"}) 28 | api = Api(app) 29 | 30 | app.config["AVATARS_IDENTICON_BG"] = (255, 255, 255) 31 | app.config["SECRET_KEY"] = secret_key 32 | app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///user_data/site.db" 33 | app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False 34 | app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 0 35 | 36 | db = SQLAlchemy(app) 37 | bcrypt = Bcrypt(app) 38 | login_manager = LoginManager(app) 39 | app.jinja_env.add_extension("jinja2.ext.loopcontrols") 40 | 41 | from dashmachine.main.routes import main 42 | from dashmachine.user_system.routes import user_system 43 | from dashmachine.error_pages.routes import error_pages 44 | from dashmachine.settings_system.routes import settings_system 45 | from dashmachine import sources 46 | 47 | app.register_blueprint(main, url_prefix=context_path) 48 | app.register_blueprint(user_system, url_prefix=context_path) 49 | app.register_blueprint(error_pages, url_prefix=context_path) 50 | app.register_blueprint(settings_system, url_prefix=context_path) 51 | 52 | 53 | from dashmachine.rest_api.resources import * 54 | 55 | api.add_resource(GetVersion, "/api/version") 56 | -------------------------------------------------------------------------------- /dashmachine/cssmin.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | def cssmin(path): 5 | 6 | with open(path, "r") as f: 7 | css = f.read() 8 | 9 | # remove comments - this will break a lot of hacks :-P 10 | css = re.sub(r"\s*/\*\s*\*/", "$$HACK1$$", css) # preserve IE<6 comment hack 11 | css = re.sub(r"/\*[\s\S]*?\*/", "", css) 12 | css = css.replace("$$HACK1$$", "/**/") # preserve IE<6 comment hack 13 | 14 | # url() doesn't need quotes 15 | css = re.sub(r'url\((["\'])([^)]*)\1\)', r"url(\2)", css) 16 | 17 | # spaces may be safely collapsed as generated content will collapse them anyway 18 | css = re.sub(r"\s+", " ", css) 19 | 20 | # shorten collapsable colors: #aabbcc to #abc 21 | css = re.sub(r"#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3(\s|;)", r"#\1\2\3\4", css) 22 | 23 | # fragment values can loose zeros 24 | css = re.sub(r":\s*0(\.\d+([cm]m|e[mx]|in|p[ctx]))\s*;", r":\1;", css) 25 | 26 | return_string = "" 27 | for rule in re.findall(r"([^{]+){([^}]*)}", css): 28 | 29 | # we don't need spaces around operators 30 | selectors = [ 31 | re.sub(r"(?<=[\[\(>+=])\s+|\s+(?=[=~^$*|>+\]\)])", r"", selector.strip()) 32 | for selector in rule[0].split(",") 33 | ] 34 | 35 | # order is important, but we still want to discard repetitions 36 | properties = {} 37 | porder = [] 38 | for prop in re.findall("(.*?):(.*?)(;|$)", rule[1]): 39 | key = prop[0].strip().lower() 40 | if key not in porder: 41 | porder.append(key) 42 | properties[key] = prop[1].strip() 43 | 44 | # output rule if it contains any declarations 45 | if properties: 46 | return_string += "%s{%s}" % ( 47 | ",".join(selectors), 48 | "".join(["%s:%s;" % (key, properties[key]) for key in porder])[:-1], 49 | ) 50 | 51 | return return_string 52 | -------------------------------------------------------------------------------- /dashmachine/error_pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/error_pages/__init__.py -------------------------------------------------------------------------------- /dashmachine/error_pages/routes.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, render_template 2 | 3 | error_pages = Blueprint("error_pages", __name__) 4 | 5 | 6 | # ------------------------------------------------------------------------------ 7 | # Error Pages 8 | # ------------------------------------------------------------------------------ 9 | @error_pages.app_errorhandler(404) 10 | def error_404(error): 11 | return render_template("/error_pages/404.html"), 404 12 | 13 | 14 | @error_pages.app_errorhandler(403) 15 | def error_403(error): 16 | return render_template("/error_pages/403.html"), 403 17 | 18 | 19 | @error_pages.app_errorhandler(500) 20 | def error_500(error): 21 | return render_template("/error_pages/500.html"), 500 22 | 23 | 24 | @error_pages.route("/unauthorized") 25 | def unauthorized(): 26 | return render_template("/error_pages/unauthorized.html") 27 | -------------------------------------------------------------------------------- /dashmachine/main/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | -------------------------------------------------------------------------------- /dashmachine/main/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import SelectField 3 | 4 | 5 | class TagsForm(FlaskForm): 6 | tags = SelectField(choices=[("All tags", "All tags")]) 7 | -------------------------------------------------------------------------------- /dashmachine/main/models.py: -------------------------------------------------------------------------------- 1 | from dashmachine import db 2 | 3 | rel_app_data_source = db.Table( 4 | "rel_app_data_source", 5 | db.Column("data_source_id", db.Integer, db.ForeignKey("data_sources.id")), 6 | db.Column("app_id", db.Integer, db.ForeignKey("apps.id")), 7 | ) 8 | 9 | 10 | class Files(db.Model): 11 | id = db.Column(db.Integer, primary_key=True) 12 | name = db.Column(db.String()) 13 | path = db.Column(db.String()) 14 | external_path = db.Column(db.String()) 15 | cache = db.Column(db.String()) 16 | folder = db.Column(db.String()) 17 | 18 | 19 | class Apps(db.Model): 20 | id = db.Column(db.Integer, primary_key=True) 21 | name = db.Column(db.String()) 22 | prefix = db.Column(db.String()) 23 | url = db.Column(db.String()) 24 | icon = db.Column(db.String()) 25 | sidebar_icon = db.Column(db.String()) 26 | description = db.Column(db.String()) 27 | open_in = db.Column(db.String()) 28 | data_template = db.Column(db.String()) 29 | groups = db.Column(db.String()) 30 | tags = db.Column(db.String()) 31 | type = db.Column(db.String()) 32 | urls = db.Column(db.String()) 33 | 34 | 35 | class DataSources(db.Model): 36 | id = db.Column(db.Integer, primary_key=True) 37 | name = db.Column(db.String()) 38 | platform = db.Column(db.String()) 39 | args = db.relationship("DataSourcesArgs", backref="data_source") 40 | apps = db.relationship( 41 | "Apps", 42 | secondary=rel_app_data_source, 43 | backref=db.backref("data_sources", lazy="dynamic"), 44 | ) 45 | 46 | 47 | class DataSourcesArgs(db.Model): 48 | id = db.Column(db.Integer, primary_key=True) 49 | key = db.Column(db.String()) 50 | value = db.Column(db.String()) 51 | data_source_id = db.Column(db.Integer, db.ForeignKey("data_sources.id")) 52 | 53 | 54 | class Groups(db.Model): 55 | id = db.Column(db.Integer, primary_key=True) 56 | name = db.Column(db.String()) 57 | roles = db.Column(db.String()) 58 | 59 | 60 | class Tags(db.Model): 61 | id = db.Column(db.Integer, primary_key=True) 62 | name = db.Column(db.String()) 63 | icon = db.Column(db.String()) 64 | sort_pos = db.Column(db.Integer) 65 | -------------------------------------------------------------------------------- /dashmachine/main/routes.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | from secrets import token_hex 4 | from htmlmin.main import minify 5 | from configparser import ConfigParser 6 | from flask import render_template, url_for, redirect, request, Blueprint, jsonify 7 | from flask_login import current_user 8 | from dashmachine.main.models import Files, Apps, DataSources 9 | from dashmachine.main.utils import ( 10 | check_groups, 11 | get_data_source, 12 | mark_update_message_read, 13 | ) 14 | from dashmachine.user_system.models import User 15 | from dashmachine.settings_system.models import Settings 16 | from dashmachine.paths import cache_folder, user_data_folder 17 | from dashmachine import app, db 18 | 19 | 20 | main = Blueprint("main", __name__) 21 | 22 | 23 | # ------------------------------------------------------------------------------ 24 | # intial routes and functions (before/after request) 25 | # ------------------------------------------------------------------------------ 26 | @app.after_request 27 | def response_minify(response): 28 | """ 29 | minify html response to decrease site traffic 30 | """ 31 | if response.content_type == "text/html; charset=utf-8": 32 | response.set_data(minify(response.get_data(as_text=True))) 33 | 34 | return response 35 | return response 36 | 37 | 38 | # ------------------------------------------------------------------------------ 39 | # /home 40 | # ------------------------------------------------------------------------------ 41 | @main.route("/") 42 | @main.route("/home", methods=["GET"]) 43 | def home(): 44 | settings = Settings.query.first() 45 | if not check_groups(settings.home_access_groups, current_user): 46 | return redirect(url_for("error_pages.unauthorized")) 47 | return render_template("main/home.html") 48 | 49 | 50 | @main.route("/app_view?", methods=["GET"]) 51 | def app_view(app_id): 52 | settings = Settings.query.first() 53 | if not check_groups(settings.home_access_groups, current_user): 54 | return redirect(url_for("user_system.login")) 55 | app_db = Apps.query.filter_by(id=app_id).first() 56 | return render_template( 57 | "main/app-view.html", url=f"{app_db.prefix}{app_db.url}", title=app_db.name 58 | ) 59 | 60 | 61 | @main.route("/load_data_source", methods=["GET"]) 62 | def load_data_source(): 63 | data_source = DataSources.query.filter_by(id=request.args.get("id")).first() 64 | data = get_data_source(data_source) 65 | return data 66 | 67 | 68 | @main.route("/update_message_read", methods=["GET"]) 69 | def update_message_read(): 70 | mark_update_message_read() 71 | return "ok" 72 | 73 | 74 | # ------------------------------------------------------------------------------ 75 | # TCDROP routes 76 | # ------------------------------------------------------------------------------ 77 | @main.route("/tcdrop/cacheFile", methods=["POST"]) 78 | def cacheFile(): 79 | f = request.files.get("file") 80 | ext = f.filename.split(".")[1] 81 | random_hex = token_hex(16) 82 | fn = f"{random_hex}.{ext}" 83 | path = os.path.join(cache_folder, fn) 84 | f.save(path) 85 | html = render_template( 86 | "main/tcdrop-file-row.html", orig_fn=f.filename, fn=fn, id=random_hex 87 | ) 88 | file = Files(name=f.filename, path=path, cache=fn, folder="cache") 89 | db.session.add(file) 90 | db.session.commit() 91 | return jsonify(data={"cached": fn, "html": html}) 92 | 93 | 94 | @main.route("/tcdrop/clearCache", methods=["GET"]) 95 | def clearCache(): 96 | files = glob.glob(cache_folder + "/*") 97 | for file in files: 98 | if ".no" not in file: 99 | os.remove(file) 100 | Files.query.filter_by(folder="cache").delete() 101 | db.session.commit() 102 | return "success" 103 | 104 | 105 | @main.route("/tcdrop/deleteCachedFile", methods=["GET"]) 106 | def deleteCachedFile(): 107 | f = request.args.get("file") 108 | path = os.path.join(cache_folder, f) 109 | Files.query.filter_by(path=path).delete() 110 | db.session.commit() 111 | try: 112 | os.remove(path) 113 | except FileNotFoundError: 114 | pass 115 | return "success" 116 | 117 | 118 | # @main.route("/tcdrop/addLocalFile", methods=["GET"]) 119 | # def addLocalFile(): 120 | # f = request.args.get("file") 121 | # email_cache = request.args.get("email_cache") 122 | # ext = f.split(".")[1] 123 | # random_hex = token_hex(16) 124 | # fn = f"{random_hex}.{ext}" 125 | # if email_cache == "true": 126 | # file = Files.query.filter_by(cache=f).first() 127 | # orig_fn = file.name 128 | # old_path = os.path.join(email_cache_folder, f) 129 | # else: 130 | # old_path = os.path.join(pdf_folder, f) 131 | # orig_fn = f 132 | # path = os.path.join(cache_folder, fn) 133 | # copyfile(old_path, path) 134 | # html = render_template( 135 | # "main/tcdrop-file-row.html", orig_fn=orig_fn, fn=fn, id=random_hex 136 | # ) 137 | # file = Files(name=orig_fn, path=path, cache=fn, folder="cache") 138 | # db.session.add(file) 139 | # db.session.commit() 140 | # return jsonify(data={"file": fn, "html": html}) 141 | -------------------------------------------------------------------------------- /dashmachine/main/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | from shutil import copyfile 4 | from PIL import Image 5 | from markdown2 import markdown 6 | from dashmachine.paths import ( 7 | dashmachine_folder, 8 | images_folder, 9 | root_folder, 10 | user_data_folder, 11 | ) 12 | from dashmachine.main.models import Groups 13 | from dashmachine.main.read_config import read_config 14 | from dashmachine.version import version as dashmachine_version 15 | from dashmachine import db 16 | 17 | 18 | def row2dict(row): 19 | d = {} 20 | for column in row.__table__.columns: 21 | d[column.name] = str(getattr(row, column.name)) 22 | 23 | return d 24 | 25 | 26 | # establishes routes decorated w/ @public_route as accessible while not signed 27 | # in. See login and register routes for usage 28 | def public_route(decorated_function): 29 | decorated_function.is_public = True 30 | return decorated_function 31 | 32 | 33 | def dashmachine_init(): 34 | resize_template_app_images() 35 | db.create_all() 36 | db.session.commit() 37 | 38 | user_data_folder = os.path.join(dashmachine_folder, "user_data") 39 | 40 | # create the user_data subdirectories, link them to static 41 | user_backgrounds_folder = os.path.join(user_data_folder, "backgrounds") 42 | backgrounds_folder = os.path.join(images_folder, "backgrounds") 43 | if not os.path.isdir(user_backgrounds_folder): 44 | os.mkdir(user_backgrounds_folder) 45 | if not os.path.isdir(backgrounds_folder): 46 | os.symlink(user_backgrounds_folder, backgrounds_folder) 47 | 48 | user_icons_folder = os.path.join(user_data_folder, "icons") 49 | icons_folder = os.path.join(images_folder, "icons") 50 | if not os.path.isdir(user_icons_folder): 51 | os.mkdir(user_icons_folder) 52 | if not os.path.isdir(icons_folder): 53 | os.symlink(user_icons_folder, icons_folder) 54 | 55 | config_file = os.path.join(user_data_folder, "config.ini") 56 | if not os.path.exists(config_file): 57 | copyfile("default_config.ini", config_file) 58 | 59 | read_config() 60 | 61 | 62 | def check_groups(groups, current_user): 63 | if current_user.is_anonymous: 64 | current_user.role = "public_user" 65 | 66 | if groups: 67 | groups_list = groups.split(",") 68 | roles_list = [] 69 | for group in groups_list: 70 | group = Groups.query.filter_by(name=group.strip()).first() 71 | for group_role in group.roles.split(","): 72 | roles_list.append(group_role.strip()) 73 | if current_user.role in roles_list: 74 | return True 75 | else: 76 | return False 77 | else: 78 | if current_user.role == "admin": 79 | return True 80 | else: 81 | return False 82 | 83 | 84 | def get_data_source(data_source): 85 | data_source_args = {} 86 | for arg in data_source.args: 87 | arg = row2dict(arg) 88 | data_source_args[arg.get("key")] = arg.get("value") 89 | data_source = row2dict(data_source) 90 | module = importlib.import_module( 91 | f"dashmachine.platform.{data_source['platform']}", "." 92 | ) 93 | platform = module.Platform(data_source, **data_source_args) 94 | return platform.process() 95 | 96 | 97 | def resize_template_app_images(): 98 | folder = os.path.join(images_folder, "apps") 99 | for file in os.listdir(folder): 100 | fp = os.path.join(folder, file) 101 | image = Image.open(fp) 102 | image.thumbnail((64, 64)) 103 | image.save(fp) 104 | 105 | 106 | def get_update_message_html(): 107 | try: 108 | with open(os.path.join(user_data_folder, ".has_read_update"), "r") as has_read: 109 | has_read_version = has_read.read() 110 | except FileNotFoundError: 111 | has_read_version = None 112 | if not has_read_version or has_read_version.strip() != dashmachine_version: 113 | with open( 114 | os.path.join(root_folder, "update_message.md"), "r" 115 | ) as update_message: 116 | md = update_message.read() 117 | 118 | config_html = markdown( 119 | md, 120 | extras=[ 121 | "tables", 122 | "fenced-code-blocks", 123 | "break-on-newline", 124 | "header-ids", 125 | "code-friendly", 126 | ], 127 | ) 128 | return config_html 129 | else: 130 | return "" 131 | 132 | 133 | def mark_update_message_read(): 134 | with open(os.path.join(user_data_folder, ".has_read_update"), "w") as has_read: 135 | has_read.write(dashmachine_version) 136 | -------------------------------------------------------------------------------- /dashmachine/paths.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | 5 | # root path of the application 6 | def get_root_folder(): 7 | curr_folder = os.path.dirname(__file__) 8 | root_folder = Path(curr_folder).parent 9 | return root_folder 10 | 11 | 12 | root_folder = get_root_folder() 13 | 14 | dashmachine_folder = os.path.join(root_folder, "dashmachine") 15 | 16 | template_apps_folder = os.path.join(root_folder, "template_apps") 17 | 18 | platform_folder = os.path.join(dashmachine_folder, "platform") 19 | 20 | user_data_folder = os.path.join(dashmachine_folder, "user_data") 21 | 22 | auth_cache = os.path.join(user_data_folder, "auth_cache") 23 | 24 | if not os.path.isdir(auth_cache): 25 | os.mkdir(auth_cache) 26 | 27 | static_folder = os.path.join(dashmachine_folder, "static") 28 | 29 | images_folder = os.path.join(static_folder, "images") 30 | 31 | apps_images_folder = os.path.join(images_folder, "apps") 32 | 33 | backgrounds_images_folder = os.path.join(images_folder, "backgrounds") 34 | 35 | icons_images_folder = os.path.join(images_folder, "icons") 36 | 37 | cache_folder = os.path.join(static_folder, "cache") 38 | 39 | user_images_folder = os.path.join(images_folder, "user") 40 | -------------------------------------------------------------------------------- /dashmachine/platform/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/platform/__init__.py -------------------------------------------------------------------------------- /dashmachine/platform/curl.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | ##### curl 4 | Curl an URL and show result 5 | ```ini 6 | [variable_name] 7 | platform = curl 8 | resource = https://example.com 9 | value_template = {{value}} 10 | response_type = json 11 | ``` 12 | > **Returns:** `value_template` as rendered string 13 | 14 | | Variable | Required | Description | Options | 15 | |-----------------|----------|-----------------------------------------------------------------|-------------------| 16 | | [variable_name] | Yes | Name for the data source. | [variable_name] | 17 | | platform | Yes | Name of the platform. | curl | 18 | | resource | Yes | Url to curl | url | 19 | | value_template | Yes | Jinja template for how the returned data from api is displayed. | jinja template | 20 | | response_type | No | Response type. Use json if response is a JSON. Default is plain.| plain,json | 21 | 22 | > **Working example:** 23 | >```ini 24 | >[test] 25 | >platform = curl 26 | >resource = https://api.myip.com 27 | >value_template = My IP: {{value.ip}} 28 | response_type = json 29 | > 30 | >[MyIp.com] 31 | >prefix = https:// 32 | >url = myip.com 33 | >icon = static/images/apps/default.png 34 | >description = Link to myip.com 35 | >open_in = new_tab 36 | >data_sources = test 37 | >``` 38 | """ 39 | 40 | import requests 41 | from flask import render_template_string 42 | 43 | 44 | class Platform: 45 | def __init__(self, *args, **kwargs): 46 | # parse the user's options from the config entries 47 | for key, value in kwargs.items(): 48 | self.__dict__[key] = value 49 | 50 | # set defaults for omitted options 51 | if not hasattr(self, "response_type"): 52 | self.response_type = "plain" 53 | 54 | def process(self): 55 | if self.response_type.lower() == "json": 56 | try: 57 | value = requests.get(self.resource).json() 58 | print(value) 59 | except Exception as e: 60 | value = f"{e}" 61 | else: 62 | try: 63 | value = requests.get(self.resource) 64 | except Exception as e: 65 | value = f"{e}" 66 | 67 | return render_template_string(self.value_template, value=value) 68 | -------------------------------------------------------------------------------- /dashmachine/platform/deluge.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | ##### deluge 4 | Display information from Deluge web ui. 5 | ```ini 6 | [variable_name] 7 | platform = deluge 8 | resource = https://deluge.example.com:8112/json 9 | value_template = ↓{{download_rate|filesizeformat}}/s ↑{{upload_rate|filesizeformat}}/s 10 | password = MySecretPassword 11 | ``` 12 | > **Returns:** `value_template` as rendered string 13 | 14 | | Variable | Required | Description | Options | 15 | |-----------------|----------|-----------------------------------------------------------------|-------------------| 16 | | [variable_name] | Yes | Name for the data source. | [variable_name] | 17 | | platform | Yes | Name of the platform. | rest | 18 | | resource | Yes | Url of your deluge instance + '/json' | url | 19 | | value_template | Yes | Jinja template for how the returned data from api is displayed. | jinja template | 20 | | password | No | Password to use for auth. | string | 21 | 22 | > **Working example:** 23 | >``` 24 | >[deluge] 25 | >platform = deluge 26 | >resource = https://deluge.example.com:8112/json 27 | >value_template = ↓{{download_rate|filesizeformat}}/s ↑{{upload_rate|filesizeformat}}/s 28 | >password = MySecretPassword 29 | > 30 | >[Deluge] 31 | >prefix = https:// 32 | >url = https://deluge.example.com:8112 33 | >icon = static/images/apps/deluge.png 34 | >sidebar_icon = static/images/apps/deluge.png 35 | >description = Deluge is a lightweight, Free Software, cross-platform BitTorrent client 36 | >open_in = iframe 37 | >data_sources = deluge 38 | >``` 39 | 40 | """ 41 | 42 | from flask import render_template_string 43 | import requests 44 | 45 | 46 | class Platform: 47 | def __init__(self, *args, **kwargs): 48 | for key, value in kwargs.items(): 49 | self.__dict__[key] = value 50 | 51 | if not hasattr(self, "resource"): 52 | self.resource = "http://localhost/json" 53 | 54 | if not hasattr(self, "password"): 55 | self.password = "" 56 | 57 | self.id = 1 58 | self.session = requests.Session() 59 | self._api_call("auth.login", [self.password]) 60 | self.password = None # Discard password, no longer needed. 61 | 62 | def _api_call(self, method, params=[]): 63 | json = {"id": self.id, "method": method, "params": params} 64 | return self.session.post(self.resource, json=json) 65 | 66 | def process(self): 67 | r = self._api_call("web.update_ui", ["download_rate", "upload_rate"]) 68 | json = r.json() 69 | data = {} 70 | for key, field in json["result"]["stats"].items(): 71 | data[key] = field 72 | 73 | value_template = render_template_string(self.value_template, **data) 74 | return value_template 75 | -------------------------------------------------------------------------------- /dashmachine/platform/http_status.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | ##### http_status 4 | Make a http call on a given URL and display if the service is online. 5 | ```ini 6 | [variable_name] 7 | platform = http_status 8 | resource = https://your-website.com/api 9 | method = get 10 | authentication = basic 11 | username = my_username 12 | password = my_password 13 | headers = {"Content-Type": "application/json"} 14 | return_codes = 2xx,3xx 15 | ``` 16 | > **Returns:** a right-aligned colored bullet point on the app card. 17 | 18 | | Variable | Required | Description | Options | 19 | |-----------------|----------|-----------------------------------------------------------------|-------------------| 20 | | [variable_name] | Yes | Name for the data source. | [variable_name] | 21 | | platform | Yes | Name of the platform. | rest | 22 | | resource | Yes | Url of rest api resource. | url | 23 | | method | No | Method for the api call, default is GET | GET,HEAD,OPTIONS,TRACE| 24 | | authentication | No | Authentication for the api call, default is None | None,basic,digest | 25 | | username | No | Username to use for auth. | string | 26 | | password | No | Password to use for auth. | string | 27 | | headers | No | Request headers | json | 28 | | return_codes | No | Acceptable http status codes, x is handled as wildcard | string | 29 | 30 | > **Working example:** 31 | >```ini 32 | >[http_status_test] 33 | >platform = http_status 34 | >resource = https://google.com 35 | >return_codes = 2xx,3xx 36 | > 37 | >[Google] 38 | >prefix = https:// 39 | >url = google.com 40 | >icon = static/images/apps/default.png 41 | >open_in = this_tab 42 | >data_sources = http_status_test 43 | >``` 44 | 45 | """ 46 | 47 | from requests import Request, Session 48 | from requests.auth import HTTPBasicAuth, HTTPDigestAuth 49 | 50 | 51 | class Platform: 52 | def __init__(self, *args, **kwargs): 53 | # parse the user's options from the config entries 54 | for key, value in kwargs.items(): 55 | self.__dict__[key] = value 56 | 57 | # set defaults for omitted options 58 | if not hasattr(self, "method"): 59 | self.method = "GET" 60 | if not hasattr(self, "authentication"): 61 | self.authentication = None 62 | if not hasattr(self, "headers"): 63 | self.headers = None 64 | if not hasattr(self, "return_codes"): 65 | self.return_codes = "2xx,3xx" 66 | if not hasattr(self, "ssl_ignore"): 67 | self.ssl_ignore = "No" 68 | 69 | def process(self): 70 | # Check if method is within allowed methods for http_status 71 | if self.method.upper() not in ["GET", "HEAD", "OPTIONS", "TRACE"]: 72 | raise NotImplementedError 73 | 74 | s = Session() 75 | # prepare Authentication mechanism 76 | if self.authentication: 77 | if self.authentication.lower() == "digest": 78 | auth = HTTPDigestAuth(self.username, self.password) 79 | else: 80 | auth = HTTPBasicAuth(self.username, self.password) 81 | else: 82 | auth = None 83 | 84 | # Send request 85 | req = Request( 86 | self.method.upper(), self.resource, headers=self.headers, auth=auth 87 | ) 88 | prepped = req.prepare() 89 | if self.ssl_ignore == "yes": 90 | resp = s.send(prepped, verify=False) 91 | else: 92 | resp = s.send(prepped) 93 | resp = s.send(prepped) 94 | 95 | return_codes = tuple([x.replace("x", "") for x in self.return_codes.split(",")]) 96 | 97 | if str(resp.status_code).startswith(return_codes): 98 | icon_class = "theme-success-text" 99 | else: 100 | icon_class = "theme-failure-text" 101 | 102 | return f"fiber_manual_record " 103 | -------------------------------------------------------------------------------- /dashmachine/platform/ping.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | ##### ping 4 | Check if a service is online. 5 | ```ini 6 | [variable_name] 7 | platform = ping 8 | resource = 192.168.1.1 9 | ``` 10 | > **Returns:** a right-aligned colored bullet point on the app card. 11 | 12 | | Variable | Required | Description | Options | 13 | |-----------------|----------|-----------------------------------------------------------------|-------------------| 14 | | [variable_name] | Yes | Name for the data source. | [variable_name] | 15 | | platform | Yes | Name of the platform. | rest | 16 | | resource | Yes | Url of whatever you want to ping | url | 17 | 18 | 19 | """ 20 | 21 | import platform 22 | import subprocess 23 | 24 | 25 | class Platform: 26 | def __init__(self, *args, **kwargs): 27 | # parse the user's options from the config entries 28 | for key, value in kwargs.items(): 29 | self.__dict__[key] = value 30 | 31 | def process(self): 32 | param = "-n" if platform.system().lower() == "windows" else "-c" 33 | command = ["ping", param, "1", self.resource] 34 | up = subprocess.call(command) == 0 35 | 36 | if up is True: 37 | icon_class = "theme-success-text" 38 | else: 39 | icon_class = "theme-failure-text" 40 | 41 | return f"fiber_manual_record " 42 | -------------------------------------------------------------------------------- /dashmachine/platform/plex.py: -------------------------------------------------------------------------------- 1 | """ 2 | ##### Plex Media Server 3 | Connect to Plex Media Server and see current sessions details 4 | ```ini 5 | [variable_name] 6 | platform = plex 7 | url = http://plex_host:plex_port 8 | token = plex_token 9 | value_template = {{ value_template }} 10 | ``` 11 | > **Returns:** `value_template` as rendered string 12 | | Variable | Required | Description | Options | 13 | |-----------------|----------|-----------------------------------------------------------------|-------------------| 14 | | [variable_name] | Yes | Name for the data source. | [variable_name] | 15 | | platform | Yes | Name of the platform. | plex | 16 | | host | Yes | URL of Plex Media Server (include port, normally 32400) | url | 17 | | token | Yes | X-Plex-Token (See [here](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/) for how to find it.) | string | 18 | | value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template | 19 |
20 | ###### **Available fields for value_template** 21 | * sessions 22 | * transcodes 23 | * libraries 24 | > **Example:** 25 | >```ini 26 | >[plex] 27 | >platform = plex 28 | >host = http://plex.example.com:32400 29 | >token = abcde_fghi_jklmnopqr 30 | >value_template = Sessions: {{sessions}}
Transcodes: {{transcodes}} 31 | > 32 | >[Plex] 33 | >prefix = http:// 34 | >url = plex.example.com:32400 35 | >icon = static/images/apps/plex.png 36 | >description = Plex data sources example 37 | >open_in = this_tab 38 | >data_sources = plex 39 | >``` 40 | """ 41 | import requests 42 | from flask import render_template_string 43 | 44 | json_header = {"Accept": "application/json"} 45 | 46 | 47 | class Plex(object): 48 | def __init__(self, url, token): 49 | self.url = url 50 | self.token = token 51 | 52 | def refresh(self): 53 | if self.token != None: 54 | sessions = requests.get( 55 | self.url + "/status/sessions?X-Plex-Token=" + self.token, 56 | headers=json_header, 57 | ).json() 58 | 59 | self.sessions = sessions["MediaContainer"]["size"] 60 | 61 | transcodes = requests.get( 62 | self.url + "/transcode/sessions?X-Plex-Token=" + self.token, 63 | headers=json_header, 64 | ).json() 65 | 66 | self.transcodes = transcodes["MediaContainer"]["size"] 67 | 68 | libraries = requests.get( 69 | self.url + "/library/sections?X-Plex-Token=" + self.token, 70 | headers=json_header, 71 | ).json() 72 | 73 | self.libraries = libraries["MediaContainer"]["size"] 74 | 75 | 76 | class Platform: 77 | def __init__(self, *args, **kwargs): 78 | # parse the user's options from the config entries 79 | for key, value in kwargs.items(): 80 | self.__dict__[key] = value 81 | 82 | if not hasattr(self, "token"): 83 | print( 84 | "Please add a token\nSee https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/ to find it." 85 | ) 86 | exit(1) 87 | else: 88 | self.plex = Plex(self.host, self.token) 89 | 90 | def process(self): 91 | self.plex.refresh() 92 | value_template = render_template_string( 93 | self.value_template, **self.plex.__dict__ 94 | ) 95 | return value_template 96 | -------------------------------------------------------------------------------- /dashmachine/platform/rest.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | ##### rest 4 | Make a call on a REST API and display the results as a jinja formatted string. 5 | ```ini 6 | [variable_name] 7 | platform = rest 8 | resource = https://your-website.com/api 9 | value_template = {{value}} 10 | method = post 11 | authentication = basic 12 | username = my_username 13 | password = my_password 14 | payload = {"var1": "hi", "var2": 1} 15 | headers = {"Content-Type": "application/json"} 16 | verify = false 17 | ``` 18 | > **Returns:** `value_template` as rendered string 19 | 20 | | Variable | Required | Description | Options | 21 | |-----------------|----------|-----------------------------------------------------------------|-------------------| 22 | | [variable_name] | Yes | Name for the data source. | [variable_name] | 23 | | platform | Yes | Name of the platform. | rest | 24 | | resource | Yes | Url of rest api resource. | url | 25 | | value_template | Yes | Jinja template for how the returned data from api is displayed. | jinja template | 26 | | method | No | Method for the api call, default is GET | GET,POST | 27 | | authentication | No | Authentication for the api call, default is None | None,basic,digest | 28 | | username | No | Username to use for auth. | string | 29 | | password | No | Password to use for auth. | string | 30 | | payload | No | Payload for post request. | json | 31 | | headers | No | Custom headers for get or post | json | 32 | | verify | No | Turn TLS verification on or off, default is True | true,false | 33 | 34 | > **Working example:** 35 | >```ini 36 | >[test] 37 | >platform = rest 38 | >resource = https://pokeapi.co/api/v2/pokemon 39 | >value_template = Pokemon: {{value['count']}} 40 | > 41 | >[Pokemon] 42 | >prefix = https:// 43 | >url = pokemon.com 44 | >icon = static/images/apps/default.png 45 | >description = Data sources example 46 | >open_in = this_tab 47 | >data_sources = test 48 | >``` 49 | 50 | """ 51 | 52 | import json 53 | from requests import get, post 54 | from requests.auth import HTTPBasicAuth, HTTPDigestAuth 55 | from flask import render_template_string 56 | 57 | 58 | class Platform: 59 | def __init__(self, *args, **kwargs): 60 | # parse the user's options from the config entries 61 | for key, value in kwargs.items(): 62 | if key == "headers": 63 | value = json.loads(value) 64 | self.__dict__[key] = value 65 | 66 | # set defaults for omitted options 67 | if not hasattr(self, "method"): 68 | self.method = "GET" 69 | if not hasattr(self, "authentication"): 70 | self.authentication = None 71 | if not hasattr(self, "headers"): 72 | self.headers = None 73 | if not hasattr(self, "verify"): 74 | self.verify = True 75 | 76 | def process(self): 77 | if self.authentication: 78 | if self.authentication.lower() == "digest": 79 | auth = HTTPDigestAuth(self.username, self.password) 80 | else: 81 | auth = HTTPBasicAuth(self.username, self.password) 82 | else: 83 | auth = None 84 | 85 | verify = False if str(self.verify).lower() == "false" else True 86 | 87 | if self.method.upper() == "GET": 88 | try: 89 | value = get( 90 | self.resource, auth=auth, headers=self.headers, verify=verify 91 | ).json() 92 | except Exception as e: 93 | value = f"{e}" 94 | 95 | elif self.method.upper() == "POST": 96 | payload = json.loads(self.payload.replace("'", '"')) 97 | value = post( 98 | self.resource, 99 | data=payload, 100 | auth=auth, 101 | headers=self.headers, 102 | verify=verify, 103 | ) 104 | value_template = render_template_string(self.value_template, value=value) 105 | return value_template 106 | -------------------------------------------------------------------------------- /dashmachine/platform/transmission.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | ##### Transmission 4 | Display information from the Trasnmission API 5 | ```ini 6 | [variable_name] 7 | platform = transmission 8 | host = localhost 9 | port = 9091 10 | user = {{ transmission Web UI username }} 11 | password = {{ Transmission Web UI password }} 12 | value_template = {{ value_template }} 13 | ``` 14 | > **Returns:** `value_template` as rendered string 15 | 16 | | Variable | Required | Description | Options | 17 | |-----------------|----------|-----------------------------------------------------------------|-------------------| 18 | | [variable_name] | Yes | Name for the data source. | [variable_name] | 19 | | platform | Yes | Name of the platform. | transmission | 20 | | host | Yes | Host of Transmission Web UI | host | 21 | | port | Yes | Port of Transmission Web UI | port | 22 | | user | Yes | Username for Transmission Web UI | username | 23 | | password | Yes | Password for Transmission Web UI | password | 24 | | value_template | Yes | Jinja template for how the returned data from API is displayed. | jinja template | 25 | 26 |
27 | ###### **Available fields for value_template** 28 | 29 | * downloadSpeed 30 | * uploadSpeed 31 | * activeTorrentCount 32 | * pausedTorrentCount 33 | * torrentCount 34 | 35 | > **Working example:** 36 | >```ini 37 | > [transmission-data] 38 | > platform = transmission 39 | > host = 192.168.1.30 40 | > port = 9091 41 | > user = admin 42 | > password = password123 43 | > value_template = 🔽 {{(downloadSpeed/1024/1024)|round(2)}} MB/s
🔼 {{(uploadSpeed/1024/1024)|round(2)}} MB/s
Active: {{activeTorrentCount}}
44 | > 45 | > [Transmission] 46 | > prefix = http:// 47 | > url = 192.168.1.30:9091 48 | > icon = static/images/apps/transmission.png 49 | > description = A Fast, Easy, and Free BitTorrent Client 50 | > open_in = new_tab 51 | > data_sources = transmission-data 52 | >``` 53 | """ 54 | 55 | import json 56 | from flask import render_template_string 57 | import transmissionrpc 58 | 59 | 60 | # from pprint import PrettyPrinter 61 | # pp = PrettyPrinter() 62 | 63 | 64 | class Platform: 65 | def __init__(self, *args, **kwargs): 66 | # parse the user's options from the config entries 67 | for key, value in kwargs.items(): 68 | self.__dict__[key] = value 69 | 70 | if not hasattr(self, "port"): 71 | self.port = 9091 72 | if not hasattr(self, "host"): 73 | self.host = "localhost" 74 | 75 | self.tc = transmissionrpc.Client( 76 | self.host, port=self.port, user=self.user, password=self.password 77 | ) 78 | 79 | def process(self): 80 | 81 | torrents = len(self.tc.get_torrents()) 82 | data = {} 83 | for key, field in self.tc.session_stats().__dict__["_fields"].items(): 84 | data[key] = field.value 85 | # pp.pprint (data) 86 | 87 | value_template = render_template_string(self.value_template, **data) 88 | return value_template 89 | 90 | 91 | # Testing 92 | # test = Platform(host='192.168.1.19', user='', password='').process() 93 | -------------------------------------------------------------------------------- /dashmachine/rest_api/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | -------------------------------------------------------------------------------- /dashmachine/rest_api/resources.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource 2 | from dashmachine.version import version 3 | 4 | 5 | class GetVersion(Resource): 6 | def get(self): 7 | return {"Version": version} 8 | -------------------------------------------------------------------------------- /dashmachine/settings_system/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/settings_system/__init__.py -------------------------------------------------------------------------------- /dashmachine/settings_system/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import TextAreaField 3 | 4 | 5 | class ConfigForm(FlaskForm): 6 | config = TextAreaField() 7 | -------------------------------------------------------------------------------- /dashmachine/settings_system/models.py: -------------------------------------------------------------------------------- 1 | from dashmachine import db 2 | 3 | 4 | class Settings(db.Model): 5 | id = db.Column(db.Integer, primary_key=True) 6 | theme = db.Column(db.String()) 7 | accent = db.Column(db.String()) 8 | background = db.Column(db.String()) 9 | roles = db.Column(db.String()) 10 | home_access_groups = db.Column(db.String()) 11 | settings_access_groups = db.Column(db.String()) 12 | custom_app_title = db.Column(db.String()) 13 | sidebar_default = db.Column(db.String()) 14 | tags_expanded = db.Column(db.String()) 15 | -------------------------------------------------------------------------------- /dashmachine/settings_system/routes.py: -------------------------------------------------------------------------------- 1 | import os 2 | from shutil import move 3 | from configparser import ConfigParser 4 | from flask_login import current_user 5 | from flask import render_template, request, Blueprint, jsonify, redirect, url_for 6 | from dashmachine.user_system.forms import UserForm 7 | from dashmachine.user_system.models import User 8 | from dashmachine.main.utils import public_route, check_groups 9 | from dashmachine.main.read_config import read_config 10 | from dashmachine.main.models import Files 11 | from dashmachine.settings_system.forms import ConfigForm 12 | from dashmachine.settings_system.utils import load_files_html, get_config_html 13 | from dashmachine.settings_system.models import Settings 14 | from dashmachine.paths import ( 15 | backgrounds_images_folder, 16 | icons_images_folder, 17 | user_data_folder, 18 | template_apps_folder, 19 | ) 20 | from dashmachine.version import version, revision_number 21 | 22 | settings_system = Blueprint("settings_system", __name__) 23 | 24 | 25 | @public_route 26 | @settings_system.route("/settings", methods=["GET"]) 27 | def settings(): 28 | settings_db = Settings.query.first() 29 | if not check_groups(settings_db.settings_access_groups, current_user): 30 | return redirect(url_for("main.home")) 31 | 32 | config_form = ConfigForm() 33 | user_form = UserForm() 34 | user_form.role.choices += [(role, role) for role in settings_db.roles.split(",")] 35 | with open(os.path.join(user_data_folder, "config.ini"), "r") as config_file: 36 | config_form.config.data = config_file.read() 37 | files_html = load_files_html() 38 | 39 | template_apps = [] 40 | config = ConfigParser() 41 | for template_app_ini in os.listdir(template_apps_folder): 42 | config.read(os.path.join(template_apps_folder, template_app_ini)) 43 | entry = config[template_app_ini.replace(".ini", "")] 44 | template_apps.append(f"{template_app_ini.replace('.ini', '')}&&{entry['icon']}") 45 | 46 | users = User.query.all() 47 | config_readme = get_config_html() 48 | return render_template( 49 | "settings_system/settings.html", 50 | config_form=config_form, 51 | files_html=files_html, 52 | user_form=user_form, 53 | template_apps=",".join(template_apps), 54 | version=version, 55 | revision_number=revision_number, 56 | users=users, 57 | config_readme=config_readme, 58 | ) 59 | 60 | 61 | @settings_system.route("/settings/save_config", methods=["POST"]) 62 | def save_config(): 63 | with open(os.path.join(user_data_folder, "config.ini"), "w") as config_file: 64 | config_file.write(request.form.get("config")) 65 | msg = read_config() 66 | return jsonify(data=msg) 67 | 68 | 69 | @settings_system.route("/settings/add_images", methods=["POST"]) 70 | def add_images(): 71 | if request.form.get("folder") == "icons": 72 | dest_folder = icons_images_folder 73 | elif request.form.get("folder") == "backgrounds": 74 | dest_folder = backgrounds_images_folder 75 | for cached_file in request.form.get("files").split(","): 76 | file = Files.query.filter_by(cache=cached_file).first() 77 | new_path = os.path.join(dest_folder, file.name) 78 | move(file.path, new_path) 79 | return load_files_html() 80 | 81 | 82 | @settings_system.route("/settings/delete_file", methods=["GET"]) 83 | def delete_file(): 84 | if request.args.get("folder") == "backgrounds": 85 | file = os.path.join(backgrounds_images_folder, request.args.get("file")) 86 | if request.args.get("folder") == "icons": 87 | file = os.path.join(icons_images_folder, request.args.get("file")) 88 | os.remove(file) 89 | return load_files_html() 90 | 91 | 92 | @settings_system.route("/settings/get_app_template", methods=["GET"]) 93 | def get_app_template(): 94 | fn = os.path.join(template_apps_folder, f"{request.args.get('name')}.ini") 95 | with open(fn, "r") as template_app_ini: 96 | template = template_app_ini.read().replace("\n", "
") 97 | return template 98 | -------------------------------------------------------------------------------- /dashmachine/settings_system/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | from markdown2 import markdown 4 | from dashmachine.paths import ( 5 | backgrounds_images_folder, 6 | icons_images_folder, 7 | root_folder, 8 | platform_folder, 9 | ) 10 | from flask import render_template 11 | 12 | 13 | def load_files_html(): 14 | backgrounds = os.listdir(backgrounds_images_folder) 15 | icons = os.listdir(icons_images_folder) 16 | return render_template( 17 | "settings_system/files.html", backgrounds=backgrounds, icons=icons, 18 | ) 19 | 20 | 21 | def convert_html_to_md(md): 22 | html = markdown( 23 | md, 24 | extras=[ 25 | "tables", 26 | "fenced-code-blocks", 27 | "break-on-newline", 28 | "header-ids", 29 | "code-friendly", 30 | ], 31 | ) 32 | return html 33 | 34 | 35 | def get_config_html(): 36 | with open(os.path.join(root_folder, "readme_settings.md")) as readme_file: 37 | md = readme_file.read() 38 | html = {"settings": convert_html_to_md(md)} 39 | 40 | with open(os.path.join(root_folder, "readme_cards.md")) as readme_file: 41 | md = readme_file.read() 42 | html["cards"] = convert_html_to_md(md) 43 | 44 | with open(os.path.join(root_folder, "readme_data_sources.md")) as readme_file: 45 | md = readme_file.read() 46 | platforms = os.listdir(platform_folder) 47 | platforms = sorted(platforms) 48 | for platform in platforms: 49 | name, extension = os.path.splitext(platform) 50 | if extension.lower() == ".py": 51 | module = importlib.import_module(f"dashmachine.platform.{name}", ".") 52 | if module.__doc__: 53 | md += module.__doc__ 54 | html["data_sources"] = convert_html_to_md(md) 55 | 56 | return html 57 | -------------------------------------------------------------------------------- /dashmachine/site.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/site.db -------------------------------------------------------------------------------- /dashmachine/sources.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import random 4 | from jsmin import jsmin 5 | from flask_login import current_user 6 | from dashmachine import app 7 | from dashmachine.main.models import Apps, Tags 8 | from dashmachine.main.utils import check_groups, get_update_message_html 9 | from dashmachine.main.forms import TagsForm 10 | from dashmachine.settings_system.models import Settings 11 | from dashmachine.paths import static_folder, backgrounds_images_folder 12 | from dashmachine.cssmin import cssmin 13 | 14 | """This file establishes bundles of js and css sources, minifies them using jsmin and 15 | a dashmachine module named cssmin, adds script or style tag, uses a flask 16 | context processor to make the process functions available to every jinja template. 17 | Load orders in bundles are respected here""" 18 | 19 | """You can disable minification for debug purposes here (set to True) """ 20 | debug_js = False 21 | debug_css = False 22 | 23 | 24 | def process_js_sources(process_bundle=None, src=None, app_global=False): 25 | if src: 26 | process_bundle = [src] 27 | 28 | elif app_global is True: 29 | process_bundle = [ 30 | "global/dashmachine.js", 31 | "global/tcdrop.js", 32 | ] 33 | 34 | html = "" 35 | if debug_js is True: 36 | for source in process_bundle: 37 | html += f'' 38 | return html 39 | for source in process_bundle: 40 | source_path = os.path.join(static_folder, "js", source) 41 | with open(source_path) as js_file: 42 | minified = jsmin(js_file.read(), quote_chars="'\"`") 43 | html += f"" 44 | 45 | return html 46 | 47 | 48 | def process_css_sources(process_bundle=None, src=None, app_global=False): 49 | if src: 50 | process_bundle = [src] 51 | 52 | elif app_global is True: 53 | process_bundle = [ 54 | "global/style.css", 55 | "global/dashmachine-theme.css", 56 | "global/dashmachine.css", 57 | "global/tcdrop.css", 58 | ] 59 | 60 | html = "" 61 | if debug_css is True: 62 | for source in process_bundle: 63 | html += ( 64 | f'' 66 | ) 67 | return html 68 | else: 69 | for source in process_bundle: 70 | source_path = os.path.join(static_folder, "css", source) 71 | minified = cssmin(source_path) 72 | html += f"" 73 | 74 | return html 75 | 76 | 77 | def tag_sort_func(e): 78 | if not e.sort_pos: 79 | e.sort_pos = 99999 80 | return e.sort_pos 81 | 82 | 83 | @app.context_processor 84 | def context_processor(): 85 | apps = [] 86 | temp_tags = [] 87 | tags = [] 88 | apps_db = Apps.query.all() 89 | for app_db in apps_db: 90 | if app_db.urls: 91 | url_list = app_db.urls.replace("},{", "}%,%{").split("%,%") 92 | app_db.urls_json = [] 93 | for url in url_list: 94 | app_db.urls_json.append(json.loads(url)) 95 | if not app_db.groups: 96 | app_db.groups = None 97 | if check_groups(app_db.groups, current_user): 98 | apps.append(app_db) 99 | if app_db.tags: 100 | temp_tags += app_db.tags.split(",") 101 | 102 | tags_form = TagsForm() 103 | if len(temp_tags) > 0: 104 | temp_tags = list(dict.fromkeys([tag.strip() for tag in temp_tags])) 105 | tags_form.tags.choices += [(tag, tag) for tag in temp_tags] 106 | for tag in temp_tags: 107 | tag_db = Tags.query.filter_by(name=tag).first() 108 | if tag_db: 109 | tags.append(tag_db) 110 | tags.sort(key=tag_sort_func) 111 | settings = Settings.query.first() 112 | if settings.background == "random": 113 | if len(os.listdir(backgrounds_images_folder)) < 1: 114 | settings.background = None 115 | else: 116 | settings.background = ( 117 | f"static/images/backgrounds/" 118 | f"{random.choice(os.listdir(backgrounds_images_folder))}" 119 | ) 120 | update_message = get_update_message_html() 121 | return dict( 122 | test_key="test", 123 | process_js_sources=process_js_sources, 124 | process_css_sources=process_css_sources, 125 | apps=apps, 126 | settings=settings, 127 | tags=tags, 128 | tags_form=tags_form, 129 | update_message=update_message, 130 | ) 131 | -------------------------------------------------------------------------------- /dashmachine/static/cache/.no: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/cache/.no -------------------------------------------------------------------------------- /dashmachine/static/css/global/dashmachine-theme.css: -------------------------------------------------------------------------------- 1 | /* THEME VARIABLES */ 2 | :root { 3 | --theme-background: #EBEEF0; 4 | --theme-surface: #fff; 5 | --theme-surface-rgb: 255, 255, 255; 6 | --theme-surface-1: #fcfcfc; 7 | --theme-surface-2: #e0e0e0; 8 | --theme-primary: #FF9966; 9 | --theme-secondary: #9e9e9e; 10 | --theme-accent: #3399FF; 11 | --theme-color-font: #2c2f3a; 12 | --theme-color-font-muted: rgba(44, 47, 58, 0.85); 13 | --theme-color-font-muted2: rgba(44, 47, 58, 0.65); 14 | --theme-failure: #f44336; 15 | --theme-warning: #ffae42; 16 | --theme-success: #4BB543; 17 | --theme-on-primary: #fff; 18 | } 19 | [data-theme="dark"] { 20 | --theme-background: #1c1c1c; 21 | --theme-surface: #2f2f2f; 22 | --theme-surface-rgb: 47, 47, 47; 23 | --theme-surface-1: #434343; 24 | --theme-surface-2: #575757; 25 | --theme-color-font: #fff; 26 | --theme-color-font-muted: #f9f9f9; 27 | --theme-warning: #dc584e; 28 | } 29 | [data-accent="red"] { 30 | --theme-primary: #f44336; 31 | } 32 | [data-accent="pink"] { 33 | --theme-primary: #e91e63; 34 | } 35 | [data-accent="purple"] { 36 | --theme-primary: #9c27b0; 37 | } 38 | [data-accent="deepPurple"] { 39 | --theme-primary: #673ab7; 40 | } 41 | [data-accent="indigo"] { 42 | --theme-primary: #3f51b5; 43 | } 44 | [data-accent="blue"] { 45 | --theme-primary: #2196f3; 46 | } 47 | [data-accent="lightBlue"] { 48 | --theme-primary: #03a9f4; 49 | --theme-on-primary: #2c2f3a; 50 | } 51 | [data-accent="cyan"] { 52 | --theme-primary: #00bcd4; 53 | --theme-on-primary: #2c2f3a; 54 | } 55 | [data-accent="teal"] { 56 | --theme-primary: #009688; 57 | } 58 | [data-accent="green"] { 59 | --theme-primary: #4caf50; 60 | --theme-on-primary: #2c2f3a; 61 | } 62 | [data-accent="lightGreen"] { 63 | --theme-primary: #8bc34a; 64 | --theme-on-primary: #2c2f3a; 65 | } 66 | [data-accent="lime"] { 67 | --theme-primary: #cddc39; 68 | --theme-on-primary: #2c2f3a; 69 | } 70 | [data-accent="yellow"] { 71 | --theme-primary: #ffeb3b; 72 | --theme-on-primary: #2c2f3a; 73 | } 74 | [data-accent="amber"] { 75 | --theme-primary: #ffc107; 76 | --theme-on-primary: #2c2f3a; 77 | } 78 | [data-accent="deepOrange"] { 79 | --theme-primary: #ff5722; 80 | } 81 | [data-accent="brown"] { 82 | --theme-primary: #795548; 83 | } 84 | [data-accent="grey"] { 85 | --theme-primary: #9e9e9e; 86 | --theme-on-primary: #2c2f3a; 87 | } 88 | [data-accent="blueGrey"] { 89 | --theme-primary: #607d8b; 90 | } 91 | /* THEME CLASSES */ 92 | .theme-surface { 93 | background-color: var(--theme-surface) !important; 94 | } 95 | .theme-surface-1 { 96 | background-color: var(--theme-surface-1) !important; 97 | } 98 | .theme-background { 99 | background-color: var(--theme-background) !important; 100 | } 101 | .theme-primary { 102 | background-color: var(--theme-primary) !important; 103 | } 104 | .theme-primary-text { 105 | color: var(--theme-primary) !important; 106 | } 107 | .theme-secondary { 108 | background-color: var(--theme-secondary) !important; 109 | } 110 | .theme-secondary-text { 111 | color: var(--theme-secondary) !important; 112 | } 113 | .theme-accent { 114 | background-color: var(--theme-accent) !important; 115 | } 116 | .theme-accent-text { 117 | color: var(--theme-accent) !important; 118 | } 119 | .theme-text { 120 | color: var(--theme-color-font) !important; 121 | } 122 | .theme-failure { 123 | background-color: var(--theme-failure) !important; 124 | } 125 | .theme-failure-text { 126 | color: var(--theme-failure) !important; 127 | } 128 | .theme-warning { 129 | background-color: var(--theme-warning) !important; 130 | } 131 | .theme-warning-text { 132 | color: var(--theme-warning) !important; 133 | } 134 | .theme-success { 135 | background-color: var(--theme-success) !important; 136 | } 137 | .theme-success-text { 138 | color: var(--theme-success) !important; 139 | } 140 | .theme-muted-text { 141 | color: var(--theme-color-font-muted) !important; 142 | } 143 | .theme-muted2-text { 144 | color: var(--theme-color-font-muted2) !important; 145 | } 146 | .theme-surface-transparent { 147 | background: rgba(var(--theme-surface-rgb), 0.8) !important; 148 | } 149 | .theme-surface-transparent1 { 150 | background: rgba(var(--theme-surface-rgb), 0.9) !important; 151 | } 152 | .theme-on-primary { 153 | background-color: var(--theme-on-primary) !important; 154 | } 155 | .theme-on-primary-text { 156 | color: var(--theme-on-primary) !important; 157 | } -------------------------------------------------------------------------------- /dashmachine/static/css/global/tcdrop.css: -------------------------------------------------------------------------------- 1 | .tcdrop-form .tcdrop { 2 | width: 0.1px; 3 | height: 0.1px; 4 | opacity: 0; 5 | overflow: hidden; 6 | position: absolute; 7 | z-index: -1; 8 | } 9 | .tcdrop-form .tcdrop + label { 10 | width: 100%; 11 | height: 100%; 12 | font-size: 1.25em; 13 | font-weight: 700; 14 | color: var(--theme-color-font); 15 | display: inline-block; 16 | cursor: pointer; 17 | background: var(--theme-surface-2); 18 | padding: 25px; 19 | border-radius: 18px; 20 | text-align: center; 21 | } 22 | .tcdrop-file-hover { 23 | border: 2px dashed var(--theme-secondary); 24 | padding: 23px !important; 25 | } 26 | .tcdrop-form .tcdrop + label:hover { 27 | border: 2px dashed var(--theme-secondary); 28 | padding: 23px; 29 | } 30 | .tcdrop-li { 31 | margin-top: 10px; 32 | background: var(--theme-surface-2); 33 | padding: 15px; 34 | border-radius: 18px; 35 | color: var(--theme-color-font); 36 | text-align: left; 37 | } 38 | .tcdrop-li .material-icons { 39 | color: white; 40 | position: relative; 41 | margin-right: 10px; 42 | padding: 5px; 43 | border-radius: 24px; 44 | background: var(--theme-primary); 45 | } 46 | .tcdrop-li .material-icons:hover { 47 | -webkit-box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .3); 48 | box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .3); 49 | } 50 | .tcdrop-li .file-name { 51 | position: relative; 52 | bottom: 5px; 53 | } 54 | #tcdrop-loading-upload { 55 | text-align: center; 56 | font-size: 1.25em; 57 | font-weight: 700; 58 | } 59 | -------------------------------------------------------------------------------- /dashmachine/static/css/global/z-index-guide.txt: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------------------- 2 | APP MAIN 3 | ---------------------------------------------------------------------------------------- 4 | 0: body 5 | 0-100: main page elems 6 | 100 : feed 7 | 101-200: navbar & elems 8 | 201: email-panel 9 | 201-300: modals 10 | -------------------------------------------------------------------------------- /dashmachine/static/css/main/home.css: -------------------------------------------------------------------------------- 1 | 2 | .tags-select-col { 3 | position: relative; 4 | top: 15px; 5 | margin: 0; 6 | border-radius: .4rem; 7 | height: 45px; 8 | -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12); 9 | box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.12); 10 | } 11 | .tags-select-col .select-wrapper { 12 | top: 5px; 13 | } 14 | .tags-select-col .select-wrapper input { 15 | color: var(--theme-secondary); 16 | } 17 | @media screen and (max-width: 992px) { 18 | .tags-select-col { 19 | top: 0; 20 | /*width: calc(100vw - 45px) !important;*/ 21 | /*margin-left: 15px !important;*/ 22 | } 23 | } 24 | 25 | .app-card .card-reveal { 26 | position: 27 | 28 | } 29 | 30 | #list-view-collection .app-a { 31 | background: rgba(var(--theme-surface-rgb), 0.8); 32 | } 33 | #list-view-collection .app-a:hover { 34 | background: var(--theme-surface-1); 35 | } 36 | #list-view-collection .app-name { 37 | font-size: 1.3rem; 38 | position: relative; 39 | margin-left: 1rem; 40 | bottom: 2px; 41 | } 42 | #list-view-collection .app-description { 43 | margin-left: .4rem; 44 | } 45 | #list-view-collection .app-icon { 46 | height: 24px; 47 | position: relative; 48 | top: 4px; 49 | } 50 | #list-view-collection { 51 | border: 0; 52 | } 53 | #list-view-collection .data-source-container { 54 | position: relative; 55 | top: 6px; 56 | } 57 | #list-view-collection .material-icons-outlined { 58 | font-size: 1.2rem; 59 | position: relative; 60 | margin-left: .5rem; 61 | top: 1px; 62 | } -------------------------------------------------------------------------------- /dashmachine/static/css/main/login.css: -------------------------------------------------------------------------------- 1 | /*---------------------------------------- 2 | Login Page 3 | ------------------------------------------*/ 4 | .login-bg 5 | { 6 | background-image: url('../../images/gallery/flat-bg.jpg'); 7 | background-repeat: no-repeat; 8 | -webkit-background-size: cover; 9 | background-size: cover; 10 | } 11 | 12 | #login-page 13 | { 14 | display: -webkit-box; 15 | display: -webkit-flex; 16 | display: -moz-box; 17 | display: -ms-flexbox; 18 | display: flex; 19 | 20 | height: 100vh; 21 | 22 | -webkit-box-pack: center; 23 | -webkit-justify-content: center; 24 | -moz-box-pack: center; 25 | -ms-flex-pack: center; 26 | justify-content: center; 27 | -webkit-box-align: center; 28 | -webkit-align-items: center; 29 | -moz-box-align: center; 30 | -ms-flex-align: center; 31 | align-items: center; 32 | } 33 | #login-page .card-panel.border-radius-6.login-card 34 | { 35 | margin-left: 0 !important; 36 | } 37 | -------------------------------------------------------------------------------- /dashmachine/static/css/main/page-404.css: -------------------------------------------------------------------------------- 1 | /*---------------------------------------- 2 | 404 Page 3 | ------------------------------------------*/ 4 | .section.section-404 5 | { 6 | display: -webkit-box; 7 | display: -webkit-flex; 8 | display: -moz-box; 9 | display: -ms-flexbox; 10 | display: flex; 11 | overflow: hidden; 12 | 13 | height: 100vh; 14 | width: 100%; 15 | 16 | background: #fff; 17 | 18 | -webkit-box-pack: center; 19 | -webkit-justify-content: center; 20 | -moz-box-pack: center; 21 | -ms-flex-pack: center; 22 | justify-content: center; 23 | -webkit-box-align: center; 24 | -webkit-align-items: center; 25 | -moz-box-align: center; 26 | -ms-flex-align: center; 27 | align-items: center; 28 | color: #313541; 29 | } 30 | .section.section-404 .error-code 31 | { 32 | font-size: 10rem !important; 33 | color: #313541; 34 | } 35 | 36 | @media screen and (min-width: 992px) 37 | { 38 | .section.section-404 .error-code 39 | { 40 | font-size: 5rem !important; 41 | } 42 | .section.section-404 .bg-image-404 43 | { 44 | width: 70% !important; 45 | } 46 | } 47 | 48 | @media screen and (min-width: 769px) 49 | { 50 | .bg-image-404 51 | { 52 | width: 100% !important; 53 | } 54 | } 55 | 56 | @media screen and (max-width: 768px) 57 | { 58 | .section.section-404 .error-code 59 | { 60 | font-size: 5rem !important; 61 | } 62 | .section.section-404 .bg-image-404 63 | { 64 | width: 100%; 65 | } 66 | } 67 | 68 | @media screen and (max-width: 540px) 69 | { 70 | .section.section-404 .error-code 71 | { 72 | font-size: 2rem !important; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /dashmachine/static/css/settings_system/settings.css: -------------------------------------------------------------------------------- 1 | 2 | @media (min-width: 990px) 3 | { 4 | body { 5 | overflow: hidden !important; 6 | } 7 | } 8 | 9 | @media (max-width: 990px) 10 | { 11 | body { 12 | max-height: 200vh !important; 13 | } 14 | } 15 | 16 | .settings-page-card-right { 17 | max-height: calc(100vh - 142px) !important; 18 | min-height: calc(100vh - 142px) !important; 19 | } 20 | .settings-page-card-left { 21 | max-height: calc(100vh - 130px) !important; 22 | min-height: calc(100vh - 130px) !important; 23 | } 24 | 25 | #apps .dropdown-content { 26 | border-top-left-radius: 0px; 27 | border-top-right-radius: 0px; 28 | background: var(--theme-surface-1); 29 | } 30 | 31 | #settings-readme h5, #cards-readme h5, #data-sources-readme h5 { 32 | color: var(--theme-primary); 33 | margin-top: 5%; 34 | } 35 | #settings-readme h4, #cards-readme h4, #data-sources-readme h4 { 36 | color: var(--theme-color-font-muted); 37 | margin-top: 5%; 38 | } 39 | #configini-readme { 40 | margin-top: 2% !important; 41 | } 42 | #settings-readme code, #cards-readme code, #data-sources-readme code { 43 | -webkit-touch-callout: all; 44 | -webkit-user-select: all; 45 | -khtml-user-select: all; 46 | -moz-user-select: all; 47 | -ms-user-select: all; 48 | user-select: all; 49 | cursor: text; 50 | white-space: pre-wrap; 51 | } 52 | #settings-readme th, #cards-readme th, #data-sources-readme th { 53 | color: var(--theme-primary); 54 | } 55 | #settings-readme td, #cards-readme td, #data-sources-readme td { 56 | -webkit-touch-callout: text !important; 57 | -webkit-user-select: text !important; 58 | -khtml-user-select: text !important; 59 | -moz-user-select: text !important; 60 | -ms-user-select: text !important; 61 | user-select: text !important; 62 | cursor: text; 63 | } 64 | #settings-readme strong, #cards-readme strong, #data-sources-readme strong { 65 | font-weight: 900; 66 | } -------------------------------------------------------------------------------- /dashmachine/static/css/vendors/fonts/gelasio-kh.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/css/vendors/fonts/gelasio-kh.woff2 -------------------------------------------------------------------------------- /dashmachine/static/css/vendors/fonts/gelasio-yq.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/css/vendors/fonts/gelasio-yq.woff2 -------------------------------------------------------------------------------- /dashmachine/static/css/vendors/fonts/material-icons-outlined.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/css/vendors/fonts/material-icons-outlined.woff2 -------------------------------------------------------------------------------- /dashmachine/static/css/vendors/fonts/material-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/css/vendors/fonts/material-icons.woff2 -------------------------------------------------------------------------------- /dashmachine/static/css/vendors/fonts/montserrat-bi.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/css/vendors/fonts/montserrat-bi.woff2 -------------------------------------------------------------------------------- /dashmachine/static/css/vendors/fonts/montserrat-yw.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/css/vendors/fonts/montserrat-yw.woff2 -------------------------------------------------------------------------------- /dashmachine/static/css/vendors/google-fonts.css: -------------------------------------------------------------------------------- 1 | /* vietnamese */ 2 | @font-face { 3 | font-family: 'Gelasio'; 4 | font-style: normal; 5 | font-weight: 400; 6 | font-display: swap; 7 | src: local('Gelasio Regular'), local('Gelasio-Regular'), url(fonts/gelasio-kh.woff2) format('woff2'); 8 | unicode-range: U+0102-0103, U+0110-0111, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; 9 | } 10 | /* latin-ext */ 11 | @font-face { 12 | font-family: 'Gelasio'; 13 | font-style: normal; 14 | font-weight: 400; 15 | font-display: swap; 16 | src: local('Gelasio Regular'), local('Gelasio-Regular'), url(fonts/gelasio-kh.woff2) format('woff2'); 17 | unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; 18 | } 19 | /* latin */ 20 | @font-face { 21 | font-family: 'Gelasio'; 22 | font-style: normal; 23 | font-weight: 400; 24 | font-display: swap; 25 | src: local('Gelasio Regular'), local('Gelasio-Regular'), url(fonts/gelasio-yq.woff2) format('woff2'); 26 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 27 | } 28 | /* cyrillic-ext */ 29 | @font-face { 30 | font-family: 'Montserrat'; 31 | font-style: normal; 32 | font-weight: 400; 33 | font-display: swap; 34 | src: local('Montserrat Regular'), local('Montserrat-Regular'), url(fonts/montserrat-bi.woff2) format('woff2'); 35 | unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; 36 | } 37 | /* cyrillic */ 38 | @font-face { 39 | font-family: 'Montserrat'; 40 | font-style: normal; 41 | font-weight: 400; 42 | font-display: swap; 43 | src: local('Montserrat Regular'), local('Montserrat-Regular'), url(fonts/montserrat-bi.woff2) format('woff2'); 44 | unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; 45 | } 46 | /* vietnamese */ 47 | @font-face { 48 | font-family: 'Montserrat'; 49 | font-style: normal; 50 | font-weight: 400; 51 | font-display: swap; 52 | src: local('Montserrat Regular'), local('Montserrat-Regular'), url(fonts/montserrat-bi.woff2) format('woff2'); 53 | unicode-range: U+0102-0103, U+0110-0111, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; 54 | } 55 | /* latin-ext */ 56 | @font-face { 57 | font-family: 'Montserrat'; 58 | font-style: normal; 59 | font-weight: 400; 60 | font-display: swap; 61 | src: local('Montserrat Regular'), local('Montserrat-Regular'), url(fonts/montserrat-bi.woff2) format('woff2'); 62 | unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; 63 | } 64 | /* latin */ 65 | @font-face { 66 | font-family: 'Montserrat'; 67 | font-style: normal; 68 | font-weight: 400; 69 | font-display: swap; 70 | src: local('Montserrat Regular'), local('Montserrat-Regular'), url(fonts/montserrat-yw.woff2) format('woff2'); 71 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 72 | } -------------------------------------------------------------------------------- /dashmachine/static/css/vendors/material-icons.css: -------------------------------------------------------------------------------- 1 | /* fallback */ 2 | @font-face { 3 | font-family: 'Material Icons'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url(fonts/material-icons.woff2) format('woff2'); 7 | } 8 | /* fallback */ 9 | @font-face { 10 | font-family: 'Material Icons Outlined'; 11 | font-style: normal; 12 | font-weight: 400; 13 | src: url(fonts/material-icons-outlined.woff2) format('woff2'); 14 | } 15 | 16 | 17 | .material-icons { 18 | font-family: 'Material Icons'; 19 | font-weight: normal; 20 | font-style: normal; 21 | font-size: 24px; 22 | line-height: 1; 23 | letter-spacing: normal; 24 | text-transform: none; 25 | display: inline-block; 26 | white-space: nowrap; 27 | word-wrap: normal; 28 | direction: ltr; 29 | -webkit-font-feature-settings: 'liga'; 30 | -webkit-font-smoothing: antialiased; 31 | } 32 | 33 | .material-icons-outlined { 34 | font-family: 'Material Icons Outlined'; 35 | font-weight: normal; 36 | font-style: normal; 37 | font-size: 24px; 38 | line-height: 1; 39 | letter-spacing: normal; 40 | text-transform: none; 41 | display: inline-block; 42 | white-space: nowrap; 43 | word-wrap: normal; 44 | direction: ltr; 45 | -webkit-font-feature-settings: 'liga'; 46 | -webkit-font-smoothing: antialiased; 47 | } -------------------------------------------------------------------------------- /dashmachine/static/images/apps/Syncthing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/Syncthing.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/airsonic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/airsonic.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/alertmanager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/alertmanager.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/aria2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/aria2.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/bazarr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/bazarr.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/bitwarden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/bitwarden.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/bookstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/bookstack.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/calibre-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/calibre-web.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/cloudcmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/cloudcmd.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/cockpit-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/cockpit-project.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/default.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/deluge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/deluge.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/docker.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/dokuwiki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/dokuwiki.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/duplicati.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/duplicati.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/emby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/emby.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/filerun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/filerun.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/firefly_iii.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/firefly_iii.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/gitea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/gitea.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/glances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/glances.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/gogs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/gogs.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/gotify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/gotify.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/grafana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/grafana.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/guacamole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/guacamole.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/handbrake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/handbrake.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/healthchecks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/healthchecks.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/home-assistant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/home-assistant.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/homebridge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/homebridge.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/hp-ilo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/hp-ilo.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/icon.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/jackett.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/jackett.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/jdownloader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/jdownloader.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/jeedom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/jeedom.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/jellyfin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/jellyfin.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/jenkins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/jenkins.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/karma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/karma.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/kodi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/kodi.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/krusader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/krusader.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/lidarr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/lidarr.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/mailcow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/mailcow.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/netdata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/netdata.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/nextcloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/nextcloud.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/nginxproxymanager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/nginxproxymanager.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/nodered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/nodered.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/ombi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/ombi.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/openeats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/openeats.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/openmediavault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/openmediavault.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/openwrt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/openwrt.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/opnsense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/opnsense.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/pfsense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/pfsense.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/phoscon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/phoscon.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/phpmyadmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/phpmyadmin.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/pihole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/pihole.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/piwigo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/piwigo.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/plex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/plex.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/portainer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/portainer.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/privatebin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/privatebin.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/projectsend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/projectsend.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/prometheus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/prometheus.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/proxmox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/proxmox.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/qbittorrent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/qbittorrent.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/radarr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/radarr.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/riot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/riot.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/sonarr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/sonarr.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/statping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/statping.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-active-backup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-active-backup.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-audio-station.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-audio-station.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-calendar.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-download-station.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-download-station.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-drive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-drive.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-mailplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-mailplus.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-moments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-moments.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-note-station.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-note-station.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-office.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-office.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/synology-video-station.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/synology-video-station.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/tasmoadmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/tasmoadmin.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/tautulli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/tautulli.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/terminal.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/thelounge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/thelounge.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/traefik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/traefik.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/transmission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/transmission.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/unifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/unifi.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/vmware-esxi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/vmware-esxi.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/vscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/vscode.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/wallabag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/wallabag.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/wetty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/wetty.png -------------------------------------------------------------------------------- /dashmachine/static/images/apps/wikijs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/apps/wikijs.png -------------------------------------------------------------------------------- /dashmachine/static/images/favicon/Oapple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/favicon/Oapple-touch-icon-152x152.png -------------------------------------------------------------------------------- /dashmachine/static/images/favicon/Ofavicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/favicon/Ofavicon-32x32.png -------------------------------------------------------------------------------- /dashmachine/static/images/favicon/Omstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/favicon/Omstile-144x144.png -------------------------------------------------------------------------------- /dashmachine/static/images/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /dashmachine/static/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /dashmachine/static/images/favicon/favicons.gvdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/favicon/favicons.gvdesign -------------------------------------------------------------------------------- /dashmachine/static/images/favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /dashmachine/static/images/logo/logo.gvdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/logo/logo.gvdesign -------------------------------------------------------------------------------- /dashmachine/static/images/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/dashmachine/static/images/logo/logo.png -------------------------------------------------------------------------------- /dashmachine/static/js/global/tcdrop.js: -------------------------------------------------------------------------------- 1 | let tcdrop_files = {}; 2 | 3 | function tcdropValidateFilesLength(files, el){ 4 | let max_files = el.find(".tcdrop-form").attr('data-max-files'); 5 | if(max_files === -1){ 6 | tcdropValidateFilesType(files, el); 7 | } else { 8 | if (files.length > max_files) { 9 | el.find('.tcdrop-form').trigger("reset"); 10 | el.find(".tcdrop-error-msg").removeClass('hide'); 11 | el.find(".tcdrop-error-msg-txt").text('Only ' + max_files + ' file(s) are allowed'); 12 | } else { 13 | tcdropValidateFilesType(files, el); 14 | } 15 | } 16 | } 17 | 18 | function tcdropValidateFilesType(files, el){ 19 | let allowed_types_str = el.find(".tcdrop-form").attr('data-allowed-types'); 20 | if(allowed_types_str === 'all'){ 21 | tcdropCacheFile(files, el); 22 | return; 23 | } 24 | let allowed_types = allowed_types_str.split(','); 25 | let x = 0; 26 | $.each(files, function(){ 27 | let file_ext = this.name.split('.'); 28 | if(allowed_types.includes(file_ext[1])){ 29 | x= x + 1; 30 | } 31 | }); 32 | if(x > 0){ 33 | tcdropCacheFile(files, el); 34 | } else { 35 | el.find('.tcdrop-form').trigger("reset"); 36 | el.find(".tcdrop-error-msg").removeClass('hide'); 37 | el.find(".tcdrop-error-msg-txt").text('Only ' + allowed_types.toString() + ' files are allowed'); 38 | } 39 | } 40 | 41 | function tcdropCacheFile(files, el){ 42 | el.find(".tcdrop-loading-upload").removeClass('hide'); 43 | let url = el.find(".tcdrop-form").attr('data-cache-url'); 44 | let fd = new FormData(); 45 | $.each(files, function(){ 46 | fd.set('file', this); 47 | $.ajax({ 48 | url: url, 49 | type: 'POST', 50 | data: fd, 51 | cache: false, 52 | contentType: false, 53 | processData: false, 54 | success: function(data){ 55 | tcdrop_files[el.attr("id")].push(data.data.cached); 56 | el.find(".tcdrop-error-msg").addClass('hide'); 57 | el.find(".tcdrop-files-ul").append(data.data.html); 58 | el.find('.tcdrop-form').trigger("reset"); 59 | console.log(tcdrop_files); 60 | } 61 | }).done(function(){ 62 | tcdropHideDropArea(el); 63 | el.find(".tcdrop-loading-upload").addClass('hide'); 64 | }); 65 | }); 66 | } 67 | 68 | function tcdropHideDropArea(el){ 69 | let max_files = el.find(".tcdrop-form").attr('data-max-files'); 70 | if(tcdrop_files[el.attr("id")].length >= max_files){ 71 | el.find(".tcdrop-label").addClass('hide'); 72 | } else { 73 | el.find(".tcdrop-label").removeClass('hide'); 74 | } 75 | } 76 | 77 | function tcdropClearCache(){ 78 | let url = $(".tcdrop-form").attr('data-clear-cache-url'); 79 | $(".tcdrop-form").each(function(e) { 80 | tcdrop_files[$(this).parent().attr("id")] = []; 81 | }); 82 | $('.tcdrop-form').trigger("reset"); 83 | $.ajax({ 84 | url: url, 85 | type: 'GET' 86 | }); 87 | } 88 | 89 | function tcdropResetAll(){ 90 | $('.tcdrop-form').trigger("reset"); 91 | $(".tcdrop-error-msg").addClass('hide'); 92 | $(".tcdrop-label").removeClass('hide'); 93 | $(".tcdrop-file-li").addClass('hide'); 94 | tcdropClearCache(); 95 | } 96 | 97 | function tcdropAddLocalFile(file, url, email_cache="false", el){ 98 | el = $(el); 99 | el.find(".tcdrop-loading-upload").removeClass('hide'); 100 | $.ajax({ 101 | url: url, 102 | type: 'GET', 103 | data: {file: file, email_cache}, 104 | success: function(data){ 105 | tcdrop_files[el.attr("id")].push(data.data.file) 106 | el.find(".tcdrop-error-msg").addClass('hide'); 107 | el.find(".tcdrop-files-ul").append(data.data.html) 108 | el.find('.tcdrop-form').trigger("reset"); 109 | } 110 | }).done(function(){ 111 | tcdropHideDropArea(el); 112 | el.find(".tcdrop-loading-upload").addClass('hide'); 113 | }); 114 | } 115 | let droppedFiles = false; 116 | function initTCdrop(el){ 117 | el = $(el); 118 | tcdrop_files[el.attr("id")] = []; 119 | tcdropClearCache(); 120 | el.find('.tcdrop-form').unbind(); 121 | el.find(".tcdrop-input").unbind(); 122 | el.find(".tcdrop-error-msg-btn").unbind(); 123 | 124 | el.find('.tcdrop-form').on('drag dragstart dragend dragover dragenter dragleave drop', function(e) { 125 | e.preventDefault(); 126 | e.stopPropagation(); 127 | }) 128 | .on('dragover dragenter', function() { 129 | el.find(".tcdrop-label").addClass('tcdrop-file-hover'); 130 | }) 131 | .on('dragleave dragend drop', function() { 132 | el.find(".tcdrop-label").removeClass('tcdrop-file-hover'); 133 | }) 134 | .on('drop', function(e) { 135 | droppedFiles = e.originalEvent.dataTransfer.files; 136 | el.find(".tcdrop-input").prop("files", e.originalEvent.dataTransfer.files); 137 | tcdropValidateFilesLength(droppedFiles, el) 138 | }); 139 | el.find(".tcdrop-input").on('change', function(e){ 140 | let files = this.files; 141 | tcdropValidateFilesLength(files, el); 142 | }); 143 | el.find(".tcdrop-error-msg-btn").on('click', function(){ 144 | el.find(".tcdrop-error-msg").addClass('hide'); 145 | }); 146 | } 147 | -------------------------------------------------------------------------------- /dashmachine/static/js/main/home.js: -------------------------------------------------------------------------------- 1 | var d = document.getElementById("dashboard-sidenav"); 2 | d.className += " active theme-primary"; 3 | 4 | function get_data_source(el){ 5 | el.html(""); 6 | el.closest('.col').find('.data-source-loading').removeClass('hide'); 7 | $.ajax({ 8 | async: true, 9 | url: el.attr('data-url'), 10 | type: 'GET', 11 | data: {id: el.attr('data-id')}, 12 | success: function(data){ 13 | el.closest('.col').find('.data-source-loading').addClass('hide'); 14 | el.html(data); 15 | } 16 | }); 17 | } 18 | 19 | 20 | $( document ).ready(function() { 21 | $(".tooltipped").tooltip(); 22 | $("#apps-filter").on('keyup', function(e) { 23 | $(".toggle-tag-expand-btn").each(function(e) { 24 | if ($(this).attr("data-expanded") == 'false'){ 25 | $(this)[0].click(); 26 | } 27 | }); 28 | var value = $(this).val().toLowerCase(); 29 | 30 | $(".app-card").each(function(e) { 31 | var x = 0 32 | $(this).find('.searchable').each(function(e) { 33 | if ($(this).text().toLowerCase().indexOf(value) > -1) { 34 | x = x + 1 35 | } 36 | }); 37 | if (x > 0){ 38 | $(this).removeClass('hide'); 39 | } else { 40 | $(this).addClass('hide'); 41 | } 42 | }); 43 | 44 | $(".tag-group").each(function(i, e) { 45 | var x = 0 46 | $(this).find('.app-card').each(function(i, e) { 47 | if ($(this).hasClass("hide") === false){ 48 | x = x + 1 49 | } 50 | }); 51 | if (x === 0){ 52 | $(this).addClass('hide'); 53 | } else { 54 | $(this).removeClass('hide'); 55 | } 56 | }); 57 | }); 58 | 59 | $(".data-source-container").each(function(e) { 60 | get_data_source($(this)); 61 | }); 62 | 63 | $(".refresh-data-source-btn").on('click', function(e) { 64 | e.preventDefault(); 65 | $(this).closest('.app-card').find(".data-source-container").each(function(e) { 66 | get_data_source($(this)); 67 | }); 68 | }); 69 | 70 | $("#tags-select").on('change', function(e) { 71 | var value = $(this).val(); 72 | $(".tag-group").each(function(i, e) { 73 | if ($(this).find('.toggle-tag-expand-btn').attr("data-expanded") == "false"){ 74 | $(this).find('.toggle-tag-expand-btn')[0].click(); 75 | } 76 | if ($(this).attr("data-tag").indexOf(value) > -1 || value === "All tags") { 77 | $(this).removeClass('filtered'); 78 | } else { 79 | $(this).addClass('filtered'); 80 | } 81 | }); 82 | }); 83 | 84 | $(".toggle-tag-expand-btn").on('click', function(e) { 85 | if ($(this).attr("data-expanded") == "true"){ 86 | $(this).attr("data-expanded", "false"); 87 | $(this).text('keyboard_arrow_down'); 88 | $(this).closest('.tag-group').find('.tag-apps-row').addClass('hide'); 89 | } else { 90 | $(this).attr("data-expanded", "true"); 91 | $(this).text('keyboard_arrow_up'); 92 | $(this).closest('.tag-group').find('.tag-apps-row').removeClass('hide'); 93 | } 94 | var x = 0 95 | $(".toggle-tag-expand-btn").each(function(e) { 96 | if ($(this).attr("data-expanded") == "true") { 97 | x = x + 1 98 | } 99 | }); 100 | if (x > 0) { 101 | $("#toggle-tag-expand-all-btn").text('unfold_less'); 102 | } else { 103 | $("#toggle-tag-expand-all-btn").text('unfold_more'); 104 | } 105 | }); 106 | 107 | $("#toggle-tag-expand-all-btn").on('click', function(e) { 108 | if ($(this).text() == "unfold_more") { 109 | $(".toggle-tag-expand-btn").each(function(e) { 110 | $(this)[0].click(); 111 | }); 112 | } else { 113 | $(".toggle-tag-expand-btn").each(function(e) { 114 | if ($(this).attr("data-expanded") == "true"){ 115 | $(this)[0].click(); 116 | } 117 | }); 118 | } 119 | }); 120 | 121 | if ($("#settings-tags_expanded").val() == "False" || $("#user-tags_expanded").val() == "False"){ 122 | $(".toggle-tag-expand-btn").each(function(e) { 123 | $(this)[0].click(); 124 | }); 125 | if ($("#user-tags_expanded").val() == "True"){ 126 | $(".toggle-tag-expand-btn").each(function(e) { 127 | $(this)[0].click(); 128 | }); 129 | } 130 | } 131 | 132 | }); -------------------------------------------------------------------------------- /dashmachine/static/js/main/login.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | $(".login-form").on('submit', function(e) { 3 | e.preventDefault(); 4 | $.ajax({ 5 | url: $(this).attr('data-url'), 6 | type: 'POST', 7 | data: $(this).serialize(), 8 | success: function(data){ 9 | if (data.data.err){ 10 | M.toast({html: data.data.err, classes: 'theme-warning'}) 11 | } else { 12 | $(location).attr('href', data.data.url) 13 | } 14 | } 15 | }); 16 | }); 17 | }); -------------------------------------------------------------------------------- /dashmachine/static/js/settings_system/settings.js: -------------------------------------------------------------------------------- 1 | var d = document.getElementById("settings-sidenav"); 2 | d.className += " active theme-primary"; 3 | 4 | $( document ).ready(function() { 5 | $("#settings-readme table").addClass('responsive-table'); 6 | initTCdrop('#images-tcdrop'); 7 | $("#user-modal").modal({ 8 | onCloseEnd: function () { 9 | $("#edit-user-form").trigger('reset'); 10 | } 11 | }); 12 | 13 | $("#save-config-btn").on('click', function(e) { 14 | $.ajax({ 15 | url: $(this).attr('data-url'), 16 | type: 'POST', 17 | data: $("#config-form").serialize(), 18 | success: function(data){ 19 | if (data.data.msg === "success"){ 20 | M.toast({html: 'Config applied successfully'}); 21 | location.reload(true); 22 | } else { 23 | M.toast({html: data.data.msg, classes: "theme-failure"}); 24 | } 25 | } 26 | }); 27 | }); 28 | 29 | $("#save-images-btn").on('click', function(e) { 30 | $("#add-images-input").val(tcdrop_files['images-tcdrop'].toString()); 31 | $.ajax({ 32 | url: $(this).attr('data-url'), 33 | type: 'POST', 34 | data: $("#add-images-form").serialize(), 35 | success: function(data){ 36 | $("#add-images-form").trigger('reset'); 37 | $("#files-div").empty(); 38 | $("#files-div").append(data); 39 | tcdropResetAll(); 40 | } 41 | }); 42 | }); 43 | 44 | var template_apps = $("#templates-filter").attr("data-template_apps").split(','); 45 | var autocomplete_data = {} 46 | $.each(template_apps, function(i, e) { 47 | autocomplete_data[e.split('&&')[0]] = e.split('&&')[1] 48 | }); 49 | 50 | $("#templates-filter").autocomplete({ 51 | limit: 16, 52 | data: autocomplete_data, 53 | onAutocomplete: function () { 54 | $.ajax({ 55 | url: $("#templates-filter").attr('data-url'), 56 | type: 'GET', 57 | data: {name: $("#templates-filter").val()}, 58 | success: function(data){ 59 | $("#template-div").empty(); 60 | $("#template-div").append(data); 61 | } 62 | }); 63 | } 64 | }); 65 | 66 | $("#save-user-btn").on('click', function(e) { 67 | $.ajax({ 68 | url: $(this).attr('data-url'), 69 | type: 'POST', 70 | data: $("#edit-user-form").serialize(), 71 | success: function(data){ 72 | if (data.data.err !== 'success'){ 73 | M.toast({html: data.data.err, classes: 'theme-failure'}); 74 | } else { 75 | $("#users-div").empty(); 76 | $("#users-div").append(data.data.html); 77 | $("#user-modal").modal('close'); 78 | M.toast({html: 'User saved'}); 79 | } 80 | } 81 | }); 82 | }); 83 | 84 | }); -------------------------------------------------------------------------------- /dashmachine/templates/error_pages/403.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | 3 | {% block page_lvl_css %} 4 | 5 | {% endblock page_lvl_css %} 6 | 7 | 8 | {% block content %} 9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 |

403

17 |
Forbidden
18 | Back 19 | 20 |
21 |
22 |
23 |
24 |
25 |
26 | {% endblock content %} 27 | -------------------------------------------------------------------------------- /dashmachine/templates/error_pages/404.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | 3 | {% block page_lvl_css %} 4 | 5 | {% endblock page_lvl_css %} 6 | 7 | 8 | {% block content %} 9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 |

404

17 |
Not Found
18 | Back 19 | 20 |
21 |
22 |
23 |
24 |
25 |
26 | {% endblock content %} 27 | -------------------------------------------------------------------------------- /dashmachine/templates/error_pages/500.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | 3 | {% block page_lvl_css %} 4 | 5 | {% endblock page_lvl_css %} 6 | 7 | 8 | {% block content %} 9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 |

500

17 |
Internal Server Error
18 | Back 19 | 20 |
21 |
22 |
23 |
24 |
25 |
26 | {% endblock content %} 27 | -------------------------------------------------------------------------------- /dashmachine/templates/error_pages/unauthorized.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | {% from 'global_macros.html' import button %} 3 | 4 | 5 | {% block content %} 6 |
7 |
8 | warning 9 |

Unauthorized

10 | 11 |

This user account is not in the access group allowed to view this page.

12 | 13 | {{ button( 14 | float="center", 15 | href=url_for('user_system.login'), 16 | text="Go to login" 17 | ) }} 18 | 19 | {{ button( 20 | float="center", 21 | href="javascript:history.back()", 22 | text="Go back" 23 | ) }} 24 |
25 |
26 | {% endblock content %} 27 | -------------------------------------------------------------------------------- /dashmachine/templates/main/app-view.html: -------------------------------------------------------------------------------- 1 | {% extends "main/layout.html" %} 2 | {% block page_vendor_css %} 3 | {% endblock page_vendor_css %} 4 | 5 | {% block page_lvl_css %} 6 | {% endblock page_lvl_css %} 7 | 8 | {% block content %} 9 |
10 | 12 |
13 | 14 | {% endblock content %} 15 | 16 | {% block page_vendor_js %} 17 | {% endblock page_vendor_js %} 18 | 19 | {% block page_lvl_js %} 20 | 21 | {% endblock page_lvl_js %} -------------------------------------------------------------------------------- /dashmachine/templates/main/base.html: -------------------------------------------------------------------------------- 1 | {% from 'global_macros.html' import button %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 18 | 19 | 20 | 21 | 23 | 24 | {% if title %} 25 | {{ title }} - {{ settings.custom_app_title }} 26 | {% else %} 27 | {{ settings.custom_app_title }} 28 | {% endif %} 29 | 30 | 31 | 32 | 33 | 34 | 35 | {% block page_vendor_css %}{% endblock page_vendor_css %} 36 | 37 | 38 | {% block page_lvl_css %}{% endblock page_lvl_css %} 39 | 40 | 41 | 42 | {{ process_css_sources(app_global=True)|safe }} 43 | 44 | 45 | 46 | 47 | 49 | 50 | 51 | {# System settings from database #} 52 | 53 | 54 | 55 | 56 | 57 | {# User settings from database #} 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 85 | 86 | {% block header %}{% endblock header %} 87 | 88 | {% block sidenav %}{% endblock sidenav %} 89 | 90 | {% block content %}{% endblock content %} 91 | 92 | {% block footer %}{% endblock footer %} 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | {% block page_vendor_js %}{% endblock page_vendor_js %} 101 | 102 | 103 | 104 | {{ process_js_sources(app_global=True)|safe }} 105 | 106 | 107 | 108 | {% block page_lvl_js %}{% endblock page_lvl_js %} 109 | 110 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /dashmachine/templates/main/breadcrumb.html: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /dashmachine/templates/main/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | {% from 'main/macros.html' import AppAnchor %} 3 | 4 | {% block header %} 5 | 6 | 7 | 8 | {% endblock header %} 9 | 10 | {% block sidenav %} 11 |
12 | chevron_right 13 |
14 | 15 | 62 | 63 | 64 | {% endblock sidenav%} 65 | 66 | {% block footer %} 67 |
68 |
69 | 70 | {% if current_user.is_authenticated %} 71 | 72 | exit_to_app 73 | 74 | {% else %} 75 | 76 | account_circle 77 | 78 | {% endif %} 79 | 80 | settings 81 | 82 | 83 | dashboard 84 | 85 | 86 |
87 |
88 | 89 | 94 | 95 | {% endblock footer %} 96 | -------------------------------------------------------------------------------- /dashmachine/templates/main/tcdrop-file-row.html: -------------------------------------------------------------------------------- 1 |
  • 2 | close 6 | {{orig_fn}} 7 |
  • 8 | 36 | -------------------------------------------------------------------------------- /dashmachine/templates/main/tcdrop.html: -------------------------------------------------------------------------------- 1 | {% from "global_macros.html" import preload_circle %} 2 | {% macro tcdrop(allowed_types='all', max_files=-1, on_add="", id="", class="") %} 3 | 4 | 5 |
    6 |
    11 | 12 | 13 |
    14 | 23 |
    24 | 25 | 26 | {% endmacro %} 27 | -------------------------------------------------------------------------------- /dashmachine/templates/main/top-and-side-nav.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | 3 | {% block header %} 4 | 5 | 45 | 46 | {% endblock header %} 47 | 48 | {% block sidenav %} 49 | 50 | 63 | 64 | 65 | {% endblock sidenav%} 66 | 67 | {% block footer %} 68 | 69 | 74 | 75 | {% endblock footer %} 76 | -------------------------------------------------------------------------------- /dashmachine/templates/main/top-nav.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | {% from 'global_macros.html' import button %} 3 | 4 | {% block page_lvl_css %} 5 | {{ process_css_sources(src='site_system/site.css')|safe }} 6 | {% endblock page_lvl_css %} 7 | 8 | {% block header %} 9 | 23 | {% endblock header %} 24 | 25 | {% block sidenav %} 26 | 31 | {% endblock sidenav %} 32 | 33 | {% block footer %} 34 | {# #} 39 | {% endblock footer %} -------------------------------------------------------------------------------- /dashmachine/templates/page_template.html: -------------------------------------------------------------------------------- 1 | {% extends "main/top-and-side-nav.html" %} 2 | {% block page_vendor_css %} 3 | {% endblock page_vendor_css %} 4 | 5 | {% block page_lvl_css %} 6 | {% endblock page_lvl_css %} 7 | 8 | {% block body_class %} 9 | {% endblock body_class %} 10 | 11 | {% block content %} 12 | {% endblock content %} 13 | 14 | {% block page_vendor_js %} 15 | {% endblock page_vendor_js %} 16 | 17 | {% block page_lvl_js %} 18 | {% endblock page_lvl_js %} 19 | -------------------------------------------------------------------------------- /dashmachine/templates/user/add_payment_method.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |

    Billing Info

    8 |
    9 |
    10 | 11 |
    12 | {{ form.hidden_tag() }} 13 | 14 |
    15 |
    16 | {{ form.name(autocomplete="off", class="is-invalid") }} 17 | 18 |
    19 |
    20 | 21 |
    22 |
    23 | {{ form.card_number(autocomplete="off", class="is-invalid") }} 24 | 25 |
    26 |
    27 | 28 |
    29 |
    30 | {{ form.expiration_date(autocomplete="off", class="is-invalid") }} 31 | 32 |
    33 |
    34 | 35 |
    36 |
    37 | {{ form.cvv(autocomplete="off", class="is-invalid") }} 38 | 39 |
    40 |
    41 | 42 |
    43 |
    44 | {{ form.address(autocomplete="off", class="is-invalid") }} 45 | 46 |
    47 |
    48 | 49 |
    50 |
    51 | {{ form.city(autocomplete="off", class="is-invalid") }} 52 | 53 |
    54 |
    55 | {{ form.state(autocomplete="off", class="is-invalid") }} 56 | 57 |
    58 |
    59 | {{ form.zip_code(autocomplete="off", class="is-invalid") }} 60 | 61 |
    62 |
    63 | 64 | 65 |
    66 | {{ form.submit(class="waves-effect waves-light btn") }} 67 |
    68 | 69 |
    70 |
    71 |
    72 |
    73 |
    74 |
    75 | {% endblock content %} 76 | -------------------------------------------------------------------------------- /dashmachine/templates/user/login.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | 3 | {% block content %} 4 |
    5 |
    6 |
    7 |
    8 |
    9 | DashMachine Logo 10 |
    11 |
    12 | 41 |
    42 |
    43 |
    44 |
    45 |
    46 | {% endblock content %} 47 | 48 | {% block page_lvl_js %} 49 | {{ process_js_sources(src="main/login.js")|safe }} 50 | {% endblock page_lvl_js %} -------------------------------------------------------------------------------- /dashmachine/templates/user/register.html: -------------------------------------------------------------------------------- 1 | {% extends "main/base.html" %} 2 | {% from 'global_macros.html' import input, button %} 3 | {% block content %} 4 |
    5 |
    6 |
    Register
    7 | 8 |
    9 | {{input( 10 | id="register-fname", 11 | label="First Name", 12 | form_obj=form.fname, 13 | class="", 14 | size="s6" 15 | )}} 16 | 17 | {{input( 18 | id="register-lname", 19 | label="Last Name", 20 | form_obj=form.lname, 21 | class="", 22 | size="s6" 23 | )}} 24 | 25 | {{input( 26 | id="register-email", 27 | label="Email", 28 | form_obj=form.email, 29 | class="", 30 | size="s12" 31 | )}} 32 | 33 | {{input( 34 | id="register-password", 35 | label="Password", 36 | form_obj=form.password, 37 | class="", 38 | size="s12" 39 | )}} 40 | 41 | {{input( 42 | id="register-confirm-password", 43 | label="Confirm Password", 44 | form_obj=form.confirm_password, 45 | class="", 46 | size="s12" 47 | )}} 48 | 49 | {{button( 50 | text="submit", 51 | id="register-submit-btn", 52 | class="mb-2", 53 | data={'url': url_for('user_system.add_user'), 54 | 'done-url': url_for('user_system.login')} 55 | )}} 56 |
    57 | 58 |
    59 |
    60 | 61 | 74 | {% endblock content %} 75 | -------------------------------------------------------------------------------- /dashmachine/user_system/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | -------------------------------------------------------------------------------- /dashmachine/user_system/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField, PasswordField, BooleanField, SelectField 3 | from wtforms.validators import DataRequired, Length 4 | 5 | 6 | class UserForm(FlaskForm): 7 | username = StringField(validators=[DataRequired(), Length(min=1, max=120)]) 8 | 9 | password = PasswordField(validators=[DataRequired(), Length(min=8, max=120)]) 10 | 11 | role = SelectField(choices=[]) 12 | 13 | id = StringField() 14 | 15 | confirm_password = PasswordField() 16 | 17 | 18 | class LoginForm(FlaskForm): 19 | username = StringField(validators=[DataRequired()]) 20 | 21 | password = PasswordField(validators=[DataRequired()]) 22 | 23 | remember = BooleanField() 24 | -------------------------------------------------------------------------------- /dashmachine/user_system/models.py: -------------------------------------------------------------------------------- 1 | from dashmachine import db, login_manager 2 | from flask_login import UserMixin 3 | 4 | 5 | @login_manager.user_loader 6 | def load_user(user_id): 7 | return User.query.get(int(user_id)) 8 | 9 | 10 | class User(db.Model, UserMixin): 11 | id = db.Column(db.Integer, primary_key=True) 12 | username = db.Column(db.String(120), unique=True, nullable=False) 13 | password = db.Column(db.String(60), nullable=False) 14 | role = db.Column(db.String()) 15 | theme = db.Column(db.String()) 16 | background = db.Column(db.String()) 17 | accent = db.Column(db.String()) 18 | sidebar_default = db.Column(db.String()) 19 | tags_expanded = db.Column(db.String()) 20 | -------------------------------------------------------------------------------- /dashmachine/user_system/routes.py: -------------------------------------------------------------------------------- 1 | from flask import render_template, url_for, redirect, Blueprint, jsonify 2 | from flask_login import login_user, logout_user 3 | from dashmachine.user_system.forms import LoginForm 4 | from dashmachine.user_system.models import User 5 | from dashmachine import bcrypt 6 | from dashmachine.main.utils import public_route 7 | 8 | user_system = Blueprint("user_system", __name__) 9 | 10 | 11 | # ------------------------------------------------------------------------------ 12 | # User system routes 13 | # ------------------------------------------------------------------------------ 14 | # login page 15 | @public_route 16 | @user_system.route("/login", methods=["GET"]) 17 | def login(): 18 | form = LoginForm() 19 | return render_template("user/login.html", title="Login", form=form) 20 | 21 | 22 | @public_route 23 | @user_system.route("/check_login", methods=["POST"]) 24 | def check_login(): 25 | form = LoginForm() 26 | if form.validate_on_submit(): 27 | user = User.query.filter_by(username=form.username.data.lower()).first() 28 | if not user: 29 | response = {"err": "User not found"} 30 | 31 | elif bcrypt.check_password_hash(user.password, form.password.data): 32 | login_user(user, remember=form.remember.data) 33 | response = {"url": url_for("main.home")} 34 | else: 35 | response = {"err": "Password is wrong"} 36 | else: 37 | response = {"err": str(form.errors)} 38 | 39 | return jsonify(data=response) 40 | 41 | 42 | # this logs the user out and redirects to the login page 43 | @user_system.route("/logout") 44 | def logout(): 45 | 46 | logout_user() 47 | return redirect(url_for("user_system.login")) 48 | -------------------------------------------------------------------------------- /dashmachine/user_system/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dashmachine import bcrypt 3 | from dashmachine.paths import auth_cache 4 | from dashmachine.user_system.models import User 5 | 6 | 7 | def hash_and_cache_password(password, user_id): 8 | hashed_password = bcrypt.generate_password_hash(password).decode("utf-8") 9 | with open(os.path.join(auth_cache, str(user_id)), "w") as cache_file: 10 | cache_file.write(hashed_password) 11 | return hashed_password 12 | 13 | 14 | def get_cached_password(user_id): 15 | try: 16 | with open(os.path.join(auth_cache, str(user_id)), "r") as cache_file: 17 | password = cache_file.read() 18 | except FileNotFoundError: 19 | return hash_and_cache_password("admin", user_id) 20 | return password 21 | 22 | 23 | def clean_auth_cache(): 24 | for file in os.listdir(auth_cache): 25 | if not User.query.filter_by(id=file).first(): 26 | os.remove(os.path.join(auth_cache, file)) 27 | -------------------------------------------------------------------------------- /dashmachine/version.py: -------------------------------------------------------------------------------- 1 | version = "v0.5" 2 | revision_number = "4.1" 3 | -------------------------------------------------------------------------------- /default_config.ini: -------------------------------------------------------------------------------- 1 | [Settings] 2 | theme = light 3 | accent = orange 4 | background = None 5 | roles = admin,user,public_user 6 | home_access_groups = admin_only 7 | settings_access_groups = admin_only 8 | custom_app_title = DashMachine 9 | sidebar_default = open 10 | 11 | [admin] 12 | role = admin 13 | password = admin 14 | confirm_password = admin -------------------------------------------------------------------------------- /migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /migrations/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # template used to generate migration files 5 | # file_template = %%(rev)s_%%(slug)s 6 | 7 | # set to 'true' to run the environment during 8 | # the 'revision' command, regardless of autogenerate 9 | # revision_environment = false 10 | 11 | 12 | # Logging configuration 13 | [loggers] 14 | keys = root,sqlalchemy,alembic 15 | 16 | [handlers] 17 | keys = console 18 | 19 | [formatters] 20 | keys = generic 21 | 22 | [logger_root] 23 | level = WARN 24 | handlers = console 25 | qualname = 26 | 27 | [logger_sqlalchemy] 28 | level = WARN 29 | handlers = 30 | qualname = sqlalchemy.engine 31 | 32 | [logger_alembic] 33 | level = INFO 34 | handlers = 35 | qualname = alembic 36 | 37 | [handler_console] 38 | class = StreamHandler 39 | args = (sys.stderr,) 40 | level = NOTSET 41 | formatter = generic 42 | 43 | [formatter_generic] 44 | format = %(levelname)-5.5s [%(name)s] %(message)s 45 | datefmt = %H:%M:%S 46 | -------------------------------------------------------------------------------- /migrations/env.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | 3 | import logging 4 | from logging.config import fileConfig 5 | 6 | from sqlalchemy import engine_from_config 7 | from sqlalchemy import pool 8 | 9 | from alembic import context 10 | 11 | # this is the Alembic Config object, which provides 12 | # access to the values within the .ini file in use. 13 | config = context.config 14 | 15 | # Interpret the config file for Python logging. 16 | # This line sets up loggers basically. 17 | fileConfig(config.config_file_name) 18 | logger = logging.getLogger("alembic.env") 19 | 20 | # add your model's MetaData object here 21 | # for 'autogenerate' support 22 | # from myapp import mymodel 23 | # target_metadata = mymodel.Base.metadata 24 | from flask import current_app 25 | 26 | config.set_main_option( 27 | "sqlalchemy.url", 28 | current_app.config.get("SQLALCHEMY_DATABASE_URI").replace("%", "%%"), 29 | ) 30 | target_metadata = current_app.extensions["migrate"].db.metadata 31 | 32 | # other values from the config, defined by the needs of env.py, 33 | # can be acquired: 34 | # my_important_option = config.get_main_option("my_important_option") 35 | # ... etc. 36 | 37 | 38 | def run_migrations_offline(): 39 | """Run migrations in 'offline' mode. 40 | 41 | This configures the context with just a URL 42 | and not an Engine, though an Engine is acceptable 43 | here as well. By skipping the Engine creation 44 | we don't even need a DBAPI to be available. 45 | 46 | Calls to context.execute() here emit the given string to the 47 | script output. 48 | 49 | """ 50 | url = config.get_main_option("sqlalchemy.url") 51 | context.configure(url=url, target_metadata=target_metadata, literal_binds=True) 52 | 53 | with context.begin_transaction(): 54 | context.run_migrations() 55 | 56 | 57 | def run_migrations_online(): 58 | """Run migrations in 'online' mode. 59 | 60 | In this scenario we need to create an Engine 61 | and associate a connection with the context. 62 | 63 | """ 64 | 65 | # this callback is used to prevent an auto-migration from being generated 66 | # when there are no changes to the schema 67 | # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html 68 | def process_revision_directives(context, revision, directives): 69 | if getattr(config.cmd_opts, "autogenerate", False): 70 | script = directives[0] 71 | if script.upgrade_ops.is_empty(): 72 | directives[:] = [] 73 | logger.info("No changes in schema detected.") 74 | 75 | connectable = engine_from_config( 76 | config.get_section(config.config_ini_section), 77 | prefix="sqlalchemy.", 78 | poolclass=pool.NullPool, 79 | ) 80 | 81 | with connectable.connect() as connection: 82 | context.configure( 83 | connection=connection, 84 | target_metadata=target_metadata, 85 | process_revision_directives=process_revision_directives, 86 | **current_app.extensions["migrate"].configure_args 87 | ) 88 | 89 | with context.begin_transaction(): 90 | context.run_migrations() 91 | 92 | 93 | if context.is_offline_mode(): 94 | run_migrations_offline() 95 | else: 96 | run_migrations_online() 97 | -------------------------------------------------------------------------------- /migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /migrations/versions/7450bb0ce3ff_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7450bb0ce3ff 4 | Revises: 5 | Create Date: 2020-03-19 15:15:40.303871 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "7450bb0ce3ff" 14 | down_revision = None 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.create_table( 22 | "apps", 23 | sa.Column("id", sa.Integer(), nullable=False), 24 | sa.Column("name", sa.String(), nullable=True), 25 | sa.Column("prefix", sa.String(), nullable=True), 26 | sa.Column("url", sa.String(), nullable=True), 27 | sa.Column("icon", sa.String(), nullable=True), 28 | sa.Column("sidebar_icon", sa.String(), nullable=True), 29 | sa.Column("description", sa.String(), nullable=True), 30 | sa.Column("open_in", sa.String(), nullable=True), 31 | sa.Column("data_template", sa.String(), nullable=True), 32 | sa.Column("groups", sa.String(), nullable=True), 33 | sa.Column("tags", sa.String(), nullable=True), 34 | sa.PrimaryKeyConstraint("id"), 35 | ) 36 | op.create_table( 37 | "data_sources", 38 | sa.Column("id", sa.Integer(), nullable=False), 39 | sa.Column("name", sa.String(), nullable=True), 40 | sa.Column("platform", sa.String(), nullable=True), 41 | sa.PrimaryKeyConstraint("id"), 42 | ) 43 | op.create_table( 44 | "files", 45 | sa.Column("id", sa.Integer(), nullable=False), 46 | sa.Column("name", sa.String(), nullable=True), 47 | sa.Column("path", sa.String(), nullable=True), 48 | sa.Column("external_path", sa.String(), nullable=True), 49 | sa.Column("cache", sa.String(), nullable=True), 50 | sa.Column("folder", sa.String(), nullable=True), 51 | sa.PrimaryKeyConstraint("id"), 52 | ) 53 | op.create_table( 54 | "groups", 55 | sa.Column("id", sa.Integer(), nullable=False), 56 | sa.Column("name", sa.String(), nullable=True), 57 | sa.Column("roles", sa.String(), nullable=True), 58 | sa.PrimaryKeyConstraint("id"), 59 | ) 60 | op.create_table( 61 | "settings", 62 | sa.Column("id", sa.Integer(), nullable=False), 63 | sa.Column("theme", sa.String(), nullable=True), 64 | sa.Column("accent", sa.String(), nullable=True), 65 | sa.Column("background", sa.String(), nullable=True), 66 | sa.Column("roles", sa.String(), nullable=True), 67 | sa.Column("home_access_groups", sa.String(), nullable=True), 68 | sa.Column("settings_access_groups", sa.String(), nullable=True), 69 | sa.Column("home_view_mode", sa.String(), nullable=True), 70 | sa.Column("custom_app_title", sa.String(), nullable=True), 71 | sa.PrimaryKeyConstraint("id"), 72 | ) 73 | op.create_table( 74 | "tags", 75 | sa.Column("id", sa.Integer(), nullable=False), 76 | sa.Column("name", sa.String(), nullable=True), 77 | sa.PrimaryKeyConstraint("id"), 78 | ) 79 | op.create_table( 80 | "user", 81 | sa.Column("id", sa.Integer(), nullable=False), 82 | sa.Column("username", sa.String(length=120), nullable=False), 83 | sa.Column("password", sa.String(length=60), nullable=False), 84 | sa.Column("role", sa.String(), nullable=True), 85 | sa.PrimaryKeyConstraint("id"), 86 | sa.UniqueConstraint("username"), 87 | ) 88 | op.create_table( 89 | "data_sources_args", 90 | sa.Column("id", sa.Integer(), nullable=False), 91 | sa.Column("key", sa.String(), nullable=True), 92 | sa.Column("value", sa.String(), nullable=True), 93 | sa.Column("data_source_id", sa.Integer(), nullable=True), 94 | sa.ForeignKeyConstraint(["data_source_id"], ["data_sources.id"],), 95 | sa.PrimaryKeyConstraint("id"), 96 | ) 97 | op.create_table( 98 | "rel_app_data_source", 99 | sa.Column("data_source_id", sa.Integer(), nullable=True), 100 | sa.Column("app_id", sa.Integer(), nullable=True), 101 | sa.ForeignKeyConstraint(["app_id"], ["apps.id"],), 102 | sa.ForeignKeyConstraint(["data_source_id"], ["data_sources.id"],), 103 | ) 104 | # ### end Alembic commands ### 105 | 106 | 107 | def downgrade(): 108 | # ### commands auto generated by Alembic - please adjust! ### 109 | op.drop_table("rel_app_data_source") 110 | op.drop_table("data_sources_args") 111 | op.drop_table("user") 112 | op.drop_table("tags") 113 | op.drop_table("settings") 114 | op.drop_table("groups") 115 | op.drop_table("files") 116 | op.drop_table("data_sources") 117 | op.drop_table("apps") 118 | # ### end Alembic commands ### 119 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | # [ ] I'm submitting a template app 2 | - [ ] I have added a .ini file to DashMachine with the same name used for [App Name] in the .ini file (e.g. 'App Name.ini') 3 | - [ ] My .ini has the exact format as the others in the folder, only changing the name, icon location, and description. 4 | - [ ] I used the official description and name for the app, found on their repository/website. 5 | - [ ] I chose an icon (.png file) sized over 64px, with a transparent background, square aspect ratio, preferably the icon only version (not icon w/ text) of the app's icon. 6 | - [ ] I understand that the icon will be resized to 64px x 64px and have verified that it looks good at that size. 7 | - [ ] I have added the icon to static/images/apps with the correct name matching what's in the .ini. 8 | - [ ] I tested it to make sure it looks good in the interface 9 | 10 | # [ ] I'm submitting a platform 11 | - [ ] I looked at the other platform examples and followed a very similar methodology. 12 | - [ ] I added a docstring at the top of my platform file that is formatted exactly like rest.py 13 | - [ ] I added sample code to any template app that this platform works for, look at deluge.ini in /template_apps/ for an example 14 | - [ ] I have thoroughly tested this platform and it works 15 | - [ ] I am committed to updating this platform if something breaks it in the future -------------------------------------------------------------------------------- /readme_data_sources.md: -------------------------------------------------------------------------------- 1 | #### Data Source Platforms 2 | DashMachine includes several different 'platforms' for displaying data on your dash applications. Platforms are essentially plugins. All data source config entries require the `platform` variable, which tells DashMachine which platform file in the platform folder to load. **Note:** you are able to load your own platform files by placing them in the platform folder and referencing them in the config. However currently they will be deleted if you update the application, if you would like to make them permanent, submit a pull request for it to be added by default! 3 | 4 | > To add a data source to your app, add a data source config entry from one of the samples below 5 | **above** the application entry in config.ini, then add the following to your app config entry: 6 | `data_source = variable_name` -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | alembic 2 | aniso8601 3 | bcrypt 4 | certifi 5 | cffi 6 | chardet 7 | Click 8 | Flask 9 | Flask-Bcrypt 10 | Flask-Caching 11 | Flask-Login 12 | Flask-RESTful 13 | Flask-Script 14 | Flask-SQLAlchemy 15 | Flask-WTF 16 | gunicorn 17 | htmlmin 18 | idna 19 | itsdangerous 20 | Jinja2 21 | jsmin 22 | Mako 23 | MarkupSafe 24 | Pillow 25 | pycparser 26 | python-dateutil 27 | python-editor 28 | pytz 29 | requests 30 | six 31 | SQLAlchemy 32 | urllib3 33 | Werkzeug 34 | WTForms 35 | transmissionrpc 36 | markdown2 -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import pathlib 5 | 6 | 7 | root_folder = pathlib.Path().absolute() 8 | if not os.path.isdir(os.path.join(root_folder, "dashmachine", "user_data")): 9 | os.mkdir(os.path.join(root_folder, "dashmachine", "user_data")) 10 | db_file_path = os.path.join(root_folder, "dashmachine", "user_data", "site.db") 11 | try: 12 | os.remove(db_file_path) 13 | except FileNotFoundError: 14 | pass 15 | 16 | 17 | from dashmachine import app 18 | from dashmachine.main.utils import dashmachine_init 19 | 20 | dashmachine_init() 21 | 22 | if __name__ == "__main__": 23 | app.run(debug=True, use_reloader=True, host="0.0.0.0", threaded=True) 24 | -------------------------------------------------------------------------------- /screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/screenshot1.png -------------------------------------------------------------------------------- /screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/screenshot2.png -------------------------------------------------------------------------------- /screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/screenshot3.png -------------------------------------------------------------------------------- /screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmountjoy92/DashMachine/be50c2e763ae4e18f95855c18ba31c42199a4b3e/screenshot4.png -------------------------------------------------------------------------------- /template_apps/Airsonic.ini: -------------------------------------------------------------------------------- 1 | [Airsonic] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/airsonic.png 5 | sidebar_icon = static/images/apps/airsonic.png 6 | description = A Free and Open Source community driven media server 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Alertmanager.ini: -------------------------------------------------------------------------------- 1 | [Alertmanager] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/alertmanager.png 5 | sidebar_icon = static/images/apps/alertmanager.png 6 | description = The Alertmanager handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integrations such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Aria2.ini: -------------------------------------------------------------------------------- 1 | [Aria2] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/aria2.png 5 | sidebar_icon = static/images/apps/aria2.png 6 | description = aria2 is a lightweight multi-protocol & multi-source, cross platform download utility. It supports HTTP/HTTPS, FTP, SFTP, BitTorrent and Metalink. . 7 | open_in = this_tab 8 | 9 | -------------------------------------------------------------------------------- /template_apps/Bazarr.ini: -------------------------------------------------------------------------------- 1 | [Bazarr] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/bazarr.png 5 | sidebar_icon = static/images/apps/bazarr.png 6 | description = A companion application to Sonarr and Radarr 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Bitwarden.ini: -------------------------------------------------------------------------------- 1 | [Bitwarden] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/bitwarden.png 5 | sidebar_icon = static/images/apps/bitwarden.png 6 | description = A free and open-source password management service 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/BookStack.ini: -------------------------------------------------------------------------------- 1 | [BookStack] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/bookstack.png 5 | sidebar_icon = static/images/apps/bookstack.png 6 | description = BookStack is a simple, self-hosted, easy-to-use platform for organising and storing information 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Calibre-Web.ini: -------------------------------------------------------------------------------- 1 | [Calibre-Web] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/calibre-web.png 5 | sidebar_icon = static/images/apps/calibre-web.png 6 | description = Calibre-Web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. 7 | open_in = this_tab -------------------------------------------------------------------------------- /template_apps/CloudCMD.ini: -------------------------------------------------------------------------------- 1 | [CloudCMD] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/cloudcmd.png 5 | sidebar_icon = static/images/apps/cloudcmd.png 6 | description = Cloud Commander a file manager for the web with console and editor. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Cockpit.ini: -------------------------------------------------------------------------------- 1 | [Cockpit] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/cockpit-project.png 5 | sidebar_icon = static/images/apps/cockpit-project.png 6 | description = An interactive server admin interface 7 | open_in = iframe -------------------------------------------------------------------------------- /template_apps/Deluge.ini: -------------------------------------------------------------------------------- 1 | # ----------------- 2 | # App config entry 3 | # ----------------- 4 | [Deluge] 5 | prefix = https:// 6 | url = your-website.com 7 | icon = static/images/apps/deluge.png 8 | sidebar_icon = static/images/apps/deluge.png 9 | description = Deluge is a lightweight, Free Software, cross-platform BitTorrent client 10 | open_in = this_tab 11 | 12 | # ----------------------------------------------------------- 13 | # Data Source (place this above the app entry in config.ini) 14 | # ----------------------------------------------------------- 15 | [variable_name] 16 | platform = deluge 17 | resource = https://deluge.example.com:8112/json 18 | value_template = ↓{{download_rate|filesizeformat}}/s ↑{{upload_rate|filesizeformat}}/s 19 | password = MySecretPassword 20 | 21 | # then add to the app config entry: 22 | data_sources = variable name -------------------------------------------------------------------------------- /template_apps/Docker.ini: -------------------------------------------------------------------------------- 1 | [Docker] 2 | prefix = http:// 3 | url = your-website.com 4 | icon = static/images/apps/docker.png 5 | sidebar_icon = static/images/apps/docker.png 6 | description = Empowering App Development for Developers 7 | open_in = this_tab -------------------------------------------------------------------------------- /template_apps/DokuWiki.ini: -------------------------------------------------------------------------------- 1 | [DokuWiki] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/dokuwiki.png 5 | sidebar_icon = static/images/apps/dokuwiki.png 6 | description = DokuWiki is a simple to use and highly versatile Open Source wiki software that does not require a database. 7 | open_in = this_tab -------------------------------------------------------------------------------- /template_apps/Duplicati.ini: -------------------------------------------------------------------------------- 1 | [Duplicati] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/duplicati.png 5 | sidebar_icon = static/images/apps/duplicati.png 6 | description = Free backup software to store encrypted backups online 7 | open_in = this_tab -------------------------------------------------------------------------------- /template_apps/Emby.ini: -------------------------------------------------------------------------------- 1 | [Emby] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/emby.png 5 | sidebar_icon = static/images/apps/emby.png 6 | description = Emby Server is a personal media server with apps on just about every device 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/FileRun.ini: -------------------------------------------------------------------------------- 1 | [FileRun] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/filerun.png 5 | sidebar_icon = static/images/apps/filerun.png 6 | description = Access your files anywhere through self-hosted secure cloud storage, file backup and sharing for your photos, videos, files and more. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Firefly-III.ini: -------------------------------------------------------------------------------- 1 | [Firefly-III] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/firefly_iii.png 5 | sidebar_icon = static/images/apps/firefly_iii.png 6 | description = Firefly III is a self-hosted financial manager. It can help you keep track of expenses, income, budgets and everything in between. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Gitea.ini: -------------------------------------------------------------------------------- 1 | [Gitea] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/gitea.png 5 | sidebar_icon = static/images/apps/gitea.png 6 | description = A painless self-hosted Git service 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Glances.ini: -------------------------------------------------------------------------------- 1 | [Glances] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/glances.png 5 | sidebar_icon = static/images/apps/glances.png 6 | description = A cross-platform system monitoring tool 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Gogs.ini: -------------------------------------------------------------------------------- 1 | [Gogs] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/gogs.png 5 | sidebar_icon = static/images/apps/gogs.png 6 | description = Gogs is a painless self-hosted Git service. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Gotify.ini: -------------------------------------------------------------------------------- 1 | [Gotify] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/gotify.png 5 | sidebar_icon = static/images/apps/gotify.png 6 | description = A simple server for sending and receiving messages 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Grafana.ini: -------------------------------------------------------------------------------- 1 | [Grafana] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/grafana.png 5 | sidebar_icon = static/images/apps/grafana.png 6 | description = The open observability platform 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Guacamole.ini: -------------------------------------------------------------------------------- 1 | [Guacamole] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/guacamole.png 5 | sidebar_icon = static/images/apps/guacamole.png 6 | description = Apache Guacamole is a clientless HTML5 web application that can be used to access your remote servers and desktops via a web browser. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/HP iLO.ini: -------------------------------------------------------------------------------- 1 | [HP iLO] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/hp-ilo.png 5 | sidebar_icon = static/images/apps/hp-ilo.png 6 | description = Integrated Lights-Out, or iLO, is a proprietary embedded server management technology provides out-of-band management facilities. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/HandBrake.ini: -------------------------------------------------------------------------------- 1 | [HandBrake] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/handbrake.png 5 | sidebar_icon = static/images/icons/handbrake.png 6 | description = HandBrake is an open-source, GPL-licensed, multiplatform, multithreaded video transcoder 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Healthchecks.ini: -------------------------------------------------------------------------------- 1 | [Healthchecks] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/healthchecks.png 5 | sidebar_icon = static/images/apps/healthchecks.png 6 | description = Healthchecks is a watchdog for your cron jobs. It's a web server that listens for pings from your cron jobs, plus a web interface. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Home Assistant.ini: -------------------------------------------------------------------------------- 1 | [Home Assistant] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/home-assistant.png 5 | sidebar_icon = static/images/apps/home-assistant.png 6 | description = Open source home automation that puts local control and privacy first 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Homebridge.ini: -------------------------------------------------------------------------------- 1 | [Homebridge] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/homebridge.png 5 | sidebar_icon = static/images/apps/homebridge.png 6 | description = Homebridge is a lightweight NodeJS server you can run on your home network that emulates the iOS HomeKit API. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/JDownloader.ini: -------------------------------------------------------------------------------- 1 | [JDownloader] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/jdownloader.png 5 | sidebar_icon = static/images/apps/jdownloader.png 6 | description = A download manager written in Java 7 | open_in = iframe -------------------------------------------------------------------------------- /template_apps/Jackett.ini: -------------------------------------------------------------------------------- 1 | [Jackett] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/jackett.png 5 | sidebar_icon = static/images/apps/jackett.png 6 | description = API Support for your favorite torrent trackers 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Jeedom.ini: -------------------------------------------------------------------------------- 1 | [Jeedom] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/jeedom.png 5 | sidebar_icon = static/images/apps/jeedom.png 6 | description = Jeedom is a Home Automation System that lets you monitor and configure various devices like: Lights, Switches, various sensors/meters like Temperature... 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Jellyfin.ini: -------------------------------------------------------------------------------- 1 | [Jellyfin] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/jellyfin.png 5 | sidebar_icon = static/images/apps/jellyfin.png 6 | description = The Free Software Media System 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Jenkins.ini: -------------------------------------------------------------------------------- 1 | [Jenkins] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/jenkins.png 5 | sidebar_icon = static/images/apps/jenkins.png 6 | description = Jenkins is an open source automation server which enables developers around the world to reliably build, test, and deploy their software. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Karma.ini: -------------------------------------------------------------------------------- 1 | [Karma] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/karma.png 5 | sidebar_icon = static/images/apps/karma.png 6 | description = Alert dashboard for Prometheus Alertmanager. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Kodi.ini: -------------------------------------------------------------------------------- 1 | [Kodi] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/kodi.png 5 | sidebar_icon = static/images/apps/kodi.png 6 | description = Kodi is a free open source home theater software designed to look great on your big screen TV but is just as home on a small screen. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Krusader.ini: -------------------------------------------------------------------------------- 1 | [Krusader] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/krusader.png 5 | sidebar_icon = static/images/apps/krusader.png 6 | description = Twin panel file management for your desktop 7 | open_in = iframe -------------------------------------------------------------------------------- /template_apps/Lidarr.ini: -------------------------------------------------------------------------------- 1 | [Lidarr] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/lidarr.png 5 | sidebar_icon = static/images/apps/lidarr.png 6 | description = Looks and smells like Sonarr but made for music 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Mailcow.ini: -------------------------------------------------------------------------------- 1 | [Mailcow] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/mailcow.png 5 | sidebar_icon = static/images/apps/mailcow.png 6 | description = mailcow is a mail server suite based on Dovecot, Postfix and other open source software, that provides a modern web UI for user/server administration. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Netdata.ini: -------------------------------------------------------------------------------- 1 | [Netdata] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/netdata.png 5 | sidebar_icon = static/images/apps/netdata.png 6 | description = Instantly diagnose slowdowns and anomalies in your infrastructure with thousands of metrics, interactive visualizations, and insightful health alarms 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Nextcloud.ini: -------------------------------------------------------------------------------- 1 | [Nextcloud] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/nextcloud.png 5 | sidebar_icon = static/images/apps/nextcloud.png 6 | description = A safe home for all your data – community-driven, free & open source 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Nginx Proxy Manager.ini: -------------------------------------------------------------------------------- 1 | [Nginx Proxy Manager] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/nginxproxymanager.png 5 | sidebar_icon = static/images/apps/nginxproxymanager.png 6 | description = Docker container for managing Nginx proxy hosts with a simple, powerful interface 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Node-RED.ini: -------------------------------------------------------------------------------- 1 | [Node-RED] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/nodered.png 5 | sidebar_icon = static/images/apps/nodered.png 6 | description = Node-RED is a programming tool for wiring together hardware devices, APIs and online services in new and interesting ways. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/OPNsense.ini: -------------------------------------------------------------------------------- 1 | [OPNsense] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/opnsense.png 5 | sidebar_icon = static/images/apps/opnsense.png 6 | description = Secure Your Network with ease. From Virtual Private Networking to Intrusion Detection, Best in class, FREE Open Source Project 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Ombi.ini: -------------------------------------------------------------------------------- 1 | [Ombi] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/ombi.png 5 | sidebar_icon = static/images/apps/ombi.png 6 | description = Want a Movie or TV Show on Plex or Emby? Use Ombi! 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/OpenEats.ini: -------------------------------------------------------------------------------- 1 | [OpenEats] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/openeats.png 5 | sidebar_icon = static/images/apps/openeats.png 6 | description = OpenEats is a recipe management site that allows users to create, share, and store their personal collection of recipes. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/OpenMediaVault.ini: -------------------------------------------------------------------------------- 1 | [OpenMediaVault] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/openmediavault.png 5 | sidebar_icon = static/images/apps/openmediavault.png 6 | description = OpenMediaVault is the next generation network attached storage (NAS) solution based on Debian Linux. 7 | open_in = this_tab -------------------------------------------------------------------------------- /template_apps/OpenWRT.ini: -------------------------------------------------------------------------------- 1 | [OpenWRT] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/openwrt.png 5 | sidebar_icon = static/images/apps/openwrt.png 6 | description = OpenWrt is an open source project for embedded operating systems based on Linux, primarily used on embedded devices to route network traffic. 7 | open_in = this_tab 8 | 9 | -------------------------------------------------------------------------------- /template_apps/Phoscon.ini: -------------------------------------------------------------------------------- 1 | [Phoscon] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/phoscon.png 5 | sidebar_icon = static/images/apps/phoscon.png 6 | description = The Phoscon App is a browser based web application and supports lights, sensors and switches. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Pi-hole.ini: -------------------------------------------------------------------------------- 1 | [Pi-hole] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/pihole.png 5 | sidebar_icon = static/images/apps/pihole.png 6 | description = A black hole for Internet advertisements 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Piwigo.ini: -------------------------------------------------------------------------------- 1 | [Piwigo] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/piwigo.png 5 | description = Manage your photos with Piwigo, a full featured open source photo gallery application for the web. 6 | open_in = this_tab 7 | -------------------------------------------------------------------------------- /template_apps/Plex.ini: -------------------------------------------------------------------------------- 1 | [Plex] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/plex.png 5 | sidebar_icon = static/images/apps/plex.png 6 | description = Plex is a client–server media player system and software suite. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Portainer.ini: -------------------------------------------------------------------------------- 1 | [Portainer] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/portainer.png 5 | sidebar_icon = static/images/apps/portainer.png 6 | description = Making Docker management easy 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/PrivateBin.ini: -------------------------------------------------------------------------------- 1 | [PrivateBin] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/privatebin.png 5 | sidebar_icon = static/images/apps/privatebin.png 6 | description = PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/ProjectSend.ini: -------------------------------------------------------------------------------- 1 | [ProjectSend] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/projectsend.png 5 | sidebar_icon = static/images/apps/projectsend.png 6 | description = ProjectSend is a self-hosted application (you can install it easily on your own VPS or shared web hosting account) that lets you upload files and assign them to specific clients that you create yourself. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Prometheus.ini: -------------------------------------------------------------------------------- 1 | [Prometheus] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/prometheus.png 5 | sidebar_icon = static/images/apps/prometheus.png 6 | description = Prometheus, a Cloud Native Computing Foundation project, is a systems and service monitoring system. It collects metrics from configured targets at given intervals, evaluates rule expressions, displays the results, and can trigger alerts if some condition is observed to be true. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Proxmox.ini: -------------------------------------------------------------------------------- 1 | [Proxmox] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/proxmox.png 5 | sidebar_icon = static/images/apps/proxmox.png 6 | description = Proxmox VE is a complete open-source platform for enterprise virtualization. 7 | open_in = this_tab -------------------------------------------------------------------------------- /template_apps/Radarr.ini: -------------------------------------------------------------------------------- 1 | [Radarr] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/radarr.png 5 | sidebar_icon = static/images/apps/radarr.png 6 | description = A fork of Sonarr to work with movies à la Couchpotato 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Riot.ini: -------------------------------------------------------------------------------- 1 | [Riot] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/riot.png 5 | sidebar_icon = static/images/apps/riot.png 6 | description = A glossy Matrix collaboration client for the web 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/SSH.ini: -------------------------------------------------------------------------------- 1 | [SSH] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/terminal.png 5 | sidebar_icon = static/images/apps/terminal.png 6 | description = SSH, terminal, shell, shellinabox 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Sonarr.ini: -------------------------------------------------------------------------------- 1 | [Sonarr] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/sonarr.png 5 | sidebar_icon = static/images/apps/sonarr.png 6 | description = Smart PVR for newsgroup and bittorrent users 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Statping.ini: -------------------------------------------------------------------------------- 1 | [Statping] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/statping.png 5 | sidebar_icon = static/images/apps/statping.png 6 | description = Status Page for monitoring your websites and applications with beautiful graphs, analytics, and plugins. Run on any type of environment. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Syncthing.ini: -------------------------------------------------------------------------------- 1 | [Syncthing] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/Syncthing.png 5 | sidebar_icon = static/images/apps/Syncthing.png 6 | description = Open Source Continuous File Synchronization 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology ActiveBackup.ini: -------------------------------------------------------------------------------- 1 | [Synology ActiveBackup] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-active-backup.png 5 | sidebar_icon = static/images/apps/synology-active-backup.png 6 | description = Active Backup for Business is designed to provide a comprehensive while centralized data protection solution, helping you back up business PCs, VMs, physical servers, and file servers all alike. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology Audio Station.ini: -------------------------------------------------------------------------------- 1 | [Synology Audio Station] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-audio-station.png 5 | sidebar_icon = static/images/apps/synology-audio-station.png 6 | description = With Audio Station and its mobile app DS audio, you can enjoy high-quality playback, listen to radios, manage your music collection, create personal playlist, and share with friends anywhere. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology Calendar.ini: -------------------------------------------------------------------------------- 1 | [Synology Calendar] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-calendar.png 5 | sidebar_icon = static/images/apps/synology-calendar.png 6 | description = Calendar is a web-based application for organizing and planning out daily events. You can create events in your own personal calendar or share a calendar within a group of people. CalDAV is also supported and is compatible with mobile devices. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology Download Station.ini: -------------------------------------------------------------------------------- 1 | [Synology Download Station] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-download-station.png 5 | sidebar_icon = static/images/apps/synology-download-station.png 6 | description = Download Station is a web-based download application which allows you to download files from the Internet through BT, FTP, HTTP, NZB, FlashGet, QQDL, and eMule. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology Drive.ini: -------------------------------------------------------------------------------- 1 | [Synology Drive] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-drive.png 5 | sidebar_icon = static/images/apps/synology-drive.png 6 | description = Synology Drive Server is an all-round solution for file management, sharing and synchronization. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology MailPlus.ini: -------------------------------------------------------------------------------- 1 | [Synology MailPlus] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-mailplus.png 5 | sidebar_icon = static/images/apps/synology-mailplus.png 6 | description = Synology MailPlus is a powerful webmail service that provides rich features. You can send and receive mail to and from any other mail servers to keep all your emails in one convenient and secure place. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology Moments.ini: -------------------------------------------------------------------------------- 1 | [Synology Moments] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-moments.png 5 | sidebar_icon = static/images/apps/synology-moments.png 6 | description = Moments organizes your personal photos intelligently, allowing you to keep track of all your special moments in great detail. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology Note Station.ini: -------------------------------------------------------------------------------- 1 | [Synology Note Station] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-note-station.png 5 | sidebar_icon = static/images/apps/synology-note-station.png 6 | description = Note Station helps you fully enjoy writing, viewing, managing, and sharing content-rich notes. It is very easy to create content with rich text editing, media embedding, attachments, and much more. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology Office.ini: -------------------------------------------------------------------------------- 1 | [Synology Office] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-office.png 5 | sidebar_icon = static/images/apps/synology-office.png 6 | description = Synology Office is a collaboration-oriented application for creating documents, spreadsheets and slides in Synology Drive. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Synology Video Station.ini: -------------------------------------------------------------------------------- 1 | [Synology Video Station] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/synology-video-station.png 5 | sidebar_icon = static/images/apps/synology-video-station.png 6 | description = Video Station lets you manage and watch your video collection—movies, TV shows, home videos, and TV recordings—with intuitive convenience. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/TasmoAdmin.ini: -------------------------------------------------------------------------------- 1 | [TasmoAdmin] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/tasmoadmin.png 5 | sidebar_icon = static/images/apps/tasmoadmin.png 6 | description = Website to manage Sonoff Devices flashed with Tasmota 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Tautulli.ini: -------------------------------------------------------------------------------- 1 | [Tautulli] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/tautulli.png 5 | sidebar_icon = static/images/apps/tautulli.png 6 | description = A Python based monitoring and tracking tool for Plex Media Server 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/The Lounge.ini: -------------------------------------------------------------------------------- 1 | [The Lounge] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/thelounge.png 5 | sidebar_icon = static/images/apps/thelounge.png 6 | description = Modern, responsive, cross-platform, self-hosted web IRC client 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Traefik.ini: -------------------------------------------------------------------------------- 1 | [Traefik] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/traefik.png 5 | sidebar_icon = static/images/apps/traefik.png 6 | description = The Cloud Native Edge Router 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Transmission.ini: -------------------------------------------------------------------------------- 1 | [Transmission] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/transmission.png 5 | sidebar_icon = static/images/apps/transmission.png 6 | description = A Fast, Easy, and Free BitTorrent Client 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Unifi.ini: -------------------------------------------------------------------------------- 1 | [Unifi] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/unifi.png 5 | sidebar_icon = static/images/apps/unifi.png 6 | description = The UniFi® Controller is a wireless network management software solution from Ubiquiti Networks™. It allows you to manage multiple wireless networks using a web browser. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/VMware ESXi.ini: -------------------------------------------------------------------------------- 1 | [VMware ESXi] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/vmware-esxi.png 5 | sidebar_icon = static/images/apps/vmware-esxi.png 6 | description = An hypervisor developed by VMware for deploying and serving virtual computers. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/VS Code.ini: -------------------------------------------------------------------------------- 1 | [VS Code] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/vscode.png 5 | sidebar_icon = static/images/apps/vscode.png 6 | description = A code editor redefined and optimized for building and debugging modern web and cloud applications 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Wallabag.ini: -------------------------------------------------------------------------------- 1 | [Wallabag] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/wallabag.png 5 | sidebar_icon = static/images/apps/wallabag.png 6 | description = Wallabag is a self hostable application for saving web pages: Save and classify articles. Read them later. Freely. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/WeTTy.ini: -------------------------------------------------------------------------------- 1 | [WeTTy] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/wetty.png 5 | sidebar_icon = static/images/apps/wetty.png 6 | description = WeTTy (Web + TTy) is a terminal over HTTP and https which uses websockets rather then Ajax and hence better response time. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/Wikijs.ini: -------------------------------------------------------------------------------- 1 | [Wikijs] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/wikijs.png 5 | sidebar_icon = static/images/apps/wikijs.png 6 | description = A modern, lightweight and powerful wiki app built on Node.js 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/pfSense.ini: -------------------------------------------------------------------------------- 1 | [pfSense] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/pfsense.png 5 | sidebar_icon = static/images/apps/pfsense.png 6 | description = pfSense is a free and open source firewall and router that also features unified threat management, load balancing, multi WAN, and more. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/phpMyAdmin.ini: -------------------------------------------------------------------------------- 1 | [phpMyAdmin] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/phpmyadmin.png 5 | sidebar_icon = static/images/apps/phpmyadmin.png 6 | description = phpMyAdmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. 7 | open_in = this_tab 8 | -------------------------------------------------------------------------------- /template_apps/qBittorrent.ini: -------------------------------------------------------------------------------- 1 | [qBittorrent] 2 | prefix = https:// 3 | url = your-website.com 4 | icon = static/images/apps/qbittorrent.png 5 | sidebar_icon = static/images/apps/qbittorrent.png 6 | description = A BitTorrent client in Qt 7 | open_in = iframe 8 | -------------------------------------------------------------------------------- /update_message.md: -------------------------------------------------------------------------------- 1 | ##### Updated to version 0.5! 2 | > In this version, I removed the need for database migrations by making the database 100% dynamic. Meaning when you run dashmachine your old site.db file will be deleted and a new one will be created using the data from config.ini. This will make adding features in the future easier and updates will break things less. To accomplish this you will notice that user management has been moved to config.ini. If you are upgrading from a previous version, your user table was deleted. Please login with default user/pass (which is now 'admin' and 'admin') and take a look at the users section in the readme to add users. 3 | 4 | **Changelog** 5 | - ui fixes 6 | - users are now managed through config.ini 7 | - no more alembic, completely dynamic database, created on startup 8 | - users can now override global settings 9 | - added update message 10 | - performance fixes 11 | - added setting for hiding sidebar by default 12 | - broke up config readme into 3 tabs, and 3 .md files 13 | - changed 'app templates' to 'card templates' 14 | - added 'collection' cards 15 | - added 'custom' cards 16 | - added options for setting tag icons and sort position 17 | - removed list view to focus on different card types on /home 18 | - added ability to collapse/expand tags on /home 19 | - added setting for having tags default to collapsed state 20 | - created a public user view with no sidebar 21 | - added sidebar default overrides for users 22 | - fixes #57, #55, #45, #41, #40 -------------------------------------------------------------------------------- /wsgi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | 5 | root_folder = os.path.dirname(__file__) 6 | db_file_path = os.path.join(root_folder, "dashmachine", "user_data", "site.db") 7 | try: 8 | os.remove(db_file_path) 9 | except FileNotFoundError: 10 | pass 11 | 12 | from dashmachine import app 13 | from dashmachine.main.utils import dashmachine_init 14 | 15 | dashmachine_init() 16 | 17 | if __name__ == "__main__": 18 | app.run() 19 | --------------------------------------------------------------------------------