admin panel, login page with auth session.

This commit is contained in:
samantha42
2026-04-30 08:02:03 +02:00
parent 676568100f
commit fbc880f40b
13 changed files with 1562 additions and 15 deletions

View File

@@ -1,13 +1,19 @@
package website
import (
"Portifolio/internal/middleware"
"Portifolio/internal/model"
"Portifolio/internal/service"
"crypto/rand"
_ "embed"
"encoding/base64"
"fmt"
"net/http"
"os"
"strconv"
"time"
"github.com/FuLygon/go-totp/v2"
_ "github.com/mattn/go-sqlite3"
)
@@ -21,13 +27,136 @@ func Getsite() http.HandlerFunc {
}
}
//go:embed static/styles.css
var styleCSS []byte
//go:embed static/login.html
var loginxHTML []byte
func Getstylesheet() http.HandlerFunc {
func GetAdminLogin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write(loginxHTML)
}
}
type contextKey string
const ContextKeyRequestTime contextKey = "requestTime"
func LoginHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// checked once when the handler is registered
envUsername := os.Getenv("username")
envPassword := os.Getenv("password")
envTOTPSecret := os.Getenv("AUTH_TOTP_SECRET")
if envUsername == "" || envPassword == "" || envTOTPSecret == "" {
http.Error(w, "env var missing", http.StatusInternalServerError)
}
ts, ok := r.Context().Value(ContextKeyRequestTime).(time.Time)
if !ok {
ts = time.Now()
}
if err := r.ParseForm(); err != nil {
http.Error(w, "invalid form data", http.StatusBadRequest)
return
}
password := r.FormValue("password")
username := r.FormValue("username")
code := r.FormValue("auth_code")
fmt.Println(username, password, code)
if password == "" || username == "" || code == "" {
http.Error(w, "form value is empty", http.StatusBadRequest)
return
}
if password != envPassword || username != envUsername {
http.Error(w, "username or password is not valid", http.StatusUnauthorized)
return
}
v := totp.Validator{
Algorithm: totp.AlgorithmSHA1,
Digits: 6,
Period: 30,
Secret: envTOTPSecret,
}
valid, err := v.ValidateWithTimestamp(code, ts.Unix())
if err != nil {
http.Error(w, fmt.Sprintf("error validating TOTP code: %s", err), http.StatusUnauthorized)
return
}
if !valid {
http.Error(w, "invalid auth code", http.StatusUnauthorized)
return
}
// In your login POST handler, after setting the cookie:
token := generateSessionToken()
middleware.RegisterSession(token) // <-- register before writing cookie
http.SetCookie(w, &http.Cookie{
Name: "session",
Value: token,
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteLaxMode,
MaxAge: 86400,
})
w.Header().Set("HX-Redirect", "/admin")
w.WriteHeader(http.StatusOK)
}
}
// Simple session token generator
func generateSessionToken() string {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return base64.URLEncoding.EncodeToString(b)
}
//go:embed static/admin.html
var adminHTML []byte
func GetAdmin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write(adminHTML)
}
}
//go:embed static/styles.css
var styleCSSmain []byte
func GetstylesheetMain() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/css; charset=utf-8")
w.Write(styleCSS)
w.Write(styleCSSmain)
}
}
//go:embed static/login.css
var styleCSSlogin []byte
func GetstylesheetLogin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/css; charset=utf-8")
w.Write(styleCSSlogin)
}
}
//go:embed static/admin.css
var styleCSSadmin []byte
func GetstylesheetAdmin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/css; charset=utf-8")
w.Write(styleCSSadmin)
}
}