Add display names for app users
This commit is contained in:
parent
6e7aa87de6
commit
b625c95c29
3 changed files with 92 additions and 35 deletions
|
|
@ -64,9 +64,9 @@ func renderShell(w http.ResponseWriter, active, readingPane string) {
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Mail-Graveyard</title>
|
<title>Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-16">
|
<link rel="stylesheet" href="/static/style.css?v=20260712-17">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260712-16" defer></script></head>
|
<script src="/static/app.js?v=20260712-17" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -385,6 +385,7 @@ func userSaveHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
username := strings.TrimSpace(r.FormValue("username"))
|
username := strings.TrimSpace(r.FormValue("username"))
|
||||||
|
displayName := strings.TrimSpace(r.FormValue("display_name"))
|
||||||
password := r.FormValue("password")
|
password := r.FormValue("password")
|
||||||
role := strings.TrimSpace(r.FormValue("role"))
|
role := strings.TrimSpace(r.FormValue("role"))
|
||||||
active := r.FormValue("active") == "1"
|
active := r.FormValue("active") == "1"
|
||||||
|
|
@ -413,7 +414,7 @@ func userSaveHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := SaveAppUser(username, hash, role, active); err != nil {
|
if err := SaveAppUser(username, displayName, hash, role, active); err != nil {
|
||||||
redirectUsers(w, r, username, err.Error())
|
redirectUsers(w, r, username, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -482,7 +483,7 @@ func userToggleActiveHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
redirectUsers(w, r, username, "Nur Admins duerfen Verwalter oder Admins sperren.")
|
redirectUsers(w, r, username, "Nur Admins duerfen Verwalter oder Admins sperren.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := SaveAppUser(username, "", target.Role, active); err != nil {
|
if err := SaveAppUser(username, target.DisplayName, "", target.Role, active); err != nil {
|
||||||
redirectUsers(w, r, username, err.Error())
|
redirectUsers(w, r, username, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -503,9 +504,9 @@ func renderAccountsPage(w http.ResponseWriter, accounts []Account, edit Account,
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Konten-Verwaltung - Mail-Graveyard</title>
|
<title>Konten-Verwaltung - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-16">
|
<link rel="stylesheet" href="/static/style.css?v=20260712-17">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260712-16" defer></script></head>
|
<script src="/static/app.js?v=20260712-17" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -548,9 +549,9 @@ func renderArchivesPage(w http.ResponseWriter, archives []string, accounts []Acc
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Archiv-mbox Verwaltung - Mail-Graveyard</title>
|
<title>Archiv-mbox Verwaltung - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-16">
|
<link rel="stylesheet" href="/static/style.css?v=20260712-17">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260712-16" defer></script></head>
|
<script src="/static/app.js?v=20260712-17" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -596,9 +597,9 @@ func renderUsersPage(w http.ResponseWriter, r *http.Request, users []AppUser, ed
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Benutzerverwaltung - Mail-Graveyard</title>
|
<title>Benutzerverwaltung - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-16">
|
<link rel="stylesheet" href="/static/style.css?v=20260712-17">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260712-16" defer></script></head>
|
<script src="/static/app.js?v=20260712-17" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -634,7 +635,7 @@ func renderUsersPage(w http.ResponseWriter, r *http.Request, users []AppUser, ed
|
||||||
fmt.Fprint(w, `</div><div class="account-form-panel"><div class="pane-head">Benutzer eintragen</div>`)
|
fmt.Fprint(w, `</div><div class="account-form-panel"><div class="pane-head">Benutzer eintragen</div>`)
|
||||||
renderUserForm(w, r, edit)
|
renderUserForm(w, r, edit)
|
||||||
fmt.Fprint(w, `</div></section></main><footer class="statusbar"><span>angemeldet als `)
|
fmt.Fprint(w, `</div></section></main><footer class="statusbar"><span>angemeldet als `)
|
||||||
fmt.Fprint(w, html.EscapeString(CurrentUser(r).Username+" / "+CurrentUser(r).Role))
|
fmt.Fprint(w, html.EscapeString(userDisplayLabel(CurrentUser(r))+" / "+CurrentUser(r).Role))
|
||||||
fmt.Fprint(w, `</span></footer></body></html>`)
|
fmt.Fprint(w, `</span></footer></body></html>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -666,9 +667,9 @@ func renderTransferPage(w http.ResponseWriter, mode transferMode, accounts []Acc
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>%s - Mail-Graveyard</title>
|
<title>%s - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-16">
|
<link rel="stylesheet" href="/static/style.css?v=20260712-17">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260712-16" defer></script></head>
|
<script src="/static/app.js?v=20260712-17" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -707,9 +708,9 @@ func renderSourceEmailBoxPage(w http.ResponseWriter, accounts []Account) {
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Quell-Postfach - Mail-Graveyard</title>
|
<title>Quell-Postfach - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-16">
|
<link rel="stylesheet" href="/static/style.css?v=20260712-17">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260712-16" defer></script></head>
|
<script src="/static/app.js?v=20260712-17" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -749,9 +750,9 @@ func renderTargetEmailBoxPage(w http.ResponseWriter, accounts []Account) {
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Ziel-Postfach - Mail-Graveyard</title>
|
<title>Ziel-Postfach - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-16">
|
<link rel="stylesheet" href="/static/style.css?v=20260712-17">
|
||||||
<script src="/static/htmx.min.js"></script>
|
<script src="/static/htmx.min.js"></script>
|
||||||
<script src="/static/app.js?v=20260712-16" defer></script></head>
|
<script src="/static/app.js?v=20260712-17" defer></script></head>
|
||||||
<body class="ol2013">
|
<body class="ol2013">
|
||||||
<header class="ribbon">
|
<header class="ribbon">
|
||||||
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
<button class="app-menu-button" type="button" aria-label="Mail-Graveyard-Menue" aria-expanded="false" data-backstage-toggle><span class="mail-logo" aria-hidden="true"></span></button>
|
||||||
|
|
@ -999,7 +1000,7 @@ func renderUsersTable(w http.ResponseWriter, users []AppUser, editName string) {
|
||||||
fmt.Fprint(w, `<div class="ef-empty">Noch keine Benutzer eingetragen.</div>`)
|
fmt.Fprint(w, `<div class="ef-empty">Noch keine Benutzer eingetragen.</div>`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, `<table class="account-table user-table"><thead><tr><th>Benutzer</th><th>Rolle</th><th>Status</th><th class="actions-col"></th></tr></thead><tbody>`)
|
fmt.Fprint(w, `<table class="account-table user-table"><thead><tr><th>Name</th><th>Login/E-Mail</th><th>Rolle</th><th>Status</th><th class="actions-col"></th></tr></thead><tbody>`)
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
activeClass := ""
|
activeClass := ""
|
||||||
if u.Username == editName {
|
if u.Username == editName {
|
||||||
|
|
@ -1020,8 +1021,8 @@ func renderUsersTable(w http.ResponseWriter, users []AppUser, editName string) {
|
||||||
toggleButton := fmt.Sprintf(`<form method="post" action="/users/toggle-active"><input type="hidden" name="username" value="%s"><input type="hidden" name="active" value="%s"><button class="%s" type="submit">%s</button></form>`,
|
toggleButton := fmt.Sprintf(`<form method="post" action="/users/toggle-active"><input type="hidden" name="username" value="%s"><input type="hidden" name="active" value="%s"><button class="%s" type="submit">%s</button></form>`,
|
||||||
html.EscapeString(u.Username), nextActive, toggleClass, toggleLabel)
|
html.EscapeString(u.Username), nextActive, toggleClass, toggleLabel)
|
||||||
deleteButton := fmt.Sprintf(`<form method="post" action="/users/delete"><input type="hidden" name="username" value="%s"><button class="btn compact secondary danger" type="submit">Loeschen</button></form>`, html.EscapeString(u.Username))
|
deleteButton := fmt.Sprintf(`<form method="post" action="/users/delete"><input type="hidden" name="username" value="%s"><button class="btn compact secondary danger" type="submit">Loeschen</button></form>`, html.EscapeString(u.Username))
|
||||||
fmt.Fprintf(w, `<tr%s><td>%s</td><td>%s</td><td>%s</td><td class="row-actions">%s%s%s</td></tr>`,
|
fmt.Fprintf(w, `<tr%s><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td class="row-actions">%s%s%s</td></tr>`,
|
||||||
activeClass, html.EscapeString(u.Username), html.EscapeString(u.Role), status, editButton, toggleButton, deleteButton)
|
activeClass, html.EscapeString(userDisplayName(u)), html.EscapeString(u.Username), html.EscapeString(u.Role), status, editButton, toggleButton, deleteButton)
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, `</tbody></table>`)
|
fmt.Fprint(w, `</tbody></table>`)
|
||||||
}
|
}
|
||||||
|
|
@ -1033,11 +1034,12 @@ func renderUserForm(w http.ResponseWriter, r *http.Request, u AppUser) {
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, `<form class="account-form user-form" method="post" action="/users/save">
|
fmt.Fprintf(w, `<form class="account-form user-form" method="post" action="/users/save">
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<label class="label">Benutzername<input class="input" name="username" value="%s" required></label>
|
<label class="label">Login/E-Mail<input class="input" name="username" value="%s" required></label>
|
||||||
<label class="check-row"><input type="checkbox" name="active" value="1" %s> Aktiv</label>
|
<label class="check-row"><input type="checkbox" name="active" value="1" %s> Aktiv</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-columns">
|
<div class="form-columns">
|
||||||
<fieldset><legend>Zugang</legend>
|
<fieldset><legend>Zugang</legend>
|
||||||
|
<label class="label">Anzeigename<input class="input" name="display_name" value="%s" placeholder="z.B. Max Mustermann"></label>
|
||||||
<div class="form-hint">%s</div>
|
<div class="form-hint">%s</div>
|
||||||
<label class="label">Passwort<input class="input" name="password" type="password" autocomplete="new-password" placeholder="%s"></label>
|
<label class="label">Passwort<input class="input" name="password" type="password" autocomplete="new-password" placeholder="%s"></label>
|
||||||
<label class="label">Rolle%s</label>
|
<label class="label">Rolle%s</label>
|
||||||
|
|
@ -1048,7 +1050,23 @@ func renderUserForm(w http.ResponseWriter, r *http.Request, u AppUser) {
|
||||||
<a class="btn secondary" href="/users">Neu</a>
|
<a class="btn secondary" href="/users">Neu</a>
|
||||||
</div>
|
</div>
|
||||||
</form>`,
|
</form>`,
|
||||||
html.EscapeString(u.Username), checked(u.Active), passwordHint(u.Username), passwordPlaceholder(u.Username), roleField)
|
html.EscapeString(u.Username), checked(u.Active), html.EscapeString(u.DisplayName), passwordHint(u.Username), passwordPlaceholder(u.Username), roleField)
|
||||||
|
}
|
||||||
|
|
||||||
|
func userDisplayName(u AppUser) string {
|
||||||
|
name := strings.TrimSpace(u.DisplayName)
|
||||||
|
if name != "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return u.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
func userDisplayLabel(u AppUser) string {
|
||||||
|
name := strings.TrimSpace(u.DisplayName)
|
||||||
|
if name != "" {
|
||||||
|
return name + " <" + u.Username + ">"
|
||||||
|
}
|
||||||
|
return u.Username
|
||||||
}
|
}
|
||||||
|
|
||||||
func roleSelect(current string, admin bool) string {
|
func roleSelect(current string, admin bool) string {
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ type Account struct {
|
||||||
type AppUser struct {
|
type AppUser struct {
|
||||||
ID int64
|
ID int64
|
||||||
Username string
|
Username string
|
||||||
|
DisplayName string
|
||||||
Role string
|
Role string
|
||||||
Active bool
|
Active bool
|
||||||
PasswordHash string
|
PasswordHash string
|
||||||
|
|
@ -119,6 +120,7 @@ func ConnectDB() error {
|
||||||
`CREATE TABLE IF NOT EXISTS app_users(
|
`CREATE TABLE IF NOT EXISTS app_users(
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
username TEXT NOT NULL UNIQUE,
|
username TEXT NOT NULL UNIQUE,
|
||||||
|
display_name TEXT NOT NULL DEFAULT '',
|
||||||
password_hash TEXT NOT NULL,
|
password_hash TEXT NOT NULL,
|
||||||
role TEXT NOT NULL DEFAULT 'user',
|
role TEXT NOT NULL DEFAULT 'user',
|
||||||
active INTEGER NOT NULL DEFAULT 1,
|
active INTEGER NOT NULL DEFAULT 1,
|
||||||
|
|
@ -139,6 +141,41 @@ func ConnectDB() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DB = db
|
DB = db
|
||||||
|
if err := ensureAppUserColumns(); err != nil {
|
||||||
|
_ = db.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureAppUserColumns() error {
|
||||||
|
hasDisplayName := false
|
||||||
|
rows, err := DB.Query(`PRAGMA table_info(app_users)`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var cid int
|
||||||
|
var name, typ string
|
||||||
|
var notNull int
|
||||||
|
var defaultValue any
|
||||||
|
var pk int
|
||||||
|
if err := rows.Scan(&cid, &name, &typ, ¬Null, &defaultValue, &pk); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if name == "display_name" {
|
||||||
|
hasDisplayName = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !hasDisplayName {
|
||||||
|
if _, err := DB.Exec(`ALTER TABLE app_users ADD COLUMN display_name TEXT NOT NULL DEFAULT ''`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +186,7 @@ func CountAppUsers() (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListAppUsers() ([]AppUser, error) {
|
func ListAppUsers() ([]AppUser, error) {
|
||||||
rows, err := DB.Query(`SELECT id, username, password_hash, role, active FROM app_users ORDER BY username`)
|
rows, err := DB.Query(`SELECT id, username, display_name, password_hash, role, active FROM app_users ORDER BY username`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -166,30 +203,32 @@ func ListAppUsers() ([]AppUser, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAppUser(username string) (AppUser, error) {
|
func GetAppUser(username string) (AppUser, error) {
|
||||||
row := DB.QueryRow(`SELECT id, username, password_hash, role, active FROM app_users WHERE username=?`, username)
|
row := DB.QueryRow(`SELECT id, username, display_name, password_hash, role, active FROM app_users WHERE username=?`, username)
|
||||||
return scanAppUser(row)
|
return scanAppUser(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAppUserByID(id int64) (AppUser, error) {
|
func GetAppUserByID(id int64) (AppUser, error) {
|
||||||
row := DB.QueryRow(`SELECT id, username, password_hash, role, active FROM app_users WHERE id=?`, id)
|
row := DB.QueryRow(`SELECT id, username, display_name, password_hash, role, active FROM app_users WHERE id=?`, id)
|
||||||
return scanAppUser(row)
|
return scanAppUser(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveAppUser(username, passwordHash, role string, active bool) error {
|
func SaveAppUser(username, displayName, passwordHash, role string, active bool) error {
|
||||||
|
displayName = strings.TrimSpace(displayName)
|
||||||
role = normalizeRole(role)
|
role = normalizeRole(role)
|
||||||
if passwordHash == "" {
|
if passwordHash == "" {
|
||||||
_, err := DB.Exec(`UPDATE app_users SET role=?, active=?, updated_at=CURRENT_TIMESTAMP WHERE username=?`,
|
_, err := DB.Exec(`UPDATE app_users SET display_name=?, role=?, active=?, updated_at=CURRENT_TIMESTAMP WHERE username=?`,
|
||||||
role, boolInt(active), username)
|
displayName, role, boolInt(active), username)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err := DB.Exec(`INSERT INTO app_users(username, password_hash, role, active)
|
_, err := DB.Exec(`INSERT INTO app_users(username, display_name, password_hash, role, active)
|
||||||
VALUES(?,?,?,?)
|
VALUES(?,?,?,?,?)
|
||||||
ON CONFLICT(username) DO UPDATE SET
|
ON CONFLICT(username) DO UPDATE SET
|
||||||
|
display_name=excluded.display_name,
|
||||||
password_hash=excluded.password_hash,
|
password_hash=excluded.password_hash,
|
||||||
role=excluded.role,
|
role=excluded.role,
|
||||||
active=excluded.active,
|
active=excluded.active,
|
||||||
updated_at=CURRENT_TIMESTAMP`,
|
updated_at=CURRENT_TIMESTAMP`,
|
||||||
username, passwordHash, role, boolInt(active))
|
username, displayName, passwordHash, role, boolInt(active))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -209,7 +248,7 @@ func DeleteSession(token string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionUser(token string) (AppUser, error) {
|
func SessionUser(token string) (AppUser, error) {
|
||||||
row := DB.QueryRow(`SELECT u.id, u.username, u.password_hash, u.role, u.active
|
row := DB.QueryRow(`SELECT u.id, u.username, u.display_name, u.password_hash, u.role, u.active
|
||||||
FROM app_sessions s JOIN app_users u ON u.id=s.user_id
|
FROM app_sessions s JOIN app_users u ON u.id=s.user_id
|
||||||
WHERE s.token=? AND s.expires_at > CURRENT_TIMESTAMP AND u.active=1`, token)
|
WHERE s.token=? AND s.expires_at > CURRENT_TIMESTAMP AND u.active=1`, token)
|
||||||
return scanAppUser(row)
|
return scanAppUser(row)
|
||||||
|
|
@ -351,7 +390,7 @@ func normalizeAccount(a *Account) {
|
||||||
func scanAppUser(s appUserScanner) (AppUser, error) {
|
func scanAppUser(s appUserScanner) (AppUser, error) {
|
||||||
var u AppUser
|
var u AppUser
|
||||||
var active int
|
var active int
|
||||||
err := s.Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role, &active)
|
err := s.Scan(&u.ID, &u.Username, &u.DisplayName, &u.PasswordHash, &u.Role, &active)
|
||||||
u.Role = normalizeRole(u.Role)
|
u.Role = normalizeRole(u.Role)
|
||||||
u.Active = active != 0
|
u.Active = active != 0
|
||||||
return u, err
|
return u, err
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ func InitAuth() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return SaveAppUser(strings.TrimSpace(Cfg.AdminUser), hash, roleAdmin, true)
|
return SaveAppUser(strings.TrimSpace(Cfg.AdminUser), "Admin", hash, roleAdmin, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthMiddleware schuetzt alle Routen ausser /login und /static.
|
// AuthMiddleware schuetzt alle Routen ausser /login und /static.
|
||||||
|
|
@ -169,7 +169,7 @@ func renderLoginPage(w http.ResponseWriter, errMsg string) {
|
||||||
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Login - Mail-Graveyard</title>
|
<title>Login - Mail-Graveyard</title>
|
||||||
<link rel="stylesheet" href="/static/style.css?v=20260712-16"></head>
|
<link rel="stylesheet" href="/static/style.css?v=20260712-17"></head>
|
||||||
<body class="ol2013 login-body">
|
<body class="ol2013 login-body">
|
||||||
<main class="login-card">
|
<main class="login-card">
|
||||||
<div class="login-brand"><span class="mail-logo" aria-hidden="true"></span><strong>Mail-Graveyard</strong></div>
|
<div class="login-brand"><span class="mail-logo" aria-hidden="true"></span><strong>Mail-Graveyard</strong></div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue