Initial vibeproxy implementation

Self-contained HTTP/HTTPS/TCP/UDP reverse proxy manager written in Go,
with SQLite storage, a Bootstrap 5 admin UI (Go templates, no JS
framework), and a REST API under /api/v1.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QTXFrNP1imCW7jtfL5SZrw
This commit is contained in:
2026-08-17 10:28:59 -05:00
co-authored by Claude Sonnet 5
commit ba1f7c4c03
105 changed files with 9142 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
{{define "certificates"}}{{template "header" .}}
<div class="d-flex justify-content-end gap-2 mb-3">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#issueLEModal"><i class="bi bi-shield-plus"></i> Request Let's Encrypt</button>
<button class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#uploadCertModal"><i class="bi bi-upload"></i> Upload Custom</button>
</div>
<div class="card">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light"><tr><th>Name</th><th>Domains</th><th>Type</th><th>Expires</th><th>Status</th><th></th></tr></thead>
<tbody>
{{range .Data}}
<tr>
<td>{{.Name}}</td>
<td>{{join .Domains ", "}}</td>
<td><span class="badge text-bg-{{if eq .Type "letsencrypt"}}success{{else}}secondary{{end}}">{{.Type}}</span></td>
<td>{{if .NotAfter}}{{fmtTime (deref .NotAfter)}}{{else}}—{{end}}</td>
<td>{{if .LastError}}<span class="badge text-bg-danger" title="{{.LastError}}">error</span>{{else}}<span class="badge text-bg-success">ok</span>{{end}}</td>
<td class="text-end">
{{if eq .Type "letsencrypt"}}
<form method="post" action="/certificates/{{.ID}}/renew" class="d-inline">
{{template "csrf_field" $.CSRFToken}}
<button class="btn btn-sm btn-outline-secondary" title="Renew now"><i class="bi bi-arrow-clockwise"></i></button>
</form>
{{end}}
<form method="post" action="/certificates/{{.ID}}/delete" id="del-cert-{{.ID}}" class="d-inline">
{{template "csrf_field" $.CSRFToken}}
</form>
<button type="button" class="btn btn-sm btn-outline-danger" data-confirm-delete-form="del-cert-{{.ID}}" data-confirm-message="Delete certificate &quot;{{.Name}}&quot;? Proxy hosts using it will fall back to no TLS certificate."><i class="bi bi-trash"></i></button>
</td>
</tr>
{{else}}
<tr><td colspan="6" class="text-center text-secondary py-4">No certificates yet.</td></tr>
{{end}}
</tbody>
</table>
</div>
</div>
<div class="modal fade" id="issueLEModal" tabindex="-1">
<div class="modal-dialog"><div class="modal-content">
<form method="post" action="/certificates/letsencrypt">
{{template "csrf_field" .CSRFToken}}
<div class="modal-header"><h5 class="modal-title">Request Let's Encrypt Certificate</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
<div class="modal-body">
<div class="mb-3"><label class="form-label">Name</label><input class="form-control" name="name" required></div>
<div class="mb-3"><label class="form-label">Domains (one per line, wildcards need DNS-01)</label><textarea class="form-control" name="domains" rows="3" required></textarea></div>
<div class="mb-3"><label class="form-label">Contact Email (optional — falls back to Settings)</label><input class="form-control" name="email" type="email"></div>
<div class="mb-3">
<label class="form-label">Challenge Type</label>
<select class="form-select" name="challenge_type">
<option value="http-01">HTTP-01 (port 80 reachable from the internet)</option>
<option value="dns-01">DNS-01 (required for wildcards)</option>
</select>
</div>
<div class="mb-3"><label class="form-label">DNS Provider (DNS-01 only)</label><input class="form-control" name="dns_provider" placeholder="cloudflare"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Request Certificate</button>
</div>
</form>
</div></div>
</div>
<div class="modal fade" id="uploadCertModal" tabindex="-1">
<div class="modal-dialog"><div class="modal-content">
<form method="post" action="/certificates/custom">
{{template "csrf_field" .CSRFToken}}
<div class="modal-header"><h5 class="modal-title">Upload Custom Certificate</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
<div class="modal-body">
<div class="mb-3"><label class="form-label">Name</label><input class="form-control" name="name" required></div>
<div class="mb-3"><label class="form-label">Domains (one per line)</label><textarea class="form-control" name="domains" rows="2"></textarea></div>
<div class="mb-3"><label class="form-label">Certificate (PEM, full chain)</label><textarea class="form-control font-monospace" name="cert_pem" rows="5" required placeholder="-----BEGIN CERTIFICATE-----"></textarea></div>
<div class="mb-3"><label class="form-label">Private Key (PEM)</label><textarea class="form-control font-monospace" name="key_pem" rows="5" required placeholder="-----BEGIN PRIVATE KEY-----"></textarea></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Upload</button>
</div>
</form>
</div></div>
</div>
{{template "confirm_delete_modal" .}}
{{template "footer" .}}{{end}}