8 changed files with 398 additions and 5 deletions
@ -0,0 +1,227 @@
|
||||
/* ===== OTB shared branding ===== */ |
||||
body{ |
||||
padding-bottom: 56px; |
||||
} |
||||
|
||||
.site-container{ |
||||
max-width: 1100px; |
||||
margin: 0 auto; |
||||
padding: 20px 18px 0 18px; |
||||
} |
||||
|
||||
.site-header{ |
||||
width: 100%; |
||||
} |
||||
|
||||
.site-nav{ |
||||
display:flex; |
||||
align-items:center; |
||||
justify-content:space-between; |
||||
gap:18px; |
||||
padding:12px 0 22px 0; |
||||
} |
||||
|
||||
.site-brand{ |
||||
display:flex; |
||||
align-items:center; |
||||
gap:14px; |
||||
text-decoration:none; |
||||
color:inherit; |
||||
} |
||||
|
||||
.site-brand img{ |
||||
height:60px; |
||||
width:auto; |
||||
display:block; |
||||
object-fit:contain; |
||||
background: rgba(255,255,255,0.92); |
||||
padding: 6px 12px; |
||||
border-radius: 999px; |
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.35); |
||||
} |
||||
|
||||
.site-title{ |
||||
display:flex; |
||||
flex-direction:column; |
||||
line-height:1.1; |
||||
} |
||||
|
||||
.site-title strong{ |
||||
letter-spacing:.2px; |
||||
color: var(--text, #e8eefc); |
||||
} |
||||
|
||||
.site-title span{ |
||||
color: var(--muted, #aab6d6); |
||||
font-size:13px; |
||||
margin-top:2px; |
||||
} |
||||
|
||||
.site-navlinks{ |
||||
display:flex; |
||||
gap:12px; |
||||
flex-wrap:wrap; |
||||
justify-content:flex-end; |
||||
align-items:center; |
||||
} |
||||
|
||||
.site-navlinks > a, |
||||
.dropdown-toggle{ |
||||
text-decoration:none; |
||||
padding:8px 10px; |
||||
border-radius:12px; |
||||
color: var(--muted, #aab6d6); |
||||
border:1px solid transparent; |
||||
} |
||||
|
||||
.site-navlinks > a:hover, |
||||
.dropdown-toggle:hover{ |
||||
color: var(--text, #e8eefc); |
||||
border-color: rgba(255,255,255,.08); |
||||
background: rgba(255,255,255,.03); |
||||
} |
||||
|
||||
.dropdown{ |
||||
position:relative; |
||||
display:inline-block; |
||||
} |
||||
|
||||
.dropdown-toggle{ |
||||
display:inline-block; |
||||
cursor:pointer; |
||||
} |
||||
|
||||
.dropdown-menu{ |
||||
position:absolute; |
||||
top:calc(100% + 8px); |
||||
right:0; |
||||
min-width:220px; |
||||
display:none; |
||||
padding:10px; |
||||
border-radius:14px; |
||||
background:rgba(18,24,37,.98); |
||||
border:1px solid rgba(255,255,255,.08); |
||||
box-shadow:0 16px 40px rgba(0,0,0,.35); |
||||
z-index:9999; |
||||
} |
||||
|
||||
.dropdown:hover .dropdown-menu, |
||||
.dropdown:focus-within .dropdown-menu{ |
||||
display:block; |
||||
} |
||||
|
||||
.dropdown-menu a{ |
||||
display:block; |
||||
padding:9px 10px; |
||||
border-radius:10px; |
||||
color:var(--muted, #aab6d6); |
||||
text-decoration:none; |
||||
white-space:nowrap; |
||||
margin:0; |
||||
} |
||||
|
||||
.dropdown-menu a + a{ |
||||
margin-top:4px; |
||||
} |
||||
|
||||
.dropdown-menu a:hover{ |
||||
color:var(--text, #e8eefc); |
||||
background:rgba(255,255,255,.04); |
||||
} |
||||
|
||||
.otb-statusbar{ |
||||
position: fixed; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
z-index: 9999; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
min-height: 42px; |
||||
padding: 8px 14px; |
||||
background: rgba(8, 16, 32, 0.94); |
||||
border-top: 1px solid rgba(255,255,255,.10); |
||||
backdrop-filter: blur(8px); |
||||
box-shadow: 0 -8px 24px rgba(0,0,0,.28); |
||||
} |
||||
|
||||
.otb-statusbar-inner{ |
||||
width: 100%; |
||||
max-width: 1100px; |
||||
display: flex; |
||||
gap: 10px; |
||||
align-items: center; |
||||
justify-content: center; |
||||
flex-wrap: wrap; |
||||
text-align: center; |
||||
color: var(--muted, #aab6d6); |
||||
font-size: 12px; |
||||
line-height: 1.35; |
||||
} |
||||
|
||||
.otb-statusbar strong{ |
||||
color: var(--text, #e8eefc); |
||||
font-weight: 700; |
||||
} |
||||
|
||||
.otb-statusbar a{ |
||||
color: #62e6b7; |
||||
text-decoration: none; |
||||
font-weight: 600; |
||||
} |
||||
|
||||
.otb-statusbar a:hover{ |
||||
text-decoration: underline; |
||||
} |
||||
|
||||
.otb-dot{ |
||||
width: 6px; |
||||
height: 6px; |
||||
border-radius: 999px; |
||||
display: inline-block; |
||||
background: rgba(255,255,255,.25); |
||||
flex: 0 0 auto; |
||||
} |
||||
|
||||
@media (max-width: 900px){ |
||||
.site-nav{ |
||||
align-items:flex-start; |
||||
flex-direction:column; |
||||
} |
||||
|
||||
.site-navlinks{ |
||||
justify-content:flex-start; |
||||
} |
||||
|
||||
.dropdown{ |
||||
width:100%; |
||||
} |
||||
|
||||
.dropdown-toggle{ |
||||
width:100%; |
||||
} |
||||
|
||||
.dropdown-menu{ |
||||
position:static; |
||||
right:auto; |
||||
top:auto; |
||||
min-width:100%; |
||||
margin-top:6px; |
||||
} |
||||
|
||||
.site-brand img{ |
||||
height:54px; |
||||
} |
||||
} |
||||
|
||||
@media (max-width: 700px){ |
||||
body{ |
||||
padding-bottom: 72px; |
||||
} |
||||
|
||||
.otb-statusbar-inner{ |
||||
font-size: 11px; |
||||
line-height: 1.25; |
||||
} |
||||
} |
||||
@ -0,0 +1,49 @@
|
||||
#!/bin/bash |
||||
set -e |
||||
SITE_DIR="/var/www/outsidethebox.top" |
||||
cd "$SITE_DIR" || exit 1 |
||||
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)" |
||||
BKDIR="backups/shared-brand-$STAMP" |
||||
mkdir -p "$BKDIR" |
||||
cp -av index.html pricing.html terms.html contact.html assets/style.css "$BKDIR"/ |
||||
|
||||
HEADER="$(cat /home/def/otb-shared-brand/header.html)" |
||||
FOOTER="$(cat /home/def/otb-shared-brand/footer.html)" |
||||
BRANDCSS="$(cat /home/def/otb-shared-brand/brand.css)" |
||||
|
||||
python3 - <<PY |
||||
from pathlib import Path |
||||
import re |
||||
|
||||
header = """$HEADER""" |
||||
footer = """$FOOTER""" |
||||
brandcss = """$BRANDCSS""" |
||||
|
||||
for name in ["index.html", "pricing.html", "terms.html", "contact.html"]: |
||||
p = Path(name) |
||||
text = p.read_text(encoding="utf-8") |
||||
|
||||
if '<header class="site-header">' in text: |
||||
text = re.sub(r'<header class="site-header">.*?</header>', header, text, count=1, flags=re.S) |
||||
elif '<header class="header">' in text: |
||||
text = re.sub(r'<header class="header">.*?</header>', header, text, count=1, flags=re.S) |
||||
else: |
||||
text = text.replace('<body>', '<body>\n' + header, 1) |
||||
|
||||
if '<div class="otb-statusbar">' in text: |
||||
text = re.sub(r'<div class="otb-statusbar">.*?</div>\s*</div>', footer, text, count=1, flags=re.S) |
||||
else: |
||||
text = text.replace('</body>', footer + '\n</body>', 1) |
||||
|
||||
p.write_text(text, encoding="utf-8") |
||||
|
||||
cssp = Path("assets/style.css") |
||||
css = cssp.read_text(encoding="utf-8") |
||||
if "/* ===== OTB shared branding ===== */" in css: |
||||
css = re.sub(r'/\* ===== OTB shared branding ===== \*/.*', brandcss, css, flags=re.S) |
||||
else: |
||||
css += "\n\n" + brandcss |
||||
cssp.write_text(css, encoding="utf-8") |
||||
print("mintme deploy complete") |
||||
PY |
||||
@ -0,0 +1,71 @@
|
||||
#!/bin/bash |
||||
set -e |
||||
APP_DIR="/home/def/monitor" |
||||
cd "$APP_DIR" || exit 1 |
||||
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)" |
||||
BKDIR="backups/shared-brand-$STAMP" |
||||
mkdir -p "$BKDIR" |
||||
cp -av frontend/index.html frontend/styles.css "$BKDIR"/ || true |
||||
|
||||
HEADER="$(cat /home/def/otb-shared-brand/header.html)" |
||||
FOOTER="$(cat /home/def/otb-shared-brand/footer.html)" |
||||
BRANDCSS="$(cat /home/def/otb-shared-brand/brand.css)" |
||||
|
||||
cat > frontend/index.html <<HTML |
||||
<!doctype html> |
||||
<html lang="en" data-theme="dark"> |
||||
<head> |
||||
<meta charset="utf-8" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
||||
<title>Monitor</title> |
||||
<link rel="stylesheet" href="/styles.css" /> |
||||
</head> |
||||
<body> |
||||
$HEADER |
||||
|
||||
<div class="wrap"> |
||||
<header class="top"> |
||||
<div> |
||||
<div class="title">Monitor</div> |
||||
<div class="sub">7-day snapshot • rotating refresh</div> |
||||
</div> |
||||
|
||||
<div class="top-right"> |
||||
<div class="status-pill" id="status">Loading…</div> |
||||
<div class="cycle" id="cycle"></div> |
||||
|
||||
<label class="switch" title="Toggle theme"> |
||||
<input type="checkbox" id="themeToggle" aria-label="Toggle theme" /> |
||||
<span class="slider"></span> |
||||
</label> |
||||
</div> |
||||
</header> |
||||
|
||||
<div class="card"> |
||||
<div id="root"></div> |
||||
</div> |
||||
</div> |
||||
|
||||
$FOOTER |
||||
|
||||
<script src="/app.js" defer></script> |
||||
</body> |
||||
</html> |
||||
HTML |
||||
|
||||
python3 - <<PY |
||||
from pathlib import Path |
||||
import re |
||||
brandcss = """$BRANDCSS""" |
||||
p = Path("frontend/styles.css") |
||||
css = p.read_text(encoding="utf-8") |
||||
if "/* ===== OTB shared branding ===== */" in css: |
||||
css = re.sub(r'/\* ===== OTB shared branding ===== \*/.*', brandcss, css, flags=re.S) |
||||
else: |
||||
css = brandcss + "\n\n" + css |
||||
p.write_text(css, encoding="utf-8") |
||||
print("monitor branding css updated") |
||||
PY |
||||
|
||||
/home/def/monitor/deploy-monitor.sh |
||||
@ -0,0 +1,41 @@
|
||||
#!/bin/bash |
||||
set -e |
||||
APP_DIR="/home/def/otb_billing" |
||||
cd "$APP_DIR" || exit 1 |
||||
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)" |
||||
BKDIR="backups/shared-brand-$STAMP" |
||||
mkdir -p "$BKDIR" |
||||
cp -av templates/includes/site_nav.html static/css/style.css templates/portal_*.html "$BKDIR"/ |
||||
|
||||
cp -av /home/def/otb-shared-brand/header.html templates/includes/site_nav.html |
||||
|
||||
FOOTER="$(cat /home/def/otb-shared-brand/footer.html)" |
||||
BRANDCSS="$(cat /home/def/otb-shared-brand/brand.css)" |
||||
|
||||
python3 - <<PY |
||||
from pathlib import Path |
||||
import re |
||||
|
||||
footer = """$FOOTER""" |
||||
brandcss = """$BRANDCSS""" |
||||
|
||||
for p in Path("templates").glob("portal_*.html"): |
||||
text = p.read_text(encoding="utf-8") |
||||
if '<div class="otb-statusbar">' in text: |
||||
text = re.sub(r'<div class="otb-statusbar">.*?</div>\s*</div>', footer, text, count=1, flags=re.S) |
||||
else: |
||||
text = text.replace('{% include "footer.html" %}', footer + '\n\n {% include "footer.html" %}') |
||||
p.write_text(text, encoding="utf-8") |
||||
|
||||
cssp = Path("static/css/style.css") |
||||
css = cssp.read_text(encoding="utf-8") |
||||
if "/* ===== OTB shared branding ===== */" in css: |
||||
css = re.sub(r'/\* ===== OTB shared branding ===== \*/.*', brandcss, css, flags=re.S) |
||||
else: |
||||
css += "\n\n" + brandcss |
||||
cssp.write_text(css, encoding="utf-8") |
||||
print("portal deploy complete") |
||||
PY |
||||
|
||||
sudo systemctl restart otb_billing.service |
||||
Loading…
Reference in new issue