Add a landing login page and a logout option.

BasicAuth without an explicit landing page or a logout option has
sometimes been confusing to users. This commit adds a static
landing page on / with a login link and a logout option in the admin
that "logs out" BasicAuth session by posting invalid credentials to
the server to obtain a 401.
This commit is contained in:
Kailash Nadh 2021-09-26 23:42:57 +05:30
parent 9d2bc9c41d
commit 98ed4fb384
19 changed files with 73 additions and 21 deletions

View file

@ -51,8 +51,8 @@ func initHTTPHandlers(e *echo.Echo, app *App) {
// Admin JS app views.
// /admin/static/* file server is registered in initHTTPServer().
g.GET("/", func(c echo.Context) error {
return c.Redirect(http.StatusPermanentRedirect, path.Join(adminRoot, ""))
e.GET("/", func(c echo.Context) error {
return c.Render(http.StatusOK, "home", publicTpl{Title: "listmonk"})
})
g.GET(path.Join(adminRoot, ""), handleAdminPage)
g.GET(path.Join(adminRoot, "/*"), handleAdminPage)

View file

@ -10,7 +10,9 @@
</div>
</template>
<template slot="end">
<b-navbar-item tag="div"></b-navbar-item>
<b-navbar-item tag="div">
<a href="#" @click.prevent="doLogout">{{ $t('users.logout') }}</a>
</b-navbar-item>
</template>
</b-navbar>
@ -134,6 +136,7 @@
<script>
import Vue from 'vue';
import { mapState } from 'vuex';
import { uris } from './constants';
export default Vue.extend({
name: 'App',
@ -164,6 +167,21 @@ export default Vue.extend({
toggleGroup(group, state) {
this.activeGroup = state ? { [group]: true } : {};
},
doLogout() {
const http = new XMLHttpRequest();
const u = uris.root.substr(-1) === '/' ? uris.root : `${uris.root}/`;
http.open('get', `${u}api/logout`, false, 'logout_non_user', 'logout_non_user');
http.onload = () => {
document.location.href = uris.root;
};
http.onerror = () => {
document.location.href = uris.root;
};
http.send();
},
reloadApp() {
this.$api.reloadApp().then(() => {
this.$utils.toast('Reloading app ...');

View file

@ -6,7 +6,7 @@ import store from '../store';
import { models } from '../constants';
const http = axios.create({
baseURL: process.env.VUE_APP_API_URL || '/',
baseURL: process.env.VUE_APP_ROOT_URL || '/',
withCredentials: false,
responseType: 'json',
@ -276,3 +276,7 @@ export const getLogs = async () => http.get('/api/logs',
export const getLang = async (lang) => http.get(`/api/lang/${lang}`,
{ loading: models.lang, preserveCase: true });
export const logout = async () => http.get('/api/logout', {
auth: { username: 'wrong', password: 'wrong' },
});

View file

@ -939,10 +939,6 @@ section.analytics {
}
}
.navbar-burger {
display: none;
}
td .tags {
.tag:not(:last-child) {
margin-right: 0;

View file

@ -13,6 +13,7 @@ export const models = Object.freeze({
});
// Ad-hoc URIs that are used outside of vuex requests.
const rootURL = process.env.VUE_APP_ROOT_URL || '/';
const baseURL = process.env.BASE_URL.replace(/\/$/, '');
export const uris = Object.freeze({
@ -20,6 +21,8 @@ export const uris = Object.freeze({
previewTemplate: '/api/templates/:id/preview',
previewRawTemplate: '/api/templates/preview',
exportSubscribers: '/api/subscribers/export',
base: `${baseURL}/static`,
root: rootURL,
static: `${baseURL}/static`,
});

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Nová šablona",
"templates.placeholderHelp": "Zástupný symbol {placeholder} by se měl v šabloně objevit právě jednou.",
"templates.preview": "Náhled",
"templates.rawHTML": "Kód HTML"
"templates.rawHTML": "Kód HTML",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Neue Vorlage",
"templates.placeholderHelp": "Der Platzhalter \"{placeholder}\" darf nur einmal im Template vorkommen.",
"templates.preview": "Vorschau",
"templates.rawHTML": "HTML"
"templates.rawHTML": "HTML",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "New template",
"templates.placeholderHelp": "The placeholder {placeholder} should appear exactly once in the template.",
"templates.preview": "Preview",
"templates.rawHTML": "Raw HTML"
"templates.rawHTML": "Raw HTML",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Nueva plantilla",
"templates.placeholderHelp": "El marcador {placeholder} debe aparecer exactamente una vez en la plantilla.",
"templates.preview": "Vista pewliminar",
"templates.rawHTML": "HTML crudo"
"templates.rawHTML": "HTML crudo",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Nouveau modèle",
"templates.placeholderHelp": "L'espace réservé {placeholder} doit apparaître exactement une fois dans le modèle.",
"templates.preview": "Aperçu",
"templates.rawHTML": "HTML brut"
"templates.rawHTML": "HTML brut",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Nuovo modello",
"templates.placeholderHelp": "Il segnaposto {placeholder} deve apparire esattamente una volta nel modello.",
"templates.preview": "Anteprima",
"templates.rawHTML": "HTML semplice"
"templates.rawHTML": "HTML semplice",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "പുതിയ ടെംപ്ലേറ്റ്",
"templates.placeholderHelp": "{placeholder} എന്ന പ്ലെയ്‌സ്‌ഹോൾഡർ ടെംപ്ലേറ്റിൽ ഒരിക്കലെങ്കിലും വരണം.",
"templates.preview": "പ്രിവ്യൂ",
"templates.rawHTML": "എച്. ടീ. എം. എൽ"
"templates.rawHTML": "എച്. ടീ. എം. എൽ",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Nowy szablon",
"templates.placeholderHelp": "Symbol zastępczy {placeholder} powinien występować dokładnie raz w szablonie.",
"templates.preview": "Podgląd",
"templates.rawHTML": "Surowy HTML"
"templates.rawHTML": "Surowy HTML",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Novo modelo",
"templates.placeholderHelp": "O palavra reservada {placeholder} deve aparecer exatamente uma vez no modelo.",
"templates.preview": "Pré-visualizar",
"templates.rawHTML": "Código HTML"
"templates.rawHTML": "Código HTML",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Novo template",
"templates.placeholderHelp": "O placeholder {placeholder} deve aparecer exatamente uma vez no template.",
"templates.preview": "Pré-visualização",
"templates.rawHTML": "HTML Simples"
"templates.rawHTML": "HTML Simples",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Template nou",
"templates.placeholderHelp": "Substituentul {placeholder} ar trebui să apară exact o dată în șablon.",
"templates.preview": "Previzualizare",
"templates.rawHTML": "HTML brut"
"templates.rawHTML": "HTML brut",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Новый шаблон",
"templates.placeholderHelp": "Заполнитель {placeholder} должен присутствовать в шаблоне в одном экземпляре.",
"templates.preview": "Предпросмотр",
"templates.rawHTML": "Необработанный HTML"
"templates.rawHTML": "Необработанный HTML",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -482,5 +482,7 @@
"templates.newTemplate": "Yeni taslak",
"templates.placeholderHelp": "Yer tutucu {placeholder} taslak içinde sadece bir kere olmalıdır.",
"templates.preview": "Önizleme",
"templates.rawHTML": "Ham HTML"
"templates.rawHTML": "Ham HTML",
"users.login": "Login",
"users.logout": "Logout"
}

View file

@ -46,6 +46,9 @@ input[type="text"], input[type="email"], select {
border-color: #0055d4;
}
.center {
text-align: center;
}
.button {
background: #0055d4;
padding: 15px 30px;
@ -57,9 +60,11 @@ input[type="text"], input[type="email"], select {
display: inline-block;
min-width: 150px;
font-size: 1.1em;
text-align: center;
}
.button:hover {
background: #333;
color: #fff;
}
.button.button-outline {
background: #fff;