1
0
mirror of https://github.com/gilbN/theme.park.git synced 2024-07-07 04:19:17 +02:00

Merge branch 'develop' into testing

This commit is contained in:
GilbN 2023-05-29 14:03:28 +02:00
commit 5b242cc7c3
24 changed files with 277 additions and 137 deletions

View File

@ -12,7 +12,7 @@ A clear and concise description of what the bug is.
**Theme Install Method** (Don't skip)
- Docker run / Compose Config
- If using docker mods share your container logs..
- If using docker mods share your COMPLETE container logs..
- Webserver subfilter Config
- Stylus Config

View File

@ -136,7 +136,7 @@ jobs:
docker manifest create ${IMAGE}:${TAG} ${AMD64} ${ARM64} ${ARMV7}
docker manifest push ${IMAGE}:${TAG}
- name: Latest manifest
if: ${{ github.event.release.target_commitish == 'master' }}
if: ${{ github.event.release.target_commitish == 'master' }} || ${{ github.event.inputs.branch == 'master' }}
env:
DOCKER_CLI_EXPERIMENTAL: enabled
run: |

View File

@ -58,7 +58,8 @@ p {
}
.page-header-canvas,
.page-toolbar[class*="css-"] {
.page-toolbar[class*="css-"],
.css-278jzv {
background: var(--transparency-dark-25) !important;
box-shadow: none !important;
border-bottom: none !important;
@ -534,7 +535,8 @@ p {
}
/* PANEL BACKGROUND COLOR*/
.panel-container {
.panel-container,
.css-85us1i-panel-container {
background: var(--transparency-dark-25);
border: transparent;
box-shadow: none !important;
@ -1308,4 +1310,4 @@ input:checked+.gf-form-switch__slider {
::-webkit-input-placeholder {
color: var(--text-hover) !important;
}
}

View File

@ -1451,6 +1451,10 @@ a:hover,
background: var(--transparency-light-20);
}
#homepageOrderUptimeKuma .card-body {
background: var(--transparency-light-20);
}
.card,
.card-body {
border-radius: 3px;

View File

@ -411,11 +411,11 @@
}
/* Network */
.table-striped>tbody>tr:nth-of-type(odd) {
.table-striped>tbody>tr:nth-of-type(odd):not(#network-details .table-striped>tbody>tr:nth-of-type(odd)) {
background: var(--transparency-dark-25) !important;
}
.table-striped>tbody>tr:nth-of-type(even) {
.table-striped>tbody>tr:nth-of-type(even):not(#network-details .table-striped>tbody>tr:nth-of-type(even)) {
background: var(--transparency-dark-50) !important;
}

View File

@ -1,4 +1,4 @@
#!/usr/bin/with-contenv bash
#!/command/with-contenv bash
echo '--------------------------------------'
echo '| Nginx Proxy Manager theme.park Mod |'

View File

@ -1,4 +1,4 @@
#!/usr/bin/with-contenv bash
#!/command/with-contenv bash
echo '--------------------------------------'
echo '| Nginx Proxy Manager theme.park Mod |'

View File

@ -0,0 +1,81 @@
## Version 2022/08/16 - Changelog: https://github.com/linuxserver/docker-baseimage-alpine-nginx/commits/master/root/defaults/nginx/nginx.conf.sample
### Based on alpine defaults
# https://git.alpinelinux.org/aports/tree/main/nginx/nginx.conf?h=3.15-stable
user abc;
# Set number of worker processes automatically based on number of CPU cores.
include /config/nginx/worker_processes.conf;
# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;
# Configures default error logger.
error_log /config/log/nginx/error.log;
# Includes files with directives to load dynamic modules.
include /etc/nginx/modules/*.conf;
# Include files with config snippets into the root context.
include /etc/nginx/conf.d/*.conf;
events {
# The maximum number of simultaneous connections that can be opened by
# a worker process.
worker_connections 1024;
}
http {
# Includes mapping of file name extensions to MIME types of responses
# and defines the default type.
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Name servers used to resolve names of upstream servers into addresses.
# It's also needed when using tcpsocket and udpsocket in Lua modules.
#resolver 1.1.1.1 1.0.0.1 2606:4700:4700::1111 2606:4700:4700::1001;
include /config/nginx/resolver.conf;
# Don't tell nginx version to the clients. Default is 'on'.
server_tokens off;
# Specifies the maximum accepted body size of a client request, as
# indicated by the request header Content-Length. If the stated content
# length is greater than this size, then the client receives the HTTP
# error code 413. Set to 0 to disable. Default is '1m'.
client_max_body_size 0;
# Sendfile copies data between one FD and other from within the kernel,
# which is more efficient than read() + write(). Default is off.
sendfile on;
# Causes nginx to attempt to send its HTTP response head in one packet,
# instead of using partial frames. Default is 'off'.
tcp_nopush on;
# all ssl related config moved to ssl.conf
include /config/nginx/ssl.conf;
# Enable gzipping of responses.
#gzip on;
# Set the Vary HTTP header as defined in the RFC 2616. Default is 'off'.
gzip_vary on;
# Helper variable for proxying websockets.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Sets the path, format, and configuration for a buffered log write.
access_log /config/log/nginx/access.log;
# Includes virtual hosts configs.
include /etc/nginx/http.d/*.conf;
include /config/nginx/site-confs/*.conf;
}
daemon off;
pid /run/nginx.pid;

View File

@ -1,34 +1,32 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name _;
ssl_certificate /config/keys/cert.crt;
ssl_certificate_key /config/keys/cert.key;
index index.html index.htm index.php;
location / {
alias /config/www/;
try_files $uri $uri/ =404;
}
location /themepark {return 302 $scheme://$http_host/themepark/;}
location /themepark/ {
alias /config/www/;
sub_filter_types *;
sub_filter 'url("/css/' 'url("/themepark/css/';
sub_filter 'url(/resources/' 'url(/themepark/resources/';
sub_filter_once off;
try_files $uri $uri/ =404;
}
# Don't cache
add_header Last-Modified $date_gmt;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires -1;
etag off;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name _;
index index.html index.htm index.php;
location / {
alias /config/www/;
try_files $uri $uri/ =404;
}
location /themepark {return 302 $scheme://$http_host/themepark/;}
location /themepark/ {
alias /config/www/;
sub_filter_types *;
sub_filter 'url("/css/' 'url("/themepark/css/';
sub_filter 'url(/resources/' 'url(/themepark/resources/';
sub_filter_once off;
try_files $uri $uri/ =404;
}
# Don't cache
add_header Last-Modified $date_gmt;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires -1;
etag off;
}

View File

@ -0,0 +1,39 @@
## Version 2022/08/20 - Changelog: https://github.com/linuxserver/docker-baseimage-alpine-nginx/commits/master/root/defaults/nginx/ssl.conf.sample
### Mozilla Recommendations
# generated 2022-08-05, Mozilla Guideline v5.6, nginx 1.17.7, OpenSSL 1.1.1k, intermediate configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.17.7&config=intermediate&openssl=1.1.1k&guideline=5.6
ssl_certificate /config/keys/cert.crt;
ssl_certificate_key /config/keys/cert.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
ssl_dhparam /config/nginx/dhparams.pem;
# intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
#add_header Strict-Transport-Security "max-age=63072000" always;
# OCSP stapling
#ssl_stapling on;
#ssl_stapling_verify on;
# verify chain of trust of OCSP response using Root CA and Intermediate certs
#ssl_trusted_certificate /config/keys/cert.crt;
# Optional additional headers
#add_header Cache-Control "no-transform" always;
#add_header Content-Security-Policy "upgrade-insecure-requests; frame-ancestors 'self'";
#add_header Permissions-Policy "interest-cohort=()";
#add_header Referrer-Policy "same-origin" always;
#add_header X-Content-Type-Options "nosniff" always;
#add_header X-Frame-Options "SAMEORIGIN" always;
#add_header X-UA-Compatible "IE=Edge" always;
#add_header X-XSS-Protection "1; mode=block" always;

View File

@ -1,66 +0,0 @@
#!/usr/bin/with-contenv bash
echo '
----------------------------------------------------------------------------------------
dP dP dP
88 88 88
d8888P 88d888b. .d8888b. 88d8b.d8b. .d8888b. 88d888b. .d8888b. 88d888b. 88 .dP
88 88 `88 88ooood8 88 88 88 88ooood8 88. `88 88 `88 88 `88 88888.
88 88 88 88. ... 88 88 88 88. ... 88. .88 88 88 88 88 `8b.
dP dP dP `88888P dP dP dP `88888P 88 88Y888P `88888P8 dP dP `YP
88
dP
Made by @gilbN
https://theme-park.dev
----------------------------------------------------------------------------------------'
# Display variables for troubleshooting
echo -e "[theme.park-init] Variables set:\\n\
PUID=${PUID}\\n\
PGID=${PGID}\\n\
TZ=${TZ}\\n\
TP_DOMAIN=${TP_DOMAIN}\\n\
TP_URLBASE=${TP_URLBASE}\\n"
# Remove forward slash
case ${TP_URLBASE} in
*"/"*)
TP_URLBASE=$(echo "${TP_URLBASE}" | sed 's/\///g')
;;
esac
DEFAULT='/defaults/default'
if [[ ${TP_URLBASE} ]]; then
if ! grep -q "${TP_URLBASE}" "${DEFAULT}"; then
sed -i "s/themepark/${TP_URLBASE}/g" ${DEFAULT}
fi
fi
cp /defaults/default /config/nginx/site-confs
# make our folders and links
mkdir -p \
/config/www/{css,resources} \
/config/docker-mods
echo '[theme.park-init] Copying theme files'
# copy theme files
cp -R /app/themepark/css /config/www
cp -R /app/themepark/resources /config/www
cp /app/themepark/index.html /config/www
cp /app/themepark/themes.py /config/www
cp /app/themepark/CNAME /config/www
echo '[theme.park-init] Copying mods into /config/docker-mods'
# copy mods
for folder in /app/themepark/docker-mods/*; do \
cp /app/themepark/docker-mods/"${folder##*/}"/root/etc/cont-init.d/98-themepark /config/docker-mods/98-themepark-"${folder##*/}"; \
done
echo '[theme.park-init] Running themes.py and creating CSS files'
python3 /config/www/themes.py
# permissions
chown -R abc:abc \
/config

View File

@ -0,0 +1,10 @@
████████╗██╗ ██╗███████╗███╗ ███╗███████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗
╚══██╔══╝██║ ██║██╔════╝████╗ ████║██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██║ ██╔╝
██║ ███████║█████╗ ██╔████╔██║█████╗ ██████╔╝███████║██████╔╝█████╔╝
██║ ██╔══██║██╔══╝ ██║╚██╔╝██║██╔══╝ ██╔═══╝ ██╔══██║██╔══██╗██╔═██╗
██║ ██║ ██║███████╗██║ ╚═╝ ██║███████╗██╗██║ ██║ ██║██║ ██║██║ ██╗
╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
Made by @gilbN
https://theme-park.dev

View File

@ -0,0 +1 @@
oneshot

View File

@ -0,0 +1 @@
# This file doesn't do anything, it's just the end of the themepark init process

View File

@ -0,0 +1,58 @@
#!/usr/bin/with-contenv bash
# Display variables for troubleshooting
echo -e "[theme.park-init] Variables set:\\n\
PUID=${PUID}\\n\
PGID=${PGID}\\n\
TZ=${TZ}\\n\
TP_DOMAIN=${TP_DOMAIN}\\n\
TP_URLBASE=${TP_URLBASE}\\n"
# Remove forward slash
case ${TP_URLBASE} in
*"/"*)
TP_URLBASE=$(echo "${TP_URLBASE}" | sed 's/\///g')
;;
esac
DEFAULT='/defaults/nginx/site-confs/default.conf'
if [[ ${TP_URLBASE} ]]; then
if ! grep -q "${TP_URLBASE}" "${DEFAULT}"; then
sed -i "s/themepark/${TP_URLBASE}/g" ${DEFAULT}
fi
fi
echo '[theme.park-init] Copying nginx files'
cp -TR /defaults /config
# Remove old config
if [[ -f /config/nginx/site-confs/default ]]; then
echo '[theme.park-init] Removing old default file'
rm /config/nginx/site-confs/default
fi
# make our folders and links
mkdir -p \
/config/www/{css,resources} \
/config/docker-mods
echo '[theme.park-init] Copying theme files'
# copy theme files
cp -R /app/themepark/css /config/www
cp -R /app/themepark/resources /config/www
cp /app/themepark/index.html /config/www
cp /app/themepark/themes.py /config/www
cp /app/themepark/CNAME /config/www
echo '[theme.park-init] Copying mods'
# copy mods
for folder in /app/themepark/docker-mods/*; do \
cp /app/themepark/docker-mods/"${folder##*/}"/root/etc/cont-init.d/98-themepark /config/docker-mods/98-themepark-"${folder##*/}"; \
done
echo '[theme.park-init] Running themes.py and creating CSS files'
python3 /config/www/themes.py
echo '[theme.park-init] done.'
# permissions
chown -R abc:abc \
/config

View File

@ -0,0 +1 @@
oneshot

View File

@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/init-themepark/run

View File

@ -0,0 +1 @@
# No version checks for you!

View File

@ -1,4 +1,4 @@
FROM ghcr.io/linuxserver/baseimage-alpine-nginx:3.14
FROM ghcr.io/linuxserver/baseimage-alpine-nginx:3.16
# set version label
ARG BUILD_DATE

View File

@ -1,4 +1,4 @@
FROM ghcr.io/linuxserver/baseimage-alpine-nginx:arm32v7-3.14
FROM ghcr.io/linuxserver/baseimage-alpine-nginx:arm32v7-3.16
# set version label
ARG BUILD_DATE

View File

@ -1,4 +1,4 @@
FROM ghcr.io/linuxserver/baseimage-alpine-nginx:arm64v8-3.14
FROM ghcr.io/linuxserver/baseimage-alpine-nginx:arm64v8-3.16
# set version label
ARG BUILD_DATE

View File

@ -1,13 +1,14 @@
#! /usr/bin/env python3
from os import defpath, listdir, environ as env, chdir
from os import defpath, listdir, environ as env, chdir, getcwd
from os.path import isdir, isfile, join, dirname, abspath
from json import dump, dumps, loads, load
import subprocess
from hashlib import md5
chdir(dirname(abspath(__file__))) # Set working dir
def get_shas(output):
def get_shas(output) -> dict[str, str]:
"""Returns a dict of CSS files and SHAs"""
output_lines = output.splitlines() if output else []
sha_dict = {}
@ -19,9 +20,17 @@ def get_shas(output):
return(sha_dict)
def create_addons_json():
addon_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/addons/*.css"]) if isdir(".git") else []
SHAS = get_shas(addon_shas)
def get_md5_hash(file_path) -> str:
"""Returns the MD5 hash of a file"""
md5_hash = md5()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
md5_hash.update(byte_block)
return md5_hash.hexdigest()
def create_addons_json() -> str:
#addon_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/addons/*.css"]) if isdir(".git") else []
#SHAS = get_shas(addon_shas)
ADDONS = {"addons": {}}
addon_root = './css/addons'
addon_folders = [name for name in listdir(
@ -38,12 +47,12 @@ def create_addons_json():
f"{addon_root}/{app}/{addon}") if isfile(join(f"{addon_root}/{app}/{addon}", file))]
if len([f for f in files if f.endswith('.css')]) > 1:
ADDONS["addons"][app][addon].update({
"css": [f"{scheme}://{DOMAIN}/css/addons/{app}/{addon}/{file}?sha={SHAS.get(file)}" for file in files if file.split(".")[1] == "css"]
"css": [f"{scheme}://{DOMAIN}/css/addons/{app}/{addon}/{file}?sha={get_md5_hash(join(getcwd(),'css','addons',app,addon,file))}" for file in files if file.split(".")[1] == "css"]
}
)
else:
ADDONS["addons"][app].update({
addon: f"{scheme}://{DOMAIN}/css/addons/{app}/{addon}/{file}?sha={SHAS.get(file)}" for file in files if file.split(".")[1] == "css"
addon: f"{scheme}://{DOMAIN}/css/addons/{app}/{addon}/{file}?sha={get_md5_hash(join(getcwd(),'css','addons',app,addon,file))}" for file in files if file.split(".")[1] == "css"
}
)
extra_dirs = [dir for dir in listdir(
@ -54,7 +63,7 @@ def create_addons_json():
f"{addon_root}/{app}/{addon}/{dir}") if isfile(join(f"{addon_root}/{app}/{addon}/{dir}", file))]
ADDONS["addons"][app][addon].update({
dir: {
"css": [f"{scheme}://{DOMAIN}/css/addons/{app}/{addon}/{dir}/{extra_file}?sha={SHAS.get(extra_file)}" for extra_file in extra_dir_files if extra_file.split(".")[1] == "css"]
"css": [f"{scheme}://{DOMAIN}/css/addons/{app}/{addon}/{dir}/{extra_file}?sha={get_md5_hash(join(getcwd(),'css','addons',app,addon,dir,extra_file))}" for extra_file in extra_dir_files if extra_file.split(".")[1] == "css"]
}
}
)
@ -64,18 +73,18 @@ def create_addons_json():
def create_json(app_folders: list = None, themes: list = None, community_themes: list = None ,docker_mods: list = None, no_sub_folders=False) -> str:
if no_sub_folders:
THEMES_DICT = {}
theme_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/theme-options/*.css"]) if isdir(".git") else []
community_theme_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/community-theme-options/*.css"]) if isdir(".git") else []
THEME_SHAS = get_shas(theme_shas)
COMMUNITY_THEME_SHAS = get_shas(community_theme_shas)
#theme_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/theme-options/*.css"]) if isdir(".git") else []
#community_theme_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/community-theme-options/*.css"]) if isdir(".git") else []
#THEME_SHAS = get_shas(theme_shas)
#COMMUNITY_THEME_SHAS = get_shas(community_theme_shas)
THEMES = {
theme.split(".")[0].capitalize(): {
"url": f"{scheme}://{DOMAIN}/css/theme-options/{theme}?sha={THEME_SHAS.get(theme)}"
"url": f"{scheme}://{DOMAIN}/css/theme-options/{theme}?sha={get_md5_hash(join(getcwd(),'css','theme-options', theme))}"
}for theme in themes if themes
}
COMMUNITY_THEMES = {
theme.split(".")[0].capitalize(): {
"url": f"{scheme}://{DOMAIN}/css/community-theme-options/{theme}?sha={COMMUNITY_THEME_SHAS.get(theme)}"
"url": f"{scheme}://{DOMAIN}/css/community-theme-options/{theme}?sha={get_md5_hash(join(getcwd(),'css','community-theme-options', theme))}"
}for theme in community_themes if community_themes
}
THEMES_DICT.update(dict(sorted({
@ -93,12 +102,12 @@ def create_json(app_folders: list = None, themes: list = None, community_themes:
else:
ADDONS = loads(create_addons_json())
APPS = {}
app_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/base/*base.css"]) if isdir(".git") else []
SHAS = get_shas(app_shas)
#app_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/base/*base.css"]) if isdir(".git") else []
#SHAS = get_shas(app_shas)
APPS.update(dict(sorted({
"applications": {
app: {
"base_css": f"{scheme}://{DOMAIN}/css/base/{app}/{app}-base.css?sha={SHAS.get(f'{app}-base.css')}",
"base_css": f"{scheme}://{DOMAIN}/css/base/{app}/{app}-base.css?sha={get_md5_hash(join('css','base', app, f'{app}-base.css'))}",
"addons": ADDONS["addons"][app] if app in ADDONS["addons"] else {}
} for app in app_folders if not isfile(f'./css/base/{app}/.deprecated')
}
@ -106,7 +115,7 @@ def create_json(app_folders: list = None, themes: list = None, community_themes:
APPS.update(dict(sorted({
"deprecated": {
app: {
"base_css": f"{scheme}://{DOMAIN}/css/base/{app}/{app}-base.css?sha={SHAS.get(f'{app}-base.css')}",
"base_css": f"{scheme}://{DOMAIN}/css/base/{app}/{app}-base.css?sha={get_md5_hash(join('css','base', app, f'{app}-base.css'))}",
"addons": ADDONS["addons"][app] if app in ADDONS["addons"] else {}
} for app in app_folders if isfile(f'./css/base/{app}/.deprecated')
}
@ -122,16 +131,16 @@ def create_json(app_folders: list = None, themes: list = None, community_themes:
return dumps(APPS)
def create_theme_options() -> None:
app_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/base/*base.css"]) if isdir(".git") else []
theme_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/theme-options/*.css"]) if isdir(".git") else []
community_theme_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/community-theme-options/*.css"]) if isdir(".git") else []
THEME_SHAS = get_shas(theme_shas)
COMMUNITY_THEME_SHAS = get_shas(community_theme_shas)
APP_SHAS = get_shas(app_shas)
#app_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/base/*base.css"]) if isdir(".git") else []
#theme_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/theme-options/*.css"]) if isdir(".git") else []
#community_theme_shas = subprocess.check_output(["git", "ls-files", "-s", "./css/community-theme-options/*.css"]) if isdir(".git") else []
#THEME_SHAS = get_shas(theme_shas)
#COMMUNITY_THEME_SHAS = get_shas(community_theme_shas)
#APP_SHAS = get_shas(app_shas)
def create_css(theme, theme_type="standard"):
folder = "./css/base"
with open(f"{folder}/{app}/{theme.lower()}.css", "w") as create_app:
content = f'@import url("/css/base/{app}/{app}-base.css?sha={APP_SHAS.get(f"{app}-base.css")}");\n@import url("/css/{"theme-options" if theme_type=="standard" else "community-theme-options"}/{theme.lower()}.css?sha={THEME_SHAS.get(f"{theme.lower()}.css") if theme_type=="standard" else COMMUNITY_THEME_SHAS.get(f"{theme.lower()}.css")}");'
content = f'@import url("/css/base/{app}/{app}-base.css?sha={get_md5_hash(join(getcwd(),"css","base",app,f"{app}-base.css"))}");\n@import url("/css/{"theme-options" if theme_type=="standard" else "community-theme-options"}/{theme.lower()}.css?sha={get_md5_hash(join(getcwd(),"css","theme-options",f"{theme.lower()}.css")) if theme_type=="standard" else get_md5_hash(join(getcwd(),"css","community-theme-options",f"{theme.lower()}.css"))}");'
create_app.write(content)
with open("themes.json") as themes:
data = load(themes)