diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..20b13a7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +# Avoid sending secrets or dev tooling into the build context +# config/ and plugins/ are included (no secrets; PHP configs read from ENV at runtime) +node_modules +.git +.prettierrc +.prettierignore +*.md +.env +.env.* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..048d79a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.env +.env.* +!.env.example + +node_modules +pnpm-lock.yaml diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ee89780 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +node_modules +pnpm-lock.yaml diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..85b0780 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,15 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none", + "overrides": [ + { + "files": "*.yml", + "options": { + "tabWidth": 4, + "proseWrap": "preserve" + } + } + ] +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..675a1ed --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:alpine + +COPY nginx/conf.d/ /etc/nginx/conf.d/ +COPY src/ /usr/share/nginx/html/ diff --git a/README.md b/README.md index e69de29..604ebda 100644 --- a/README.md +++ b/README.md @@ -0,0 +1 @@ +# Simple Package (Docker) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b717932 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,34 @@ +services: + service: + image: git.mifi.dev/...:${IMAGE_TAG:-latest} + container_name: service + environment: + - ENV_NAME=value + healthcheck: + test: + [ + 'CMD', + '/usr/local/bin/healthcheck.sh', + '--connect', + '--innodb_initialized' + ] + retries: 10 + start_period: 20s + networks: + - network + volumes: + - volume:/var/lib/... + - other_volume:/var/lib/... + depends_on: + - other service + restart: unless-stopped + +networks: + network: + external: true + +volumes: + volume: + external: true + other_volume: + external: false \ No newline at end of file diff --git a/nginx/conf.d/default.conf b/nginx/conf.d/default.conf new file mode 100644 index 0000000..af6f8b0 --- /dev/null +++ b/nginx/conf.d/default.conf @@ -0,0 +1,40 @@ +server { + listen 80; + listen [::]:80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # HTML: revalidate so content updates are seen quickly + location ~* \.html?$ { + add_header Cache-Control "public, no-cache"; + try_files $uri $uri/ =404; + } + + # Versioned/hashed assets: long cache, immutable + location ~* \.(js|css)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # Images and media + location ~* \.(ico|png|jpg|jpeg|gif|webp|svg|avif)$ { + expires 30d; + add_header Cache-Control "public"; + try_files $uri =404; + } + + # Fonts + location ~* \.(woff2?|ttf|otf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # Default: serve static files, then try directory/index, then 404 + location / { + add_header Cache-Control "public, no-cache"; + try_files $uri $uri/ /index.html =404; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e12d6bc --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "...", + "version": "1.0.0", + "private": true, + "packageManager": "pnpm@10.29.3", + "scripts": { + "format": "prettier --write .", + "format:check": "prettier --check .", + "lint": "yamllint .woodpecker/ci.yml .woodpecker/build.yml .woodpecker/deploy.yml docker-compose.yml", + "docker:build": "docker build --platform linux/amd64 -t git.mifi.dev/.../...:latest .", + "docker:push": "docker push git.mifi.dev/.../...:latest" + }, + "devDependencies": { + "prettier": "^3.4.2", + "yaml-lint": "^1.7.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/.../...git" + } +} diff --git a/src.delete/assets/css/accordion.css b/src.delete/assets/css/accordion.css new file mode 100644 index 0000000..47aed45 --- /dev/null +++ b/src.delete/assets/css/accordion.css @@ -0,0 +1,93 @@ +:root { + --accordion-bg: #fff; +} + +@media (prefers-color-scheme: dark) { + :root { + --accordion-bg: #24264a; + } +} + +.accordion { + margin: 1.5rem 0 1rem 0; + border-radius: var(--radius); + overflow: hidden; + box-shadow: 0 1px 6px 0 rgba(90,100,140,0.06); + background: var(--accordion-bg); +} + +.accordion-section { + border-top: 1px solid #eee; + background: var(--accordion-bg); +} + +@media (prefers-color-scheme: dark) { + .accordion-section { + border-top: 1px solid #28284b; + } +} + +.accordion-section:first-child { + border-top: none; +} + +.accordion-trigger { + display: flex; + align-items: center; + cursor: pointer; + width: 100%; + background: none; + border: none; + font-size: 1.12rem; + padding: 1.1rem 1.2rem; + text-align: left; + font-weight: 600; + transition: background 0.2s; + color: var(--text-main); + outline: none; + gap: 0.6em; + position: relative; +} + +.accordion-trigger:hover, .accordion-trigger:focus { + background: var(--background); +} + +.accordion-trigger .icon { + margin-right: 0.5em; + font-size: 1.3em; + opacity: 0.86; + transition: transform 0.2s; +} + +.accordion-trigger[aria-expanded="true"] .icon { + transform: rotate(90deg); +} + +.accordion-content { + max-height: 0; + overflow: hidden; + background: var(--surface); + transition: max-height 0.3s cubic-bezier(.7,0,.3,1); + font-size: 1rem; + padding: 0 1.5rem; + color: var(--text-main); + text-align: left; +} + +.accordion-section.open .accordion-content { + padding: 1.2rem 1.5rem 1.3rem 1.5rem; + max-height: 1000px; + transition: max-height 0.5s cubic-bezier(.7,0,.3,1); +} + +@media (max-width: 600px) { + .accordion-trigger { + font-size: 1rem; + padding: 0.93rem 0.8rem; + } + + .accordion-section.open .accordion-content { + padding: 0.7rem 0.8rem 0.9rem 0.8rem; + } +} diff --git a/src.delete/assets/css/settings.css b/src.delete/assets/css/settings.css new file mode 100644 index 0000000..a0531bc --- /dev/null +++ b/src.delete/assets/css/settings.css @@ -0,0 +1,106 @@ +:root { + --content-bg: #f8fafc; + --faq-a: #333; + --table-bg: transparent; +} + +@media (prefers-color-scheme: dark) { + :root { + --content-bg: #21223a; + --faq-a: #b7badf; + --table-bg: #252745; + } +} + +.general-settings { + background: var(--surface); + border-radius: var(--radius); + padding: 1.2rem 1.1rem 1.1rem 1.1rem; + margin-bottom: 2.1rem; + box-shadow: var(--shadow); + position: relative; + z-index: 1; +} + +.general-settings h2 { + font-size: 1.14rem; + margin: 0 0 0.6rem 0; + font-weight: 700; + letter-spacing: -0.5px; +} + +table { + width: 100%; + border-collapse: collapse; + margin-bottom: 1em; + background: var(--table-bg); +} + +td { + padding: 0.38em 0.5em; + border: none; + font-size: 1rem; +} + +td:first-child { + color: var(--text-muted); + font-weight: 500; + width: 44%; + white-space: nowrap; +} + +.tip { + background: #eef2ff; + color: var(--accent-light); + border-radius: 0.7em; + font-size: 0.98em; + padding: 0.48em 0.8em; + display: inline-block; + margin: 0.3em 0 0.2em 0; + text-align: center; +} + +@media (prefers-color-scheme: dark) { + .tip { + background: #232555; + color: #a5b4fc; + } +} + +.faq-q { + font-weight: 600; + margin-top: 0.8em; + color: var(--accent); +} + +.faq-a { + margin: 0.1em 0 0.6em 0.3em; + color: var(--faq-a); +} + +a { + color: var(--accent); + text-decoration: underline; +} + +@media (max-width: 600px) { + .container { + padding: 1.1rem 0.5rem 1rem 0.5rem; + } + + h1 { + font-size: 1.36rem; + } + + .general-settings { + padding: 0.8rem 0.6rem 0.8rem 0.6rem; + } + + .general-settings h2 { + font-size: 1rem; + } + + td { + font-size: 0.98em; + } +} diff --git a/src.delete/assets/css/style.css b/src.delete/assets/css/style.css new file mode 100644 index 0000000..4483d0c --- /dev/null +++ b/src.delete/assets/css/style.css @@ -0,0 +1,145 @@ +:root { + --accent: #4f46e5; + --accent-light: #6366f1; + --background: #f7fafc; + --button-bg: #2bc4fa; + --button-hover: #22a0ca; + --button-text: #181a20; + --text-main: #23243a; + --text-muted: #64748b; + --radius: 1.25rem; + --shadow: 0 2px 12px 0 rgba(20,30,60,0.09); + --surface: rgba(255,255,255,0.94); +} + +@media (prefers-color-scheme: dark) { + :root { + --accent: #a5b4fc; + --accent-light: #818cf8; + --background: #15181c; + --button-bg: #2bc4fa; + --button-hover: #22a0ca; + --button-text: #181a20; + --shadow: 0 2px 16px 0 rgba(8,8,24,0.24); + --surface: rgba(30,34,42,0.9); + --text-main: #f6f7fa; + --text-muted: #aab2bd; + } +} + +html, body { + margin: 0; + padding: 0; + background: var(--background); + color: var(--text-main); + font-family: 'Inter', 'Segoe UI', Arial, sans-serif; + font-size: 17px; + min-height: 100vh; +} + +body { + display: flex; + flex-flow:column nowrap; + align-items: center; + justify-content: center; + min-height: 100dvh; +} + +.container { + display: flex; + flex-grow: 1; + flex-flow: column nowrap; + justify-content: center; + margin: 1rem; + max-width: 370px; + text-align: center; +} + +.card { + background: var(--surface); + border-radius: 1.5rem; + box-shadow: var(--shadow); + padding: 3rem 2rem; +} + +.container.wide { + max-width: 580px; +} + +.emoji { + font-size: 3rem; + margin-bottom: 1rem; +} + +h1 { + font-size: 2rem; + margin-bottom: 0.5rem; + font-weight: 700; + letter-spacing: -1px; +} + +.intro { + color: var(--text-muted); + margin-bottom: 1.7rem; + text-align: center; +} + +p { + color: var(--text-muted); + font-size: 1.1rem; + line-height: 1.5; + margin-bottom: 2rem; +} + +.button { + display: block; + padding: 0.75rem 1.5rem; + margin-bottom: 0.75rem; + background: var(--button-bg); + color: var(--button-text); + border-radius: 999px; + font-weight: 600; + text-decoration: none; + transition: background 0.2s; + box-shadow: 0 1px 4px 0 rgba(43,196,250,0.11); +} + +.button:hover { + background: var(--button-hover); +} + +a { + color: var(--accent); + text-decoration: underline; +} + +a:hover { + text-decoration: none; +} + +footer { + color: #bbb; + font-size: 0.94em; + letter-spacing: 0.01em; + margin-top: 2.5em; + text-align: center; +} + +footer p { + font-size: 0.8em; + margin: 0.5rem 0 1rem; +} + +.badges { + align-items: center; + display: flex; + flex-flow: row nowrap; + gap: 1rem; + justify-content: center; +} + +@media (max-width: 400px) { + .container { + padding: 2rem 0.5rem; + } +} diff --git a/src.delete/assets/js/accordion.js b/src.delete/assets/js/accordion.js new file mode 100644 index 0000000..2c2746c --- /dev/null +++ b/src.delete/assets/js/accordion.js @@ -0,0 +1,28 @@ +document.querySelectorAll('.accordion-trigger').forEach((btn) => { + btn.addEventListener('click', function() { + const section = btn.closest('.accordion-section'); + const expanded = btn.getAttribute('aria-expanded') === "true"; + document.querySelectorAll('.accordion-section').forEach(s => { + if (s === section) { + s.classList.toggle('open', !expanded); + btn.setAttribute('aria-expanded', String(!expanded)); + const content = btn.nextElementSibling; + content.style.maxHeight = !expanded ? (content.scrollHeight+40) + "px" : "0px"; + } else { + s.classList.remove('open'); + s.querySelector('.accordion-trigger').setAttribute('aria-expanded', "false"); + s.querySelector('.accordion-content').style.maxHeight = "0px"; + } + }); + }); + + btn.addEventListener('keydown', function(e) { + if (e.key === "ArrowDown" || e.key === "ArrowUp") { + const triggers = Array.from(document.querySelectorAll('.accordion-trigger')); + let idx = triggers.indexOf(e.target); + if (e.key === "ArrowDown") idx = (idx + 1) % triggers.length; + else idx = (idx - 1 + triggers.length) % triggers.length; + triggers[idx].focus(); + } + }); +}); diff --git a/src.delete/assets/media/embed-badge-emailtest.svg b/src.delete/assets/media/embed-badge-emailtest.svg new file mode 100644 index 0000000..be2fddc --- /dev/null +++ b/src.delete/assets/media/embed-badge-emailtest.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src.delete/assets/media/embed-badge-websitetest.svg b/src.delete/assets/media/embed-badge-websitetest.svg new file mode 100644 index 0000000..82db1a8 --- /dev/null +++ b/src.delete/assets/media/embed-badge-websitetest.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src.delete/help/index.html b/src.delete/help/index.html new file mode 100644 index 0000000..a171fbe --- /dev/null +++ b/src.delete/help/index.html @@ -0,0 +1,146 @@ + + +
+ + +Friendly help for setting up your email—works with Outlook, Apple Mail, Thunderbird, phones, and more.
+| Email Address | your.name@yourdomain.com |
| Username | your.name@yourdomain.com |
| Password | (your email password) |
| Incoming Server | mail.mifi.holdings |
| Outgoing Server | mail.mifi.holdings |
| IMAP Port | 993 (SSL/TLS) |
| POP3 Port | 995 (SSL/TLS) |
| SMTP Port | 587 (STARTTLS) or 465 (SSL/TLS) |
| Authentication | Required (use same as incoming) |
| Encryption | SSL/TLS or STARTTLS |
mail.mifi.holdings, port 993 (SSL/TLS)mail.mifi.holdings, port 587 (STARTTLS) or 465 (SSL/TLS)mail.mifi.holdingsmail.mifi.holdings, correct ports, SSL/TLS required
+ You've reached mail.mifi.holdings.
+ There's nothing exciting here—just some gears whirring and mail being sorted.
+ Looking for your messages?
+