;; 平台登录页(GET/POST /login, GET /logout)—— 版式按 docs/登录页面.png
(defmodule lamina_login_pages
(export all))
(defun show (req)
(case (lamina_auth:verify-request req)
(`#(ok ,_) `#(redirect "/admin"))
(_ (lamina_ui:html 200 (page "" 'error)))))
(defun do-login (req)
(let ((params (maps:get 'params req #M())))
(let ((username (maps:get #"username" params #""))
(password (maps:get #"password" params #"")))
(case (lamina_auth:login username password)
(`#(ok ,cookie)
(let ((value (cookie-value-of cookie)))
(let ((req1 (cowboy_req:set_resp_cookie
(lamina_auth:session-cookie-name) value req
(cookie-options (lamina_auth:session-ttl-seconds)))))
(tuple 'redirect "/admin" req1))))
(`#(error ,msg)
(lamina_ui:html 200 (page msg 'error)))
(other
(lamina_ui:html 200 (page (lists:flatten (io_lib:format "登录失败: ~p" (list other))) 'error)))))))
(defun do-logout (req)
(let ((req1 (cowboy_req:set_resp_cookie
(lamina_auth:session-cookie-name) #"" req
(cookie-options 0))))
(tuple 'redirect "/" req1)))
(defun cookie-options (max-age)
(maps:from_list
(list (tuple 'path #"/")
(tuple 'max_age max-age)
(tuple 'http_only 'true)
(tuple 'same_site 'lax)
(tuple 'secure (secure-cookie?)))))
(defun cookie-value-of (cookie-string)
(let ((rest (string:slice cookie-string (length "lamina_session=") (length cookie-string))))
(hd (string:split rest ";" 'leading))))
(defun page (err session)
(let ((site (lamina_site_mgr:all)))
(let* ((site-name (maps:get 'site_name site "lamina"))
(tagline (maps:get 'site_tagline site "Small teams. Open source. Built on Fossil."))
(root (lamina_ui:trim-right-slash (maps:get 'site_url site "http://localhost:8827"))))
(lists:flatten
(list
(lamina_ui:public-head (lists:flatten (list "Sign in · " site-name)))
(lamina_ui:public-nav site-name session)
(lamina_ui:public-tagline tagline)
"<main class=\"login-block\"><h1>Sign in to " (lamina_ui:escape site-name) "</h1>"
"<p class=\"lede\">Access your repositories, organizations, and Fossil-hosted projects.</p>"
(if (=:= "" err) "" (list "<div class=\"err\">" (lamina_ui:escape err) "</div>"))
"<form method=\"POST\" action=\"/login\">"
"<label>Username<input name=\"username\" autocomplete=\"username\" autofocus></label>"
"<label>Password<input type=\"password\" name=\"password\" autocomplete=\"current-password\"></label>"
"<button class=\"btn\" type=\"submit\">Sign in</button></form>"
"<p class=\"hint\">Need an account? Contact your administrator.</p>"
"<p class=\"hint\">" (lamina_ui:escape site-name)
" is designed for self-hosted teams and private infrastructure.</p>"
"<hr class=\"rule\"><h2>Using Fossil?</h2>"
"<p>Repository permissions and clone access are managed after signing in. Once you have an account, you can clone repositories using the Fossil command-line tool:</p>"
(lamina_ui:clone-box (lists:flatten (list "fossil clone " root "/repository")))
"<p class=\"hint\">See the <a href=\"https://fossil-scm.org/home/doc/trunk/www/index.wiki\">project documentation</a> for more information.</p>"
"</main>"
(lamina_ui:public-footer site)
"</div></body></html>")))))
(defun secure-cookie? ()
(let ((url (lamina_site_mgr:site-url)))
(or (lists:prefix "https://" (lamina_ui:to-str url))
(lists:prefix "HTTPS://" (lamina_ui:to-str url)))))