Auth-related stuffs
This commit is contained in:
parent
ea1ae0d078
commit
6d2df119d8
143
auth/auth.qtpl
143
auth/auth.qtpl
@ -1,143 +0,0 @@
|
|||||||
{% import "net/http" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/internal/cfg" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
|
|
||||||
|
|
||||||
{% func Register(rq *http.Request) %}
|
|
||||||
{% code
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
%}
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
{% if cfg.AllowRegistration %}
|
|
||||||
<form class="modal" method="post" action="/register?{%s rq.URL.RawQuery %}" id="register-form" enctype="multipart/form-data" autocomplete="off">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title">{%s lc.Get("auth.register_header", &l18n.Replacements{"name": cfg.WikiName}) %}</legend>
|
|
||||||
|
|
||||||
<label for="register-form__username">{%s lc.Get("auth.username") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">{%s lc.Get("auth.password") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password">
|
|
||||||
<p>{%s lc.Get("auth.password_tip") %}</p>
|
|
||||||
<p>{%s lc.Get("auth.cookie_tip") %}</p>
|
|
||||||
<button class="btn" type="submit" value="Register">{%s lc.Get("auth.register_button") %}</button>
|
|
||||||
<a class="btn btn_weak" href="/{%s rq.URL.RawQuery %}">{%s lc.Get("ui.cancel") %}</a>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{%= telegramWidget(lc) %}
|
|
||||||
{% elseif cfg.UseAuth %}
|
|
||||||
<p>{%s lc.Get("auth.noregister") %}</p>
|
|
||||||
<p><a href="/{%s rq.URL.RawQuery %}">← {%s lc.Get("auth.go_back") %}</a></p>
|
|
||||||
{% else %}
|
|
||||||
<p>{%s lc.Get("auth.noauth") %}</p>
|
|
||||||
<p><a href="/{%s rq.URL.RawQuery %}">← {%s lc.Get("auth.go_back") %}</a></p>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func Login(lc *l18n.Localizer) %}
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
{% if cfg.UseAuth %}
|
|
||||||
<form class="modal" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title">{%s lc.Get("auth.login_header", &l18n.Replacements{"name": cfg.WikiName}) %}</legend>
|
|
||||||
<label for="login-form__username">{%s lc.Get("auth.username") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">{%s lc.Get("auth.password") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
|
||||||
<p>{%s lc.Get("auth.cookie_tip") %}</p>
|
|
||||||
<button class="btn" type="submit" value="Log in">{%s lc.Get("auth.login_button") %}</button>
|
|
||||||
<a class="btn btn_weak" href="/">{%s lc.Get("ui.cancel") %}</a>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{%= telegramWidget(lc) %}
|
|
||||||
{% else %}
|
|
||||||
<p>{%s lc.Get("auth.noauth") %}</p>
|
|
||||||
<p><a class="btn btn_weak" href="/">← {%s lc.Get("auth.go_home") %}</a></p>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
Telegram auth widget was requested by Yogurt. As you can see, we don't offer user administrators control over it. Of course we don't.
|
|
||||||
{% func telegramWidget(lc *l18n.Localizer) %}
|
|
||||||
{% if cfg.TelegramEnabled %}
|
|
||||||
<p class="telegram-notice">{%s lc.Get("auth.telegram_tip") %}</p>
|
|
||||||
<script async src="https://telegram.org/js/telegram-widget.js?15" data-telegram-login="{%s cfg.TelegramBotName %}" data-size="medium" data-userpic="false" data-auth-url="{%s cfg.URL %}/telegram-login"></script>
|
|
||||||
{% endif %}
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func LoginError(err string, lc *l18n.Localizer) %}
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
{% switch err %}
|
|
||||||
{% case "unknown username" %}
|
|
||||||
<p class="error">{%s lc.Get("auth.error_username") %}</p>
|
|
||||||
{% case "wrong password" %}
|
|
||||||
<p class="error">{%s lc.Get("auth.error_password") %}</p>
|
|
||||||
{% default %}
|
|
||||||
<p class="error">{%s err %}</p>
|
|
||||||
{% endswitch %}
|
|
||||||
<p><a href="/login">← {%s lc.Get("auth.try_again") %}</a></p>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func Logout(can bool, lc *l18n.Localizer) %}
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
{% if can %}
|
|
||||||
<h1>{%s lc.Get("auth.logout_header") %}</h1>
|
|
||||||
<form method="POST" action="/logout">
|
|
||||||
<input class="btn btn_accent" type="submit" value="{%s lc.Get("auth.logout_button") %}"/>
|
|
||||||
<a class="btn btn_weak" href="/">{%s lc.Get("auth.go_home") %}</a>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<p>{%s lc.Get("auth.logout_anon") %}</p>
|
|
||||||
<p><a href="/login">{%s lc.Get("auth.login_title") %}</a></p>
|
|
||||||
<p><a href="/">← {%s lc.Get("auth.go_home") %}</a></p>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func Lock(lc *l18n.Localizer) %}
|
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>🔒 {%s lc.Get("auth.lock_title") %}</title>
|
|
||||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main class="locked-notice main-width">
|
|
||||||
<section class="locked-notice__message">
|
|
||||||
<p class="locked-notice__lock">🔒</p>
|
|
||||||
<h1 class="locked-notice__title">{%s lc.Get("auth.lock_title") %}</h1>
|
|
||||||
<form class="locked-notice__login-form" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
|
||||||
<label for="login-form__username">{%s lc.Get("auth.username") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">{%s lc.Get("auth.password") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
|
||||||
<br>
|
|
||||||
<button class="btn" type="submit" value="Log in">{%s lc.Get("auth.login_button") %}</button>
|
|
||||||
</form>
|
|
||||||
{%= telegramWidget(lc) %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
@ -1,597 +0,0 @@
|
|||||||
// Code generated by qtc from "auth.qtpl". DO NOT EDIT.
|
|
||||||
// See https://github.com/valyala/quicktemplate for details.
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:1
|
|
||||||
package auth
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:1
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:2
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/internal/cfg"
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:3
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/l18n"
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:5
|
|
||||||
import (
|
|
||||||
qtio422016 "io"
|
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
|
||||||
)
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:5
|
|
||||||
var (
|
|
||||||
_ = qtio422016.Copy
|
|
||||||
_ = qt422016.AcquireByteBuffer
|
|
||||||
)
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:5
|
|
||||||
func StreamRegister(qw422016 *qt422016.Writer, rq *http.Request) {
|
|
||||||
//line auth/auth.qtpl:5
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:7
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:8
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:11
|
|
||||||
if cfg.AllowRegistration {
|
|
||||||
//line auth/auth.qtpl:11
|
|
||||||
qw422016.N().S(`
|
|
||||||
<form class="modal" method="post" action="/register?`)
|
|
||||||
//line auth/auth.qtpl:12
|
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
|
||||||
//line auth/auth.qtpl:12
|
|
||||||
qw422016.N().S(`" id="register-form" enctype="multipart/form-data" autocomplete="off">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title">`)
|
|
||||||
//line auth/auth.qtpl:14
|
|
||||||
qw422016.E().S(lc.Get("auth.register_header", &l18n.Replacements{"name": cfg.WikiName}))
|
|
||||||
//line auth/auth.qtpl:14
|
|
||||||
qw422016.N().S(`</legend>
|
|
||||||
|
|
||||||
<label for="register-form__username">`)
|
|
||||||
//line auth/auth.qtpl:16
|
|
||||||
qw422016.E().S(lc.Get("auth.username"))
|
|
||||||
//line auth/auth.qtpl:16
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">`)
|
|
||||||
//line auth/auth.qtpl:20
|
|
||||||
qw422016.E().S(lc.Get("auth.password"))
|
|
||||||
//line auth/auth.qtpl:20
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password">
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:23
|
|
||||||
qw422016.E().S(lc.Get("auth.password_tip"))
|
|
||||||
//line auth/auth.qtpl:23
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:24
|
|
||||||
qw422016.E().S(lc.Get("auth.cookie_tip"))
|
|
||||||
//line auth/auth.qtpl:24
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<button class="btn" type="submit" value="Register">`)
|
|
||||||
//line auth/auth.qtpl:25
|
|
||||||
qw422016.E().S(lc.Get("auth.register_button"))
|
|
||||||
//line auth/auth.qtpl:25
|
|
||||||
qw422016.N().S(`</button>
|
|
||||||
<a class="btn btn_weak" href="/`)
|
|
||||||
//line auth/auth.qtpl:26
|
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
|
||||||
//line auth/auth.qtpl:26
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line auth/auth.qtpl:26
|
|
||||||
qw422016.E().S(lc.Get("ui.cancel"))
|
|
||||||
//line auth/auth.qtpl:26
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:29
|
|
||||||
streamtelegramWidget(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:29
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:30
|
|
||||||
} else if cfg.UseAuth {
|
|
||||||
//line auth/auth.qtpl:30
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:31
|
|
||||||
qw422016.E().S(lc.Get("auth.noregister"))
|
|
||||||
//line auth/auth.qtpl:31
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p><a href="/`)
|
|
||||||
//line auth/auth.qtpl:32
|
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
|
||||||
//line auth/auth.qtpl:32
|
|
||||||
qw422016.N().S(`">← `)
|
|
||||||
//line auth/auth.qtpl:32
|
|
||||||
qw422016.E().S(lc.Get("auth.go_back"))
|
|
||||||
//line auth/auth.qtpl:32
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:33
|
|
||||||
} else {
|
|
||||||
//line auth/auth.qtpl:33
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:34
|
|
||||||
qw422016.E().S(lc.Get("auth.noauth"))
|
|
||||||
//line auth/auth.qtpl:34
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p><a href="/`)
|
|
||||||
//line auth/auth.qtpl:35
|
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
|
||||||
//line auth/auth.qtpl:35
|
|
||||||
qw422016.N().S(`">← `)
|
|
||||||
//line auth/auth.qtpl:35
|
|
||||||
qw422016.E().S(lc.Get("auth.go_back"))
|
|
||||||
//line auth/auth.qtpl:35
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:36
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:36
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
func WriteRegister(qq422016 qtio422016.Writer, rq *http.Request) {
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
StreamRegister(qw422016, rq)
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
func Register(rq *http.Request) string {
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
WriteRegister(qb422016, rq)
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:39
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
func StreamLogin(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:44
|
|
||||||
if cfg.UseAuth {
|
|
||||||
//line auth/auth.qtpl:44
|
|
||||||
qw422016.N().S(`
|
|
||||||
<form class="modal" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title">`)
|
|
||||||
//line auth/auth.qtpl:47
|
|
||||||
qw422016.E().S(lc.Get("auth.login_header", &l18n.Replacements{"name": cfg.WikiName}))
|
|
||||||
//line auth/auth.qtpl:47
|
|
||||||
qw422016.N().S(`</legend>
|
|
||||||
<label for="login-form__username">`)
|
|
||||||
//line auth/auth.qtpl:48
|
|
||||||
qw422016.E().S(lc.Get("auth.username"))
|
|
||||||
//line auth/auth.qtpl:48
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">`)
|
|
||||||
//line auth/auth.qtpl:52
|
|
||||||
qw422016.E().S(lc.Get("auth.password"))
|
|
||||||
//line auth/auth.qtpl:52
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:55
|
|
||||||
qw422016.E().S(lc.Get("auth.cookie_tip"))
|
|
||||||
//line auth/auth.qtpl:55
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<button class="btn" type="submit" value="Log in">`)
|
|
||||||
//line auth/auth.qtpl:56
|
|
||||||
qw422016.E().S(lc.Get("auth.login_button"))
|
|
||||||
//line auth/auth.qtpl:56
|
|
||||||
qw422016.N().S(`</button>
|
|
||||||
<a class="btn btn_weak" href="/">`)
|
|
||||||
//line auth/auth.qtpl:57
|
|
||||||
qw422016.E().S(lc.Get("ui.cancel"))
|
|
||||||
//line auth/auth.qtpl:57
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:60
|
|
||||||
streamtelegramWidget(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:60
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:61
|
|
||||||
} else {
|
|
||||||
//line auth/auth.qtpl:61
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:62
|
|
||||||
qw422016.E().S(lc.Get("auth.noauth"))
|
|
||||||
//line auth/auth.qtpl:62
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p><a class="btn btn_weak" href="/">← `)
|
|
||||||
//line auth/auth.qtpl:63
|
|
||||||
qw422016.E().S(lc.Get("auth.go_home"))
|
|
||||||
//line auth/auth.qtpl:63
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:64
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:64
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
func WriteLogin(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
StreamLogin(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
func Login(lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
WriteLogin(qb422016, lc)
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:67
|
|
||||||
}
|
|
||||||
|
|
||||||
// Telegram auth widget was requested by Yogurt. As you can see, we don't offer user administrators control over it. Of course we don't.
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:70
|
|
||||||
func streamtelegramWidget(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:70
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:71
|
|
||||||
if cfg.TelegramEnabled {
|
|
||||||
//line auth/auth.qtpl:71
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="telegram-notice">`)
|
|
||||||
//line auth/auth.qtpl:72
|
|
||||||
qw422016.E().S(lc.Get("auth.telegram_tip"))
|
|
||||||
//line auth/auth.qtpl:72
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<script async src="https://telegram.org/js/telegram-widget.js?15" data-telegram-login="`)
|
|
||||||
//line auth/auth.qtpl:73
|
|
||||||
qw422016.E().S(cfg.TelegramBotName)
|
|
||||||
//line auth/auth.qtpl:73
|
|
||||||
qw422016.N().S(`" data-size="medium" data-userpic="false" data-auth-url="`)
|
|
||||||
//line auth/auth.qtpl:73
|
|
||||||
qw422016.E().S(cfg.URL)
|
|
||||||
//line auth/auth.qtpl:73
|
|
||||||
qw422016.N().S(`/telegram-login"></script>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:74
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:74
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
func writetelegramWidget(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
streamtelegramWidget(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
func telegramWidget(lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
writetelegramWidget(qb422016, lc)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
func StreamLoginError(qw422016 *qt422016.Writer, err string, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:80
|
|
||||||
switch err {
|
|
||||||
//line auth/auth.qtpl:81
|
|
||||||
case "unknown username":
|
|
||||||
//line auth/auth.qtpl:81
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="error">`)
|
|
||||||
//line auth/auth.qtpl:82
|
|
||||||
qw422016.E().S(lc.Get("auth.error_username"))
|
|
||||||
//line auth/auth.qtpl:82
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:83
|
|
||||||
case "wrong password":
|
|
||||||
//line auth/auth.qtpl:83
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="error">`)
|
|
||||||
//line auth/auth.qtpl:84
|
|
||||||
qw422016.E().S(lc.Get("auth.error_password"))
|
|
||||||
//line auth/auth.qtpl:84
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:85
|
|
||||||
default:
|
|
||||||
//line auth/auth.qtpl:85
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="error">`)
|
|
||||||
//line auth/auth.qtpl:86
|
|
||||||
qw422016.E().S(err)
|
|
||||||
//line auth/auth.qtpl:86
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:87
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:87
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p><a href="/login">← `)
|
|
||||||
//line auth/auth.qtpl:88
|
|
||||||
qw422016.E().S(lc.Get("auth.try_again"))
|
|
||||||
//line auth/auth.qtpl:88
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
func WriteLoginError(qq422016 qtio422016.Writer, err string, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
StreamLoginError(qw422016, err, lc)
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
func LoginError(err string, lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
WriteLoginError(qb422016, err, lc)
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:91
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
func StreamLogout(qw422016 *qt422016.Writer, can bool, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:96
|
|
||||||
if can {
|
|
||||||
//line auth/auth.qtpl:96
|
|
||||||
qw422016.N().S(`
|
|
||||||
<h1>`)
|
|
||||||
//line auth/auth.qtpl:97
|
|
||||||
qw422016.E().S(lc.Get("auth.logout_header"))
|
|
||||||
//line auth/auth.qtpl:97
|
|
||||||
qw422016.N().S(`</h1>
|
|
||||||
<form method="POST" action="/logout">
|
|
||||||
<input class="btn btn_accent" type="submit" value="`)
|
|
||||||
//line auth/auth.qtpl:99
|
|
||||||
qw422016.E().S(lc.Get("auth.logout_button"))
|
|
||||||
//line auth/auth.qtpl:99
|
|
||||||
qw422016.N().S(`"/>
|
|
||||||
<a class="btn btn_weak" href="/">`)
|
|
||||||
//line auth/auth.qtpl:100
|
|
||||||
qw422016.E().S(lc.Get("auth.go_home"))
|
|
||||||
//line auth/auth.qtpl:100
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:102
|
|
||||||
} else {
|
|
||||||
//line auth/auth.qtpl:102
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:103
|
|
||||||
qw422016.E().S(lc.Get("auth.logout_anon"))
|
|
||||||
//line auth/auth.qtpl:103
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p><a href="/login">`)
|
|
||||||
//line auth/auth.qtpl:104
|
|
||||||
qw422016.E().S(lc.Get("auth.login_title"))
|
|
||||||
//line auth/auth.qtpl:104
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
<p><a href="/">← `)
|
|
||||||
//line auth/auth.qtpl:105
|
|
||||||
qw422016.E().S(lc.Get("auth.go_home"))
|
|
||||||
//line auth/auth.qtpl:105
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:106
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:106
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
func WriteLogout(qq422016 qtio422016.Writer, can bool, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
StreamLogout(qw422016, can, lc)
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
func Logout(can bool, lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
WriteLogout(qb422016, can, lc)
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:109
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
func StreamLock(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
qw422016.N().S(`
|
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>🔒 `)
|
|
||||||
//line auth/auth.qtpl:117
|
|
||||||
qw422016.E().S(lc.Get("auth.lock_title"))
|
|
||||||
//line auth/auth.qtpl:117
|
|
||||||
qw422016.N().S(`</title>
|
|
||||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main class="locked-notice main-width">
|
|
||||||
<section class="locked-notice__message">
|
|
||||||
<p class="locked-notice__lock">🔒</p>
|
|
||||||
<h1 class="locked-notice__title">`)
|
|
||||||
//line auth/auth.qtpl:125
|
|
||||||
qw422016.E().S(lc.Get("auth.lock_title"))
|
|
||||||
//line auth/auth.qtpl:125
|
|
||||||
qw422016.N().S(`</h1>
|
|
||||||
<form class="locked-notice__login-form" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
|
||||||
<label for="login-form__username">`)
|
|
||||||
//line auth/auth.qtpl:127
|
|
||||||
qw422016.E().S(lc.Get("auth.username"))
|
|
||||||
//line auth/auth.qtpl:127
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">`)
|
|
||||||
//line auth/auth.qtpl:131
|
|
||||||
qw422016.E().S(lc.Get("auth.password"))
|
|
||||||
//line auth/auth.qtpl:131
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
|
||||||
<br>
|
|
||||||
<button class="btn" type="submit" value="Log in">`)
|
|
||||||
//line auth/auth.qtpl:135
|
|
||||||
qw422016.E().S(lc.Get("auth.login_button"))
|
|
||||||
//line auth/auth.qtpl:135
|
|
||||||
qw422016.N().S(`</button>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:137
|
|
||||||
streamtelegramWidget(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:137
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
func WriteLock(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
StreamLock(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
func Lock(lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
WriteLock(qb422016, lc)
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:142
|
|
||||||
}
|
|
||||||
@ -79,6 +79,11 @@ func Register(username, password, group, source string, force bool) error {
|
|||||||
return SaveUserDatabase()
|
return SaveUserDatabase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnknownUsername = errors.New("unknown username")
|
||||||
|
ErrWrongPassword = errors.New("wrong password")
|
||||||
|
)
|
||||||
|
|
||||||
// LoginDataHTTP logs such user in and returns string representation of an error if there is any.
|
// LoginDataHTTP logs such user in and returns string representation of an error if there is any.
|
||||||
//
|
//
|
||||||
// The HTTP parameters are used for setting header status (bad request, if it is bad) and saving a cookie.
|
// The HTTP parameters are used for setting header status (bad request, if it is bad) and saving a cookie.
|
||||||
@ -87,12 +92,12 @@ func LoginDataHTTP(w http.ResponseWriter, username, password string) error {
|
|||||||
if !HasUsername(username) {
|
if !HasUsername(username) {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
log.Println("Unknown username", username, "was entered")
|
log.Println("Unknown username", username, "was entered")
|
||||||
return errors.New("unknown username")
|
return ErrUnknownUsername
|
||||||
}
|
}
|
||||||
if !CredentialsOK(username, password) {
|
if !CredentialsOK(username, password) {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
log.Println("A wrong password was entered for username", username)
|
log.Println("A wrong password was entered for username", username)
|
||||||
return errors.New("wrong password")
|
return ErrWrongPassword
|
||||||
}
|
}
|
||||||
token, err := AddSession(username)
|
token, err := AddSession(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -3,33 +3,29 @@
|
|||||||
"password": "Password",
|
"password": "Password",
|
||||||
|
|
||||||
"register_title": "Register",
|
"register_title": "Register",
|
||||||
"register_header": "Register on {{.name}}",
|
"register_header": "",
|
||||||
"register_button": "Register",
|
"register_button": "Register",
|
||||||
|
|
||||||
"login_title": "Login",
|
|
||||||
"login_header": "Log in to {{.name}}",
|
|
||||||
"login_button": "Log in",
|
|
||||||
|
|
||||||
"logout_title": "Logout?",
|
"logout_title": "",
|
||||||
"logout_header": "Log out?",
|
"logout_header": "Log out?",
|
||||||
"logout_button": "Confirm",
|
"logout_button": "Confirm",
|
||||||
"logout_anon": "You cannot log out because you are not logged in.",
|
"logout_anon": "",
|
||||||
|
|
||||||
"lock_title": "Locked",
|
"lock_title": "Locked",
|
||||||
|
|
||||||
"password_tip": "The server stores your password in an encrypted form; even administrators cannot read it.",
|
"password_tip": "",
|
||||||
"cookie_tip": "By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you. You will stay logged in until you log out.",
|
"cookie_tip": "",
|
||||||
"telegram_tip": "You can log in using Telegram. It only works if you have set your @username in Telegram and this username is free on this wiki.",
|
"telegram_tip": "",
|
||||||
|
|
||||||
"noauth": "Authentication is disabled. You can make edits anonymously.",
|
"noauth": "",
|
||||||
"noregister": "Registrations are currently closed. Administrators can make an account for you by hand; contact them.",
|
"noregister": "Registrations are currently closed. Administrators can make an account for you by hand; contact them.",
|
||||||
|
|
||||||
"error_username": "Unknown username.",
|
"error_username": "",
|
||||||
"error_password": "Wrong password.",
|
"error_password": "",
|
||||||
"error_telegram": "Could not authorize using Telegram.",
|
"error_telegram": "",
|
||||||
|
|
||||||
"go_back": "Go back",
|
"go_back": "Go back",
|
||||||
"go_home": "Go home",
|
"go_home": "",
|
||||||
"go_login": "Go to the login page",
|
"go_login": "Go to the login page",
|
||||||
"try_again": "Try again"
|
"try_again": "Try again"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,31 +2,31 @@
|
|||||||
"username": "Логин",
|
"username": "Логин",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
|
|
||||||
"register_title": "Регистрация",
|
"register_title": "",
|
||||||
"register_header": "Регистрация на «{{.name}}»",
|
"register_header": "",
|
||||||
"register_button": "Зарегистрироваться",
|
"register_button": "",
|
||||||
|
|
||||||
"login_title": "Вход",
|
"login_title": "Вход",
|
||||||
"login_header": "Вход в «{{.name}}»",
|
"login_header": "",
|
||||||
"login_button": "Войти",
|
"login_button": "",
|
||||||
|
|
||||||
"logout_title": "Выйти?",
|
"logout_title": "",
|
||||||
"logout_header": "Выйти?",
|
"logout_header": "?",
|
||||||
"logout_button": "Подтвердить",
|
"logout_button": "Подтвердить",
|
||||||
"logout_anon": "Вы не можете выйти, потому что ещё не вошли.",
|
"logout_anon": "",
|
||||||
|
|
||||||
"lock_title": "Доступ закрыт",
|
"lock_title": "",
|
||||||
|
|
||||||
"password_tip": "Сервер хранит ваш пароль в зашифрованном виде, даже администраторы не смогут его прочесть.",
|
"password_tip": "",
|
||||||
"cookie_tip": "Отправляя эту форму, вы разрешаете вики хранить cookie в вашем браузере. Это позволит движку связывать ваши правки с вашей учётной записью. Вы будете авторизованы, пока не выйдете из учётной записи.",
|
"cookie_tip": "",
|
||||||
"telegram_tip": "Вы можете войти с помощью Телеграм. Это сработает, если у вашего профиля есть @имя, и оно не занято в этой вики.",
|
"telegram_tip": "Вы можете войти с помощью Телеграм. Это сработает, если у вашего профиля есть @имя, и оно не занято в этой вики.",
|
||||||
|
|
||||||
"noauth": "Аутентификация отключена. Вы можете делать правки анонимно.",
|
"noauth": "",
|
||||||
"noregister": "Регистрация в текущее время недоступна. Администраторы могут вручную создать вам учётную запись, свяжитесь с ними.",
|
"noregister": "Регистрация в текущее время недоступна. Администраторы могут вручную создать вам учётную запись, свяжитесь с ними.",
|
||||||
|
|
||||||
"error_username": "Неизвестное имя пользователя.",
|
"error_username": "",
|
||||||
"error_password": "Неверный пароль.",
|
"error_password": "Неверный пароль.",
|
||||||
"error_telegram": "Не удалось авторизоваться через Телеграм.",
|
"error_telegram": "",
|
||||||
|
|
||||||
"go_back": "Назад",
|
"go_back": "Назад",
|
||||||
"go_home": "Домой",
|
"go_home": "Домой",
|
||||||
|
|||||||
1
main.go
1
main.go
@ -2,7 +2,6 @@
|
|||||||
//
|
//
|
||||||
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=history
|
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=history
|
||||||
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=mycoopts
|
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=mycoopts
|
||||||
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=auth
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
87
web/admin.go
87
web/admin.go
@ -2,16 +2,17 @@ package web
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
|
||||||
user2 "github.com/bouncepaw/mycorrhiza/internal/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"log"
|
"log"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
const adminTranslationRu = `
|
const adminTranslationRu = `
|
||||||
@ -52,53 +53,53 @@ const adminTranslationRu = `
|
|||||||
{{define "delete user warning"}}Вы уверены, что хотите удалить этого пользователя из базы данных? Это действие нельзя отменить.{{end}}
|
{{define "delete user warning"}}Вы уверены, что хотите удалить этого пользователя из базы данных? Это действие нельзя отменить.{{end}}
|
||||||
`
|
`
|
||||||
|
|
||||||
func viewPanel(meta viewutil2.Meta) {
|
func viewPanel(meta viewutil.Meta) {
|
||||||
viewutil2.ExecutePage(meta, panelChain, &viewutil2.BaseData{})
|
viewutil.ExecutePage(meta, panelChain, &viewutil.BaseData{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type listData struct {
|
type listData struct {
|
||||||
*viewutil2.BaseData
|
*viewutil.BaseData
|
||||||
UserHypha string
|
UserHypha string
|
||||||
Users []*user2.User
|
Users []*user.User
|
||||||
}
|
}
|
||||||
|
|
||||||
func viewList(meta viewutil2.Meta, users []*user2.User) {
|
func viewList(meta viewutil.Meta, users []*user.User) {
|
||||||
viewutil2.ExecutePage(meta, listChain, listData{
|
viewutil.ExecutePage(meta, listChain, listData{
|
||||||
BaseData: &viewutil2.BaseData{},
|
BaseData: &viewutil.BaseData{},
|
||||||
UserHypha: cfg.UserHypha,
|
UserHypha: cfg.UserHypha,
|
||||||
Users: users,
|
Users: users,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type newUserData struct {
|
type newUserData struct {
|
||||||
*viewutil2.BaseData
|
*viewutil.BaseData
|
||||||
Form util.FormData
|
Form util.FormData
|
||||||
}
|
}
|
||||||
|
|
||||||
func viewNewUser(meta viewutil2.Meta, form util.FormData) {
|
func viewNewUser(meta viewutil.Meta, form util.FormData) {
|
||||||
viewutil2.ExecutePage(meta, newUserChain, newUserData{
|
viewutil.ExecutePage(meta, newUserChain, newUserData{
|
||||||
BaseData: &viewutil2.BaseData{},
|
BaseData: &viewutil.BaseData{},
|
||||||
Form: form,
|
Form: form,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type editDeleteUserData struct {
|
type editDeleteUserData struct {
|
||||||
*viewutil2.BaseData
|
*viewutil.BaseData
|
||||||
Form util.FormData
|
Form util.FormData
|
||||||
U *user2.User
|
U *user.User
|
||||||
}
|
}
|
||||||
|
|
||||||
func viewEditUser(meta viewutil2.Meta, form util.FormData, u *user2.User) {
|
func viewEditUser(meta viewutil.Meta, form util.FormData, u *user.User) {
|
||||||
viewutil2.ExecutePage(meta, editUserChain, editDeleteUserData{
|
viewutil.ExecutePage(meta, editUserChain, editDeleteUserData{
|
||||||
BaseData: &viewutil2.BaseData{},
|
BaseData: &viewutil.BaseData{},
|
||||||
Form: form,
|
Form: form,
|
||||||
U: u,
|
U: u,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func viewDeleteUser(meta viewutil2.Meta, form util.FormData, u *user2.User) {
|
func viewDeleteUser(meta viewutil.Meta, form util.FormData, u *user.User) {
|
||||||
viewutil2.ExecutePage(meta, deleteUserChain, editDeleteUserData{
|
viewutil.ExecutePage(meta, deleteUserChain, editDeleteUserData{
|
||||||
BaseData: &viewutil2.BaseData{},
|
BaseData: &viewutil.BaseData{},
|
||||||
Form: form,
|
Form: form,
|
||||||
U: u,
|
U: u,
|
||||||
})
|
})
|
||||||
@ -108,12 +109,12 @@ func viewDeleteUser(meta viewutil2.Meta, form util.FormData, u *user2.User) {
|
|||||||
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
viewPanel(viewutil2.MetaFrom(w, rq))
|
viewPanel(viewutil.MetaFrom(w, rq))
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerAdminShutdown kills the wiki.
|
// handlerAdminShutdown kills the wiki.
|
||||||
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
|
||||||
if user2.CanProceed(rq, "admin/shutdown") {
|
if user.CanProceed(rq, "admin/shutdown") {
|
||||||
log.Println("An admin commanded the wiki to shutdown")
|
log.Println("An admin commanded the wiki to shutdown")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
@ -121,7 +122,7 @@ func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerAdminReindexUsers reinitialises the user system.
|
// handlerAdminReindexUsers reinitialises the user system.
|
||||||
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
|
||||||
user2.ReadUsersFromFilesystem()
|
user.ReadUsersFromFilesystem()
|
||||||
redirectTo := rq.Referer()
|
redirectTo := rq.Referer()
|
||||||
if redirectTo == "" {
|
if redirectTo == "" {
|
||||||
redirectTo = "/hypha/" + cfg.UserHypha
|
redirectTo = "/hypha/" + cfg.UserHypha
|
||||||
@ -131,8 +132,8 @@ func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) {
|
||||||
// Get a sorted list of users
|
// Get a sorted list of users
|
||||||
var users []*user2.User
|
var users []*user.User
|
||||||
for u := range user2.YieldUsers() {
|
for u := range user.YieldUsers() {
|
||||||
users = append(users, u)
|
users = append(users, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,12 +141,12 @@ func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) {
|
|||||||
less := users[i].RegisteredAt.Before(users[j].RegisteredAt)
|
less := users[i].RegisteredAt.Before(users[j].RegisteredAt)
|
||||||
return less
|
return less
|
||||||
})
|
})
|
||||||
viewList(viewutil2.MetaFrom(w, rq), users)
|
viewList(viewutil.MetaFrom(w, rq), users)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
|
||||||
vars := mux.Vars(rq)
|
vars := mux.Vars(rq)
|
||||||
u := user2.ByName(vars["username"])
|
u := user.ByName(vars["username"])
|
||||||
if u == nil {
|
if u == nil {
|
||||||
util.HTTP404Page(w, "404 page not found")
|
util.HTTP404Page(w, "404 page not found")
|
||||||
return
|
return
|
||||||
@ -157,9 +158,9 @@ func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
oldGroup := u.Group
|
oldGroup := u.Group
|
||||||
newGroup := f.Get("group")
|
newGroup := f.Get("group")
|
||||||
|
|
||||||
if user2.ValidGroup(newGroup) {
|
if user.ValidGroup(newGroup) {
|
||||||
u.Group = newGroup
|
u.Group = newGroup
|
||||||
if err := user2.SaveUserDatabase(); err != nil {
|
if err := user.SaveUserDatabase(); err != nil {
|
||||||
u.Group = oldGroup
|
u.Group = oldGroup
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
f = f.WithError(err)
|
f = f.WithError(err)
|
||||||
@ -179,12 +180,12 @@ func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
||||||
|
|
||||||
viewEditUser(viewutil2.MetaFrom(w, rq), f, u)
|
viewEditUser(viewutil.MetaFrom(w, rq), f, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) {
|
||||||
vars := mux.Vars(rq)
|
vars := mux.Vars(rq)
|
||||||
u := user2.ByName(vars["username"])
|
u := user.ByName(vars["username"])
|
||||||
if u == nil {
|
if u == nil {
|
||||||
util.HTTP404Page(w, "404 page not found")
|
util.HTTP404Page(w, "404 page not found")
|
||||||
return
|
return
|
||||||
@ -204,7 +205,7 @@ func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) {
|
|||||||
if err := u.ChangePassword(password); err != nil {
|
if err := u.ChangePassword(password); err != nil {
|
||||||
f = f.WithError(err)
|
f = f.WithError(err)
|
||||||
} else {
|
} else {
|
||||||
if err := user2.SaveUserDatabase(); err != nil {
|
if err := user.SaveUserDatabase(); err != nil {
|
||||||
u.Password = previousPassword
|
u.Password = previousPassword
|
||||||
f = f.WithError(err)
|
f = f.WithError(err)
|
||||||
} else {
|
} else {
|
||||||
@ -222,12 +223,12 @@ func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
||||||
|
|
||||||
viewEditUser(viewutil2.MetaFrom(w, rq), f, u)
|
viewEditUser(viewutil.MetaFrom(w, rq), f, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
|
||||||
vars := mux.Vars(rq)
|
vars := mux.Vars(rq)
|
||||||
u := user2.ByName(vars["username"])
|
u := user.ByName(vars["username"])
|
||||||
if u == nil {
|
if u == nil {
|
||||||
util.HTTP404Page(w, "404 page not found")
|
util.HTTP404Page(w, "404 page not found")
|
||||||
return
|
return
|
||||||
@ -236,7 +237,7 @@ func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
|
|||||||
f := util.NewFormData()
|
f := util.NewFormData()
|
||||||
|
|
||||||
if rq.Method == http.MethodPost {
|
if rq.Method == http.MethodPost {
|
||||||
f = f.WithError(user2.DeleteUser(u.Name))
|
f = f.WithError(user.DeleteUser(u.Name))
|
||||||
if !f.HasError() {
|
if !f.HasError() {
|
||||||
http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther)
|
http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther)
|
||||||
} else {
|
} else {
|
||||||
@ -248,23 +249,23 @@ func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
|
|||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
||||||
viewDeleteUser(viewutil2.MetaFrom(w, rq), f, u)
|
viewDeleteUser(viewutil.MetaFrom(w, rq), f, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerAdminUserNew(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminUserNew(w http.ResponseWriter, rq *http.Request) {
|
||||||
if rq.Method == http.MethodGet {
|
if rq.Method == http.MethodGet {
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
||||||
viewNewUser(viewutil2.MetaFrom(w, rq), util.NewFormData())
|
viewNewUser(viewutil.MetaFrom(w, rq), util.NewFormData())
|
||||||
} else if rq.Method == http.MethodPost {
|
} else if rq.Method == http.MethodPost {
|
||||||
// Create a user
|
// Create a user
|
||||||
f := util.FormDataFromRequest(rq, []string{"name", "password", "group"})
|
f := util.FormDataFromRequest(rq, []string{"name", "password", "group"})
|
||||||
|
|
||||||
err := user2.Register(f.Get("name"), f.Get("password"), f.Get("group"), "local", true)
|
err := user.Register(f.Get("name"), f.Get("password"), f.Get("group"), "local", true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
||||||
viewNewUser(viewutil2.MetaFrom(w, rq), f.WithError(err))
|
viewNewUser(viewutil.MetaFrom(w, rq), f.WithError(err))
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther)
|
http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,6 +107,7 @@ func (p *Page) RenderTo(meta viewutil.Meta, data map[string]any) error {
|
|||||||
data["CommonScripts"] = cfg.CommonScripts
|
data["CommonScripts"] = cfg.CommonScripts
|
||||||
data["EditScripts"] = cfg.EditScripts
|
data["EditScripts"] = cfg.EditScripts
|
||||||
data["HeaderLinks"] = viewutil.HeaderLinks
|
data["HeaderLinks"] = viewutil.HeaderLinks
|
||||||
|
data["UseAuth"] = cfg.UseAuth
|
||||||
|
|
||||||
tmpl := p.TemplateEnglish
|
tmpl := p.TemplateEnglish
|
||||||
if meta.LocaleIsRussian() {
|
if meta.LocaleIsRussian() {
|
||||||
|
|||||||
40
web/pages.go
40
web/pages.go
@ -12,6 +12,8 @@ var fs embed.FS
|
|||||||
var pageOrphans, pageBacklinks, pageUserList, pageChangePassword *newtmpl.Page
|
var pageOrphans, pageBacklinks, pageUserList, pageChangePassword *newtmpl.Page
|
||||||
var pageHyphaDelete, pageHyphaEdit, pageHyphaEmpty, pageHypha *newtmpl.Page
|
var pageHyphaDelete, pageHyphaEdit, pageHyphaEmpty, pageHypha *newtmpl.Page
|
||||||
var pageRevision, pageMedia *newtmpl.Page
|
var pageRevision, pageMedia *newtmpl.Page
|
||||||
|
var pageAuthLock, pageAuthLogin, pageAuthLogout, pageAuthRegister, pageAuthTelegram *newtmpl.Page
|
||||||
|
|
||||||
var panelChain, listChain, newUserChain, editUserChain, deleteUserChain viewutil.Chain
|
var panelChain, listChain, newUserChain, editUserChain, deleteUserChain viewutil.Chain
|
||||||
|
|
||||||
func initPages() {
|
func initPages() {
|
||||||
@ -136,4 +138,42 @@ func initPages() {
|
|||||||
"remove tip": "Заметьте, чтобы заменить медиа, вам не нужно его перед этим откреплять.",
|
"remove tip": "Заметьте, чтобы заменить медиа, вам не нужно его перед этим откреплять.",
|
||||||
"remove btn": "Открепить",
|
"remove btn": "Открепить",
|
||||||
}, "views/hypha-media.html")
|
}, "views/hypha-media.html")
|
||||||
|
|
||||||
|
pageAuthLock = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"lock title": "Доступ закрыт",
|
||||||
|
"username": "Логин",
|
||||||
|
"password": "Пароль",
|
||||||
|
"log in": "Войти",
|
||||||
|
}, "views/auth-telegram.html", "views/auth-lock.html")
|
||||||
|
|
||||||
|
pageAuthLogin = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"username": "Логин",
|
||||||
|
"password": "Пароль",
|
||||||
|
"log in": "Войти",
|
||||||
|
"cookie tip": "Отправляя эту форму, вы разрешаете вики хранить cookie в вашем браузере. Это позволит движку связывать ваши правки с вашей учётной записью. Вы будете авторизованы, пока не выйдете из учётной записи.",
|
||||||
|
"log in to x": "Войти в {{.}}",
|
||||||
|
"auth disabled": "Аутентификация отключена. Вы можете делать правки анонимно.",
|
||||||
|
"error username": "Неизвестное имя пользователя.",
|
||||||
|
"error password": "Неправильный пароль.",
|
||||||
|
"error telegram": "Не удалось войти через Телеграм.",
|
||||||
|
"go home": "Домой",
|
||||||
|
}, "views/auth-telegram.html", "views/auth-login.html")
|
||||||
|
|
||||||
|
pageAuthLogout = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"log out?": "Выйти?",
|
||||||
|
"log out": "Выйти",
|
||||||
|
"cannot log out anon": "Вы не можете выйти, потому что ещё не вошли.",
|
||||||
|
"log in": "Войти",
|
||||||
|
"go home": "Домой",
|
||||||
|
}, "views/auth-logout.html")
|
||||||
|
|
||||||
|
pageAuthRegister = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"username": "Логин",
|
||||||
|
"password": "Пароль",
|
||||||
|
"cookie tip": "Отправляя эту форму, вы разрешаете вики хранить cookie в вашем браузере. Это позволит движку связывать ваши правки с вашей учётной записью. Вы будете авторизованы, пока не выйдете из учётной записи.",
|
||||||
|
"password tip": "Сервер хранит ваш пароль в зашифрованном виде, даже администраторы не смогут его прочесть.",
|
||||||
|
"register btn": "Зарегистрироваться",
|
||||||
|
"register on x": "Регистрация на {{.}}",
|
||||||
|
}, "views/auth-telegram.html", "views/auth-register.html")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
33
web/views/auth-lock.html
Normal file
33
web/views/auth-lock.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{{define "title"}}{{block "lock title" .}}Locked{{end}}{{end}}
|
||||||
|
{{define "page"}}
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>🔒 {{template "lock title" .}}</title>
|
||||||
|
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="locked-notice main-width">
|
||||||
|
<section class="locked-notice__message">
|
||||||
|
<p class="locked-notice__lock">🔒</p>
|
||||||
|
<h1 class="locked-notice__title">{{template "lock title" .}}</h1>
|
||||||
|
<form class="locked-notice__login-form" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
||||||
|
<label for="login-form__username">{{block "username" .}}Username{{end}}</label>
|
||||||
|
<br>
|
||||||
|
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
||||||
|
<br>
|
||||||
|
<label for="login-form__password">{{block "password" .}}Password{{end}}</label>
|
||||||
|
<br>
|
||||||
|
<input type="password" required name="password" autocomplete="current-password" id="login-form__password">
|
||||||
|
<br>
|
||||||
|
<button class="btn" type="submit" value="Log in">{{block "log in" .}}Log in{{end}}</button>
|
||||||
|
</form>
|
||||||
|
{{template "telegram widget"}}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
||||||
39
web/views/auth-login.html
Normal file
39
web/views/auth-login.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{{define "log in to x"}}Log in to {{.}}{{end}}
|
||||||
|
{{define "title"}}{{template "log in to x" .WikiName}}{{end}}
|
||||||
|
{{define "body"}}
|
||||||
|
<main class="main-width">
|
||||||
|
<section>
|
||||||
|
{{if .UseAuth}}
|
||||||
|
{{if .ErrUnknownUsername}}
|
||||||
|
<p class="error">{{block "error username" .}}Unknown username.{{end}}</p>
|
||||||
|
{{else if .ErrWrongPassword}}
|
||||||
|
<p class="error">{{block "error password" .}}Wrong password.{{end}}</p>
|
||||||
|
{{else if .ErrTelegram}}
|
||||||
|
<p class="error">{{block "error telegram" .}}Could not authorize using Telegram.{{end}}</p>
|
||||||
|
{{else if .Err}}
|
||||||
|
<p class="error">{{.Err}}</p>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<form class="modal" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
||||||
|
<fieldset class="modal__fieldset">
|
||||||
|
<legend class="modal__title">{{template "log in to x" .WikiName}}</legend>
|
||||||
|
<label for="login-form__username">{{block "username" .}}Username{{end}}</label>
|
||||||
|
<br>
|
||||||
|
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username"{{if .Username}} value="{{.Username}}"{{end}}>
|
||||||
|
<br>
|
||||||
|
<label for="login-form__password">{{block "password" .}}Password{{end}}</label>
|
||||||
|
<br>
|
||||||
|
<input type="password" required name="password" autocomplete="current-password">
|
||||||
|
<p>{{block "cookie tip" .}}By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you. You will stay logged in until you log out.{{end}}</p>
|
||||||
|
<button class="btn" type="submit" value="Log in">{{block "log in" .}}Log in{{end}}</button>
|
||||||
|
<a class="btn btn_weak" href="/">{{block "cancel" .}}Cancel{{end}}</a>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{{template "telegram widget" .}}
|
||||||
|
{{else}}
|
||||||
|
<p>{{block "auth disabled" .}}Authentication is disabled. You can make edits anonymously.{{end}}</p>
|
||||||
|
<p><a class="btn btn_weak" href="/">← {{block "go home" .}}Go home{{end}}</a></p>
|
||||||
|
{{end}}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
{{end}}
|
||||||
18
web/views/auth-logout.html
Normal file
18
web/views/auth-logout.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{{define "title"}}{{end}}
|
||||||
|
{{define "body"}}
|
||||||
|
<main class="main-width">
|
||||||
|
<section>
|
||||||
|
{{if .CanLogout}}
|
||||||
|
<h1>{{block "log out?" .}}Log out?{{end}}</h1>
|
||||||
|
<form method="POST" action="/logout">
|
||||||
|
<input class="btn btn_accent" type="submit" value='{{block "log out" .}}Log out{{end}}'/>
|
||||||
|
<p><a class="btn btn_weak" href="/">← {{block "go home" .}}Go home{{end}}</a></p>
|
||||||
|
</form>
|
||||||
|
{{else}}
|
||||||
|
<p>{{block "cannot log out anon" .}}You cannot log out because you are not logged in.{{end}}</p>
|
||||||
|
<p><a href="/login">{{block "log in" .}}Log in{{end}}</a></p>
|
||||||
|
<p><a href="/">← {{template "go home"}}</a></p>
|
||||||
|
{{end}}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
{{end}}
|
||||||
35
web/views/auth-register.html
Normal file
35
web/views/auth-register.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{{define "register on x"}}Register on {{.}}{{end}}
|
||||||
|
{{define "title"}}{{template "register on x" .WikiName}}{{end}}
|
||||||
|
{{define "body"}}
|
||||||
|
<main class="main-width">
|
||||||
|
<section>
|
||||||
|
{{if .AllowRegistration}}
|
||||||
|
<form class="modal" method="post" action="/register?{{.RawQuery}}" id="register-form" enctype="multipart/form-data" autocomplete="off">
|
||||||
|
<fieldset class="modal__fieldset">
|
||||||
|
<legend class="modal__title">{{template "register on x" .WikiName}}</legend>
|
||||||
|
{{if .Err}}<p class="error">{{.Err.Error}}</p>{{end}}
|
||||||
|
|
||||||
|
<label for="register-form__username">{{block "username" .}}Username{{end}}</label>
|
||||||
|
<br>
|
||||||
|
<input type="text" required autofocus id="register-form__username" name="username"{{if .Username}} value="{{.Username}}"{{end}}>
|
||||||
|
<br>
|
||||||
|
<label for="register-form__password">{{block "password" .}}Password{{end}}</label>
|
||||||
|
<br>
|
||||||
|
<input type="password" required name="password" id="register-form__password"{{if .Password}} value="{{.Password}}"{{end}}>
|
||||||
|
<p>{{block "password tip" .}}The server stores your password in an encrypted form; even administrators cannot read it.{{end}}</p>
|
||||||
|
<p>{{block "cookie tip" .}}By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you. You will stay logged in until you log out.{{end}}</p>
|
||||||
|
<button class="btn" type="submit">{{block "register btn" .}}Register{{end}}</button>
|
||||||
|
<a class="btn btn_weak" href="/{{.RawQuery}}">{{block "cancel" .}}Cancel{{end}}</a>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{{template "telegram widget" .}}
|
||||||
|
{{else if .UseAuth}}
|
||||||
|
<p>{%s lc.Get("auth.noregister") %}</p>
|
||||||
|
<p><a href="/{{.RawQuery}}">← {%s lc.Get("auth.go_back") %}</a></p>
|
||||||
|
{{else}}
|
||||||
|
<p>{%s lc.Get("auth.noauth") %}</p>
|
||||||
|
<p><a href="/{{.RawQuery}}">← {%s lc.Get("auth.go_back") %}</a></p>
|
||||||
|
{{end}}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
{{end}}
|
||||||
11
web/views/auth-telegram.html
Normal file
11
web/views/auth-telegram.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{{define "telegram widget"}}
|
||||||
|
{{if .TelegramEnabled}}<!-- TODO: translate -->
|
||||||
|
<p class="telegram-notice">{{block "telegram tip" .}}You can log in using Telegram. It only works if you have set your @username in Telegram and this username is free on this wiki.{{end}}</p>
|
||||||
|
<script async
|
||||||
|
src="https://telegram.org/js/telegram-widget.js?15"
|
||||||
|
data-telegram-login="{{.TelegramBotName}}"
|
||||||
|
data-size="medium"
|
||||||
|
data-userpic="false"
|
||||||
|
data-auth-url="{{.URL}}/telegram-login"></script>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
182
web/web.go
182
web/web.go
@ -5,17 +5,17 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
user2 "github.com/bouncepaw/mycorrhiza/internal/user"
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/l18n"
|
"github.com/bouncepaw/mycorrhiza/l18n"
|
||||||
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/auth"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/categories"
|
"github.com/bouncepaw/mycorrhiza/categories"
|
||||||
"github.com/bouncepaw/mycorrhiza/help"
|
"github.com/bouncepaw/mycorrhiza/help"
|
||||||
"github.com/bouncepaw/mycorrhiza/history/histweb"
|
"github.com/bouncepaw/mycorrhiza/history/histweb"
|
||||||
@ -63,7 +63,7 @@ func Handler() http.Handler {
|
|||||||
r := router.PathPrefix("").Subrouter()
|
r := router.PathPrefix("").Subrouter()
|
||||||
r.Use(func(next http.Handler) http.Handler {
|
r.Use(func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) {
|
||||||
user := user2.FromRequest(rq)
|
user := user.FromRequest(rq)
|
||||||
if !user.ShowLockMaybe(w, rq) {
|
if !user.ShowLockMaybe(w, rq) {
|
||||||
next.ServeHTTP(w, rq)
|
next.ServeHTTP(w, rq)
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ func Handler() http.Handler {
|
|||||||
func groupMiddleware(group string) func(http.Handler) http.Handler {
|
func groupMiddleware(group string) func(http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) {
|
||||||
if cfg.UseAuth && user2.CanProceed(rq, group) {
|
if cfg.UseAuth && user.CanProceed(rq, group) {
|
||||||
next.ServeHTTP(w, rq)
|
next.ServeHTTP(w, rq)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -133,8 +133,8 @@ func groupMiddleware(group string) func(http.Handler) http.Handler {
|
|||||||
|
|
||||||
// Auth
|
// Auth
|
||||||
func handlerUserList(w http.ResponseWriter, rq *http.Request) {
|
func handlerUserList(w http.ResponseWriter, rq *http.Request) {
|
||||||
admins, moderators, editors, readers := user2.UsersInGroups()
|
admins, moderators, editors, readers := user.UsersInGroups()
|
||||||
_ = pageUserList.RenderTo(viewutil2.MetaFrom(w, rq),
|
_ = pageUserList.RenderTo(viewutil.MetaFrom(w, rq),
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"Admins": admins,
|
"Admins": admins,
|
||||||
"Moderators": moderators,
|
"Moderators": moderators,
|
||||||
@ -144,53 +144,47 @@ func handlerUserList(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerLock(w http.ResponseWriter, rq *http.Request) {
|
func handlerLock(w http.ResponseWriter, rq *http.Request) {
|
||||||
_, _ = io.WriteString(w, auth.Lock(l18n.FromRequest(rq)))
|
_ = pageAuthLock.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerRegister displays the register form (GET) or registers the user (POST).
|
// handlerRegister displays the register form (GET) or registers the user (POST).
|
||||||
func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
util.PrepareRq(rq)
|
util.PrepareRq(rq)
|
||||||
if rq.Method == http.MethodGet {
|
if rq.Method == http.MethodGet {
|
||||||
_, _ = io.WriteString(
|
slog.Info("Showing registration form")
|
||||||
w,
|
_ = pageAuthRegister.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{
|
||||||
viewutil2.Base(
|
"UseAuth": cfg.UseAuth,
|
||||||
viewutil2.MetaFrom(w, rq),
|
"AllowRegistration": cfg.AllowRegistration,
|
||||||
lc.Get("auth.register_title"),
|
"RawQuery": rq.URL.RawQuery,
|
||||||
auth.Register(rq),
|
"WikiName": cfg.WikiName,
|
||||||
map[string]string{},
|
})
|
||||||
),
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
username = rq.PostFormValue("username")
|
username = rq.PostFormValue("username")
|
||||||
password = rq.PostFormValue("password")
|
password = rq.PostFormValue("password")
|
||||||
err = user2.Register(username, password, "editor", "local", false)
|
err = user.Register(username, password, "editor", "local", false)
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to register ‘%s’: %s", username, err.Error())
|
slog.Info("Failed to register", "username", username, "err", err.Error())
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
_, _ = io.WriteString(
|
_ = pageAuthRegister.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{
|
||||||
w,
|
"UseAuth": cfg.UseAuth,
|
||||||
viewutil2.Base(
|
"AllowRegistration": cfg.AllowRegistration,
|
||||||
viewutil2.MetaFrom(w, rq),
|
"RawQuery": rq.URL.RawQuery,
|
||||||
lc.Get("auth.register_title"),
|
"WikiName": cfg.WikiName,
|
||||||
fmt.Sprintf(
|
|
||||||
`<main class="main-width"><p>%s</p><p><a href="/register">%s<a></p></main>`,
|
"Err": err,
|
||||||
err.Error(),
|
"Username": username,
|
||||||
lc.Get("auth.try_again"),
|
"Password": password,
|
||||||
),
|
})
|
||||||
map[string]string{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Successfully registered ‘%s’", username)
|
slog.Info("Registered user", "username", username)
|
||||||
if err := user2.LoginDataHTTP(w, username, password); err != nil {
|
if err := user.LoginDataHTTP(w, username, password); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther)
|
http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther)
|
||||||
@ -198,71 +192,79 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerLogout shows the logout form (GET) or logs the user out (POST).
|
// handlerLogout shows the logout form (GET) or logs the user out (POST).
|
||||||
func handlerLogout(w http.ResponseWriter, rq *http.Request) {
|
func handlerLogout(w http.ResponseWriter, rq *http.Request) {
|
||||||
if rq.Method == http.MethodGet {
|
if rq.Method == http.MethodPost {
|
||||||
var (
|
slog.Info("Somebody logged out")
|
||||||
u = user2.FromRequest(rq)
|
user.LogoutFromRequest(w, rq)
|
||||||
can = u != nil
|
|
||||||
lc = l18n.FromRequest(rq)
|
|
||||||
)
|
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
if can {
|
|
||||||
log.Println("User", u.Name, "tries to log out")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
} else {
|
|
||||||
log.Println("Unknown user tries to log out")
|
|
||||||
w.WriteHeader(http.StatusForbidden)
|
|
||||||
}
|
|
||||||
_, _ = io.WriteString(
|
|
||||||
w,
|
|
||||||
viewutil2.Base(viewutil2.MetaFrom(w, rq), lc.Get("auth.logout_title"), auth.Logout(can, lc), map[string]string{}),
|
|
||||||
)
|
|
||||||
} else if rq.Method == http.MethodPost {
|
|
||||||
user2.LogoutFromRequest(w, rq)
|
|
||||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
u = user.FromRequest(rq)
|
||||||
|
can = u != nil
|
||||||
|
)
|
||||||
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
|
if can {
|
||||||
|
slog.Info("Logging out", "username", u.Name)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
} else {
|
||||||
|
slog.Info("Unknown user logging out")
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
}
|
||||||
|
_ = pageAuthLogout.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{
|
||||||
|
"CanLogout": can,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerLogin shows the login form (GET) or logs the user in (POST).
|
// handlerLogin shows the login form (GET) or logs the user in (POST).
|
||||||
func handlerLogin(w http.ResponseWriter, rq *http.Request) {
|
func handlerLogin(w http.ResponseWriter, rq *http.Request) {
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
if rq.Method == http.MethodGet {
|
if rq.Method == http.MethodGet {
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_, _ = io.WriteString(
|
_ = pageAuthLogin.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{
|
||||||
w,
|
"UseAuth": cfg.UseAuth,
|
||||||
viewutil2.Base(
|
"ErrUnknownUsername": false,
|
||||||
viewutil2.MetaFrom(w, rq),
|
"ErrWrongPassword": false,
|
||||||
lc.Get("auth.login_title"),
|
"ErrTelegram": false,
|
||||||
auth.Login(lc),
|
"Err": nil,
|
||||||
map[string]string{},
|
"WikiName": cfg.WikiName,
|
||||||
),
|
})
|
||||||
)
|
slog.Info("Somebody logging in")
|
||||||
} else if rq.Method == http.MethodPost {
|
return
|
||||||
var (
|
|
||||||
username = util.CanonicalName(rq.PostFormValue("username"))
|
|
||||||
password = rq.PostFormValue("password")
|
|
||||||
err = user2.LoginDataHTTP(w, username, password)
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
_, _ = io.WriteString(w, viewutil2.Base(viewutil2.MetaFrom(w, rq), err.Error(), auth.LoginError(err.Error(), lc), map[string]string{}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
username = util.CanonicalName(rq.PostFormValue("username"))
|
||||||
|
password = rq.PostFormValue("password")
|
||||||
|
err = user.LoginDataHTTP(w, username, password)
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_ = pageAuthLogin.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{
|
||||||
|
"UseAuth": cfg.UseAuth,
|
||||||
|
"ErrUnknownUsername": errors.Is(err, user.ErrUnknownUsername),
|
||||||
|
"ErrWrongPassword": errors.Is(err, user.ErrWrongPassword),
|
||||||
|
"ErrTelegram": false, // TODO: ?
|
||||||
|
"Err": err.Error(),
|
||||||
|
"WikiName": cfg.WikiName,
|
||||||
|
"Username": username,
|
||||||
|
})
|
||||||
|
slog.Info("Failed to log in", "username", username, "err", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||||
|
slog.Info("Logged in", "username", username)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
|
func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
|
||||||
// Note there is no lock here.
|
// Note there is no lock here.
|
||||||
lc := l18n.FromRequest(rq)
|
lc := l18n.FromRequest(rq)
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
rq.ParseForm()
|
_ = rq.ParseForm()
|
||||||
var (
|
var (
|
||||||
values = rq.URL.Query()
|
values = rq.URL.Query()
|
||||||
username = strings.ToLower(values.Get("username"))
|
username = strings.ToLower(values.Get("username"))
|
||||||
seemsValid = user2.TelegramAuthParamsAreValid(values)
|
seemsValid = user.TelegramAuthParamsAreValid(values)
|
||||||
err = user2.Register(
|
err = user.Register(
|
||||||
username,
|
username,
|
||||||
"", // Password matters not
|
"", // Password matters not
|
||||||
"editor",
|
"editor",
|
||||||
@ -272,7 +274,7 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
|
|||||||
)
|
)
|
||||||
// If registering a user via Telegram failed, because a Telegram user with this name
|
// If registering a user via Telegram failed, because a Telegram user with this name
|
||||||
// has already registered, then everything is actually ok!
|
// has already registered, then everything is actually ok!
|
||||||
if user2.HasUsername(username) && user2.ByName(username).Source == "telegram" {
|
if user.HasUsername(username) && user.ByName(username).Source == "telegram" {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,12 +283,12 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to register ‘%s’ using Telegram: %s", username, err.Error())
|
slog.Info("Failed to register", "username", username, "err", err.Error(), "method", "telegram")
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
_, _ = io.WriteString(
|
_, _ = io.WriteString(
|
||||||
w,
|
w,
|
||||||
viewutil2.Base(
|
viewutil.Base(
|
||||||
viewutil2.MetaFrom(w, rq),
|
viewutil.MetaFrom(w, rq),
|
||||||
lc.Get("ui.error"),
|
lc.Get("ui.error"),
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
|
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
|
||||||
@ -300,14 +302,14 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
errmsg := user2.LoginDataHTTP(w, username, "")
|
errmsg := user.LoginDataHTTP(w, username, "")
|
||||||
if errmsg != nil {
|
if errmsg != nil {
|
||||||
log.Printf("Failed to login ‘%s’ using Telegram: %s", username, err.Error())
|
log.Printf("Failed to login ‘%s’ using Telegram: %s", username, err.Error())
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
_, _ = io.WriteString(
|
_, _ = io.WriteString(
|
||||||
w,
|
w,
|
||||||
viewutil2.Base(
|
viewutil.Base(
|
||||||
viewutil2.MetaFrom(w, rq),
|
viewutil.MetaFrom(w, rq),
|
||||||
"Error",
|
"Error",
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
|
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
|
||||||
@ -320,6 +322,6 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("Authorize ‘%s’ from Telegram", username)
|
|
||||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||||
|
slog.Info("Logged in", "username", username, "method", "telegram")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user