feat(web-ui): add logout (#5121)

This commit is contained in:
David Lane
2026-05-14 23:23:02 -04:00
committed by GitHub
parent 33bdb011e2
commit 0523cebdfe
6 changed files with 111 additions and 10 deletions
+1
View File
@@ -1755,6 +1755,7 @@ namespace confighttp {
server.resource["^/clients/?$"]["GET"] = page_handler("clients.html");
server.resource["^/config/?$"]["GET"] = page_handler("config.html");
server.resource["^/featured/?$"]["GET"] = page_handler("featured.html");
server.resource["^/logout/?$"]["GET"] = page_handler("logout.html", false);
server.resource["^/password/?$"]["GET"] = page_handler("password.html");
server.resource["^/pin/?$"]["GET"] = page_handler("pin.html");
server.resource["^/troubleshooting/?$"]["GET"] = page_handler("troubleshooting.html");
+61 -10
View File
@@ -41,21 +41,38 @@
{{ $t('navbar.configuration') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./password">
<Shield :size="18" class="icon"></Shield>
{{ $t('navbar.password') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./troubleshooting">
<Info :size="18" class="icon"></Info>
{{ $t('navbar.troubleshoot') }}
</a>
</li>
</ul>
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item">
<ThemeToggle/>
</li>
<li class="nav-item dropdown">
<button class="nav-link dropdown-toggle" type="button" id="navbarUserMenu"
data-bs-toggle="dropdown" aria-expanded="false" aria-label="User menu" title="User menu">
<CircleUserRound :size="18" class="icon"></CircleUserRound>
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarUserMenu">
<li>
<a class="dropdown-item d-flex align-items-center" href="./password">
<Shield :size="18" class="icon"></Shield>
{{ $t('navbar.password') }}
</a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" @click="logout">
<LogOut :size="18" class="icon"></LogOut>
{{ $t('navbar.logout') }}
</button>
</li>
</ul>
</li>
</ul>
</div>
</div>
@@ -65,7 +82,7 @@
</template>
<script>
import { Home, Lock, Layers, Star, Settings, Shield, Info } from 'lucide-vue-next'
import { CircleUserRound, Home, Info, Layers, Lock, LogOut, Settings, Shield, Star } from 'lucide-vue-next'
import ThemeToggle from './ThemeToggle.vue'
import Notification from './Notification.vue'
@@ -79,14 +96,48 @@ export default {
Star,
Settings,
Shield,
Info
Info,
CircleUserRound,
LogOut
},
created() {
console.log("Header mounted!")
},
mounted() {
let el = document.querySelector("a[href='" + document.location.pathname + "']");
if (el) el.classList.add("active")
const currentPath = globalThis.location.pathname.replace(/\/$/, '') || '/'
const links = document.querySelectorAll('.navbar-sunshine a[href]')
for (const link of links) {
const href = link.getAttribute('href')
if (!href || href === '#') {
continue
}
const linkPath = new URL(href, globalThis.location.href).pathname.replace(/\/$/, '') || '/'
if (linkPath !== currentPath) {
continue
}
link.classList.add('active')
}
},
methods: {
logout() {
const cacheBuster = Date.now().toString()
const logoutPageUrl = new URL('/logout', globalThis.location.href)
const request = new XMLHttpRequest()
const finish = () => {
globalThis.location.replace(logoutPageUrl.toString())
}
request.open('GET', '/', true, 'sunshine-logout', cacheBuster)
request.setRequestHeader('Cache-Control', 'no-store')
request.onload = finish
request.onerror = finish
request.ontimeout = finish
request.timeout = 5000
request.send()
}
}
}
</script>
+41
View File
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="auto">
<head>
<%- header %>
</head>
<body id="app" v-cloak>
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card">
<div class="card-body text-center">
<img src="/images/logo-sunshine-45.png" height="45" alt="Sunshine" class="mb-3">
<h1 class="h3 mb-3">{{ $t('logout.logged_out') }}</h1>
<p class="text-muted mb-4">{{ $t('logout.logged_out_desc') }}</p>
<a class="btn btn-primary" href="./">
<log-in :size="18" class="icon"></log-in>
{{ $t('logout.login') }}
</a>
</div>
</div>
</div>
</div>
</div>
</body>
<script type="module">
import { createApp } from 'vue'
import { initApp } from './init'
import { LogIn } from 'lucide-vue-next'
const app = createApp({
components: {
LogIn,
},
});
initApp(app);
</script>
</html>
@@ -802,6 +802,7 @@ body {
color: var(--navbar-text-muted) !important;
font-weight: 500;
padding: 0.5rem 1rem !important;
min-height: calc(1.5em + 1rem);
border-radius: var(--radius-md);
transition: all var(--transition-fast);
display: flex;
@@ -426,6 +426,7 @@
"configuration": "Configuration",
"featured": "Featured Apps",
"home": "Home",
"logout": "Log Out",
"password": "Change Password",
"pin": "PIN",
"theme_auto": "Auto",
@@ -446,6 +447,11 @@
"toggle_theme": "Theme",
"troubleshoot": "Troubleshooting"
},
"logout": {
"logged_out": "Logged Out",
"logged_out_desc": "You have successfully logged out.",
"login": "Log In"
},
"password": {
"confirm_password": "Confirm Password",
"current_creds": "Current Credentials",
+1
View File
@@ -70,6 +70,7 @@ export default defineConfig({
config: resolve(assetsSrcPath, 'config.html'),
featured: resolve(assetsSrcPath, 'featured.html'),
index: resolve(assetsSrcPath, 'index.html'),
logout: resolve(assetsSrcPath, 'logout.html'),
password: resolve(assetsSrcPath, 'password.html'),
pin: resolve(assetsSrcPath, 'pin.html'),
troubleshooting: resolve(assetsSrcPath, 'troubleshooting.html'),