feat: geo-restriction via Cloudflare WAF custom rules
Add country-based access control managed through the Settings page.
Global allow-list with per-app overrides, searchable country selector,
automatic sync to Cloudflare WAF on settings change / deploy / remove,
plus periodic 6-hour verification.
New package: internal/cloudflare/ (client, zone, waf, countries, geosync)
New API: /api/geo/* (6 endpoints) + /api/stacks/{name}/geo/override
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3009,3 +3009,50 @@ a.stat-card:hover {
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
@keyframes debug-spin { to { transform: rotate(360deg); } }
|
||||
|
||||
/* --- Geo-restriction UI --- */
|
||||
.geo-country-selector { position: relative; }
|
||||
.geo-selected-tags { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.5rem; }
|
||||
.geo-tag {
|
||||
display: inline-flex; align-items: center; gap: 0.3rem;
|
||||
background: rgba(0,136,204,0.15); color: var(--accent-light);
|
||||
padding: 0.25rem 0.55rem; border-radius: 4px; font-size: 0.85rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.geo-tag-hu { background: rgba(35,134,54,0.2); color: var(--green); }
|
||||
.geo-tag-sm { font-size: 0.75rem; padding: 0.15rem 0.4rem; }
|
||||
.geo-tag-remove { cursor: pointer; opacity: 0.7; font-size: 1.1em; line-height: 1; }
|
||||
.geo-tag-remove:hover { opacity: 1; }
|
||||
.geo-country-list {
|
||||
position: absolute; z-index: 10; background: var(--bg-card);
|
||||
border: 1px solid var(--border-color); border-radius: 6px;
|
||||
max-height: 220px; overflow-y: auto; width: 100%; margin-top: 2px;
|
||||
display: none;
|
||||
}
|
||||
.geo-country-option {
|
||||
padding: 0.4rem 0.75rem; cursor: pointer; font-size: 0.9rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.geo-country-option:hover { background: rgba(0,136,204,0.1); }
|
||||
.geo-country-option small { color: var(--text-muted); }
|
||||
.geo-app-override-row {
|
||||
display: flex; align-items: center; gap: 0.5rem;
|
||||
padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.geo-app-override-row strong { min-width: 120px; color: var(--text-primary); }
|
||||
.geo-edit-overlay {
|
||||
background: var(--bg-secondary); border: 1px solid var(--border-color);
|
||||
border-radius: 8px; padding: 1rem; margin-top: 0.5rem;
|
||||
}
|
||||
.geo-edit-grid {
|
||||
display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 0.2rem; max-height: 250px; overflow-y: auto; margin-top: 0.5rem;
|
||||
}
|
||||
.geo-edit-item { font-size: 0.85rem; cursor: pointer; padding: 0.15rem 0; color: var(--text-primary); }
|
||||
.geo-edit-item input { margin-right: 0.3rem; }
|
||||
.btn-danger-outline {
|
||||
background: transparent; color: var(--red); border: 1px solid var(--red);
|
||||
border-radius: 6px; padding: 0.2rem 0.5rem; cursor: pointer; font-size: 0.8rem;
|
||||
}
|
||||
.btn-danger-outline:hover { background: var(--red-bg); }
|
||||
|
||||
Reference in New Issue
Block a user