Initial docker conversion commit

This commit is contained in:
2026-02-13 16:07:02 -03:00
parent 622fe6e6fe
commit 36ff5d0c1a
17 changed files with 686 additions and 0 deletions

9
.dockerignore Normal file
View File

@@ -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.*

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
.env
.env.*
!.env.example
node_modules
pnpm-lock.yaml

2
.prettierignore Normal file
View File

@@ -0,0 +1,2 @@
node_modules
pnpm-lock.yaml

15
.prettierrc Normal file
View File

@@ -0,0 +1,15 @@
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"overrides": [
{
"files": "*.yml",
"options": {
"tabWidth": 4,
"proseWrap": "preserve"
}
}
]
}

4
Dockerfile Normal file
View File

@@ -0,0 +1,4 @@
FROM nginx:alpine
COPY nginx/conf.d/ /etc/nginx/conf.d/
COPY src/ /usr/share/nginx/html/

View File

@@ -0,0 +1 @@
# Simple Package (Docker)

34
docker-compose.yml Normal file
View File

@@ -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

40
nginx/conf.d/default.conf Normal file
View File

@@ -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;
}
}

21
package.json Normal file
View File

@@ -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"
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
});
});

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

146
src.delete/help/index.html Normal file
View File

@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Welcome to Email from mifi Ventures</title>
<meta name="description" content="Setup help for users of Email from mifi Ventures">
<link rel="stylesheet" href="/assets/css/style.css">
<link rel="stylesheet" href="/assets/css/settings.css">
<link rel="stylesheet" href="/assets/css/accordion.css">
</head>
<body>
<div class="container wide">
<h1>Welcome to Email from mifi Ventures</h1>
<div class="intro">
<strong>Lets get your inbox ready! 📬</strong><br>
<p>Friendly help for setting up your email—works with Outlook, Apple Mail, Thunderbird, phones, and more.</p>
</div>
<section class="card general-settings" aria-label="General Email Settings">
<h2>General Settings (All Clients)</h2>
<table>
<tr><td>Email Address</td><td>your.name@yourdomain.com</td></tr>
<tr><td>Username</td><td>your.name@yourdomain.com</td></tr>
<tr><td>Password</td><td>(your email password)</td></tr>
<tr><td>Incoming Server</td><td><b>mail.mifi.holdings</b></td></tr>
<tr><td>Outgoing Server</td><td><b>mail.mifi.holdings</b></td></tr>
<tr><td>IMAP Port</td><td>993 (SSL/TLS)</td></tr>
<tr><td>POP3 Port</td><td>995 (SSL/TLS)</td></tr>
<tr><td>SMTP Port</td><td>587 (STARTTLS) or 465 (SSL/TLS)</td></tr>
<tr><td>Authentication</td><td>Required (use same as incoming)</td></tr>
<tr><td>Encryption</td><td>SSL/TLS or STARTTLS</td></tr>
</table>
<span class="tip">Tip: Always use your <b>full email address</b> as your username!</span>
</section>
<div class="accordion" id="helpAccordion">
<!-- Outlook -->
<section class="accordion-section">
<button class="accordion-trigger" aria-expanded="false">
<span class="icon"></span> Microsoft Outlook
</button>
<div class="accordion-content">
<ol>
<li>Go to <b>File → Add Account</b></li>
<li>Enter your full email address</li>
<li>Choose <b>Advanced options</b> → check “Set up manually”</li>
<li>Select <b>IMAP</b> (recommended) or POP</li>
<li>Incoming server: <code>mail.mifi.holdings</code>, port <b>993</b> (SSL/TLS)</li>
<li>Outgoing server: <code>mail.mifi.holdings</code>, port <b>587</b> (STARTTLS) or <b>465</b> (SSL/TLS)</li>
<li>Username: full email address; Password: your password</li>
<li>Click <b>Connect</b></li>
</ol>
<span class="tip">If sending fails, make sure “Require logon using SPA” is <b>unchecked</b>.</span>
</div>
</section>
<!-- Apple Mail -->
<section class="accordion-section">
<button class="accordion-trigger" aria-expanded="false">
<span class="icon"></span> Apple Mail (macOS, iOS)
</button>
<div class="accordion-content">
<ol>
<li>Add Account &rarr; <b>Other Mail Account</b></li>
<li>Enter your name, email, and password</li>
<li>Incoming/Outgoing server: <code>mail.mifi.holdings</code></li>
<li>IMAP port: <b>993</b> (SSL); SMTP port: <b>587</b> (STARTTLS) or <b>465</b> (SSL)</li>
<li>Use full email address for username</li>
</ol>
</div>
</section>
<!-- Thunderbird -->
<section class="accordion-section">
<button class="accordion-trigger" aria-expanded="false">
<span class="icon"></span> Thunderbird
</button>
<div class="accordion-content">
<ol>
<li>Menu → Account Settings → Add Mail Account</li>
<li>Fill in your name, email, and password</li>
<li>Click “Configure manually” and use settings above</li>
</ol>
</div>
</section>
<!-- Mobile -->
<section class="accordion-section">
<button class="accordion-trigger" aria-expanded="false">
<span class="icon"></span> iOS / Android Mail / Gmail App
</button>
<div class="accordion-content">
<ul>
<li>Add Account → Other</li>
<li>Enter your email and password</li>
<li>Manual setup: <code>mail.mifi.holdings</code>, correct ports, SSL/TLS required</li>
<li>Gmail app: tap profile → Add account → Other, fill in details, use IMAP</li>
</ul>
</div>
</section>
<!-- FAQ -->
<section class="accordion-section">
<button class="accordion-trigger" aria-expanded="false">
<span class="icon"></span> FAQ / Troubleshooting
</button>
<div class="accordion-content">
<div class="faq-q">Q: My email wont send?</div>
<div class="faq-a">Check that youre using your full email address for both incoming and outgoing username, and that the port is 587 or 465.</div>
<div class="faq-q">Q: SSL/TLS errors?</div>
<div class="faq-a">Ensure SSL or STARTTLS is enabled for both incoming and outgoing mail.</div>
<div class="faq-q">Q: Still stuck?</div>
<div class="faq-a">Contact <a href="mailto:postmaster@mifi.holdings">postmaster@mifi.holdings</a>.<br>
Please include any error messages, your mail app, and a screenshot if you can!</div>
</div>
</section>
<!-- Pro Tips -->
<section class="accordion-section">
<button class="accordion-trigger" aria-expanded="false">
<span class="icon"></span> Pro Tips & Advanced
</button>
<div class="accordion-content">
<ul>
<li><b>IMAP syncs</b> your mail everywhere—choose IMAP unless you know you want POP3.</li>
<li>Your login is always your <b>full email address</b>.</li>
<li>Check your Spam/Junk folder for misfiled good emails.</li>
<li>Advanced: IMAP path prefix = <b>(leave blank)</b>; SMTP authentication is always required.</li>
</ul>
</div>
</section>
</div>
<footer>
<p class="badges">
<a href="https://internet.nl/halloffame/mail/" title="internet.nl 100% Email Hall of Fame" class="badge" rel="external"><img src="/assets/media/embed-badge-emailtest.svg" alt="internet.nl 100% Hall of Fame - Email"></a>
<a href="https://internet.nl/halloffame/web/" title="internet.nl 100% Web Hall of Fame" class="badge" rel="external"><img src="/assets/media/embed-badge-websitetest.svg" alt="internet.nl 100% Hall of Fame - Web"></a>
</p>
<p>© mifi Ventures. All rights reserved.</p>
</footer>
</div>
<script src="/assets/js/accordion.js"></script>
</body>
</html>

34
src.delete/index.html Normal file
View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>mail.mifi.holdings</title>
<link rel="stylesheet" href="/assets/css/style.css">
</head>
<body>
<div class="container">
<div class="card">
<header>
<div class="emoji">📮</div>
<h1>This is just a mailbox.</h1>
</header>
<p>
You've reached <b>mail.mifi.holdings</b>.<br>
There's nothing exciting here—just some gears whirring and mail being sorted.<br>
Looking for your messages?
</p>
<a href="/help" class="button">Email Setup Help</a>
<a href="https://webmail.mifi.holdings" class="button">Go to Webmail</a>
<a href="https://postmaster.mifi.holdings/users/login.php">Change/Forgot Password</a>
</div>
</div>
<footer>
<p class="badges">
<a href="https://internet.nl/halloffame/mail/" title="internet.nl 100% Email Hall of Fame" class="badge" rel="external"><img src="/assets/media/embed-badge-emailtest.svg" alt="internet.nl 100% Hall of Fame - Email"></a>
<a href="https://internet.nl/halloffame/web/" title="internet.nl 100% Web Hall of Fame" class="badge" rel="external"><img src="/assets/media/embed-badge-websitetest.svg" alt="internet.nl 100% Hall of Fame - Web"></a>
</p>
<p>© mifi Ventures. All rights reserved.</p>
</footer>
</body>
</html>