mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-19 18:13:41 -05:00
server: add authentication
This commit is contained in:
parent
efbf75dad8
commit
7afa2d48c4
8 changed files with 328 additions and 60 deletions
112
config.go
Normal file
112
config.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
// gomuks - A Matrix client written in Go.
|
||||
// Copyright (C) 2024 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.mau.fi/util/ptr"
|
||||
"go.mau.fi/util/random"
|
||||
"go.mau.fi/zeroconfig"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Web WebConfig `yaml:"web"`
|
||||
Logging zeroconfig.Config `yaml:"logging"`
|
||||
}
|
||||
|
||||
type WebConfig struct {
|
||||
ListenAddress string `yaml:"listen_address"`
|
||||
Username string `yaml:"username"`
|
||||
PasswordHash string `yaml:"password_hash"`
|
||||
TokenKey string `yaml:"token_key"`
|
||||
}
|
||||
|
||||
var defaultConfig = Config{
|
||||
Web: WebConfig{
|
||||
ListenAddress: "localhost:29325",
|
||||
},
|
||||
Logging: zeroconfig.Config{
|
||||
MinLevel: ptr.Ptr(zerolog.TraceLevel),
|
||||
Writers: []zeroconfig.WriterConfig{{
|
||||
Type: zeroconfig.WriterTypeStdout,
|
||||
Format: zeroconfig.LogFormatPrettyColored,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) LoadConfig() error {
|
||||
file, err := os.Open(filepath.Join(gmx.ConfigDir, "config.yaml"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
gmx.Config = defaultConfig
|
||||
changed := false
|
||||
if file != nil {
|
||||
err = yaml.NewDecoder(file).Decode(&gmx.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
changed = true
|
||||
}
|
||||
if gmx.Config.Web.TokenKey == "" {
|
||||
gmx.Config.Web.TokenKey = random.String(64)
|
||||
changed = true
|
||||
}
|
||||
if gmx.Config.Web.Username == "" || gmx.Config.Web.PasswordHash == "" {
|
||||
fmt.Println("Please create a username and password for authenticating the web app")
|
||||
fmt.Print("Username: ")
|
||||
_, err = fmt.Scanln(&gmx.Config.Web.Username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read username: %w", err)
|
||||
}
|
||||
fmt.Print("Password: ")
|
||||
var passwd string
|
||||
_, err = fmt.Scanln(&passwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read password: %w", err)
|
||||
}
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(passwd), 12)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
gmx.Config.Web.PasswordHash = string(hash)
|
||||
changed = true
|
||||
}
|
||||
if changed {
|
||||
err = gmx.SaveConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save config: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) SaveConfig() error {
|
||||
file, err := os.OpenFile(filepath.Join(gmx.ConfigDir, "config.yaml"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return yaml.NewEncoder(file).Encode(&gmx.Config)
|
||||
}
|
5
go.mod
5
go.mod
|
@ -8,8 +8,10 @@ require (
|
|||
github.com/coder/websocket v1.8.12
|
||||
github.com/mattn/go-sqlite3 v1.14.23
|
||||
github.com/rs/zerolog v1.33.0
|
||||
go.mau.fi/util v0.8.1-0.20241007163534-d608488d5cff
|
||||
go.mau.fi/util v0.8.1-0.20241011175353-d86e4c6b8fa7
|
||||
go.mau.fi/zeroconfig v0.1.3
|
||||
golang.org/x/crypto v0.27.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
maunium.net/go/mauflag v1.0.0
|
||||
maunium.net/go/mautrix v0.21.1-0.20241010172818-50f4a2eec193
|
||||
)
|
||||
|
@ -25,7 +27,6 @@ require (
|
|||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -37,8 +37,8 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
|||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
go.mau.fi/util v0.8.1-0.20241007163534-d608488d5cff h1:oQJHc5pdU9Xc6mhvfxSdWXoTreFYvlkM7g65MJWhR40=
|
||||
go.mau.fi/util v0.8.1-0.20241007163534-d608488d5cff/go.mod h1:L9qnqEkhe4KpuYmILrdttKTXL79MwGLyJ4EOskWxO3I=
|
||||
go.mau.fi/util v0.8.1-0.20241011175353-d86e4c6b8fa7 h1:BmQx/qeiR84yGDCir8QL5BtW8XLV7NNDjb44SWnPOOw=
|
||||
go.mau.fi/util v0.8.1-0.20241011175353-d86e4c6b8fa7/go.mod h1:L9qnqEkhe4KpuYmILrdttKTXL79MwGLyJ4EOskWxO3I=
|
||||
go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM=
|
||||
go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
|
@ -52,6 +52,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
51
gomuks.go
51
gomuks.go
|
@ -18,9 +18,7 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"maps"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -33,17 +31,10 @@ import (
|
|||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
"go.mau.fi/util/dbutil"
|
||||
"go.mau.fi/util/exerrors"
|
||||
"go.mau.fi/util/exhttp"
|
||||
"go.mau.fi/util/exzerolog"
|
||||
"go.mau.fi/util/ptr"
|
||||
"go.mau.fi/util/requestlog"
|
||||
"go.mau.fi/zeroconfig"
|
||||
"maunium.net/go/mautrix/hicli"
|
||||
|
||||
"go.mau.fi/gomuks/web"
|
||||
)
|
||||
|
||||
type Gomuks struct {
|
||||
|
@ -57,6 +48,8 @@ type Gomuks struct {
|
|||
TempDir string
|
||||
LogDir string
|
||||
|
||||
Config Config
|
||||
|
||||
stopOnce sync.Once
|
||||
stopChan chan struct{}
|
||||
|
||||
|
@ -74,7 +67,7 @@ func NewGomuks() *Gomuks {
|
|||
}
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) LoadConfig() {
|
||||
func (gmx *Gomuks) InitDirectories() {
|
||||
// We need 4 directories: config, data, cache, logs
|
||||
//
|
||||
// 1. If GOMUKS_ROOT is set, all directories are created under that.
|
||||
|
@ -146,46 +139,10 @@ func (gmx *Gomuks) LoadConfig() {
|
|||
}
|
||||
|
||||
func (gmx *Gomuks) SetupLog() {
|
||||
gmx.Log = exerrors.Must((&zeroconfig.Config{
|
||||
MinLevel: ptr.Ptr(zerolog.TraceLevel),
|
||||
Writers: []zeroconfig.WriterConfig{{
|
||||
Type: zeroconfig.WriterTypeStdout,
|
||||
Format: zeroconfig.LogFormatPrettyColored,
|
||||
}},
|
||||
}).Compile())
|
||||
gmx.Log = exerrors.Must(gmx.Config.Logging.Compile())
|
||||
exzerolog.SetupDefaults(gmx.Log)
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) StartServer(addr string) {
|
||||
api := http.NewServeMux()
|
||||
api.HandleFunc("GET /websocket", gmx.HandleWebsocket)
|
||||
api.HandleFunc("GET /media/{server}/{media_id}", gmx.DownloadMedia)
|
||||
apiHandler := exhttp.ApplyMiddleware(
|
||||
http.StripPrefix("/_gomuks", api),
|
||||
hlog.NewHandler(*gmx.Log),
|
||||
hlog.RequestIDHandler("request_id", "Request-ID"),
|
||||
requestlog.AccessLogger(false),
|
||||
)
|
||||
router := http.NewServeMux()
|
||||
router.Handle("/_gomuks/", apiHandler)
|
||||
if frontend, err := fs.Sub(web.Frontend, "dist"); err != nil {
|
||||
gmx.Log.Warn().Msg("Frontend not found")
|
||||
} else {
|
||||
router.Handle("/", http.FileServerFS(frontend))
|
||||
}
|
||||
gmx.Server = &http.Server{
|
||||
Addr: addr,
|
||||
Handler: router,
|
||||
}
|
||||
go func() {
|
||||
err := gmx.Server.ListenAndServe()
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
gmx.Log.Info().Str("address", addr).Msg("Server started")
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) StartClient() {
|
||||
rawDB, err := dbutil.NewFromConfig("gomuks", dbutil.Config{
|
||||
PoolConfig: dbutil.PoolConfig{
|
||||
|
|
10
main.go
10
main.go
|
@ -48,7 +48,6 @@ var (
|
|||
|
||||
var wantHelp, _ = flag.MakeHelpFlag()
|
||||
var version = flag.MakeFull("v", "version", "View gomuks version and quit.", "false").Bool()
|
||||
var listenAddress = flag.MakeFull("l", "listen", "Address to listen on.", "localhost:29325").String()
|
||||
|
||||
func main() {
|
||||
hicli.InitialDeviceDisplayName = "gomuks web"
|
||||
|
@ -72,14 +71,19 @@ func main() {
|
|||
}
|
||||
|
||||
gmx := NewGomuks()
|
||||
gmx.LoadConfig()
|
||||
gmx.InitDirectories()
|
||||
err = gmx.LoadConfig()
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintln(os.Stderr, "Failed to load config:", err)
|
||||
os.Exit(9)
|
||||
}
|
||||
gmx.SetupLog()
|
||||
gmx.Log.Info().
|
||||
Str("version", Version).
|
||||
Str("go_version", runtime.Version()).
|
||||
Time("built_at", ParsedBuildTime).
|
||||
Msg("Initializing gomuks")
|
||||
gmx.StartServer(*listenAddress)
|
||||
gmx.StartServer()
|
||||
gmx.StartClient()
|
||||
gmx.Log.Info().Msg("Initialization complete")
|
||||
gmx.WaitForInterrupt()
|
||||
|
|
181
server.go
Normal file
181
server.go
Normal file
|
@ -0,0 +1,181 @@
|
|||
// gomuks - A Matrix client written in Go.
|
||||
// Copyright (C) 2024 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/hlog"
|
||||
"go.mau.fi/util/exerrors"
|
||||
"go.mau.fi/util/exhttp"
|
||||
"go.mau.fi/util/jsontime"
|
||||
"go.mau.fi/util/requestlog"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"maunium.net/go/mautrix"
|
||||
|
||||
"go.mau.fi/gomuks/web"
|
||||
)
|
||||
|
||||
func (gmx *Gomuks) StartServer() {
|
||||
api := http.NewServeMux()
|
||||
api.HandleFunc("GET /websocket", gmx.HandleWebsocket)
|
||||
api.HandleFunc("POST /auth", gmx.Authenticate)
|
||||
api.HandleFunc("GET /media/{server}/{media_id}", gmx.DownloadMedia)
|
||||
apiHandler := exhttp.ApplyMiddleware(
|
||||
api,
|
||||
hlog.NewHandler(*gmx.Log),
|
||||
hlog.RequestIDHandler("request_id", "Request-ID"),
|
||||
requestlog.AccessLogger(false),
|
||||
exhttp.StripPrefix("/_gomuks"),
|
||||
gmx.AuthMiddleware,
|
||||
)
|
||||
router := http.NewServeMux()
|
||||
router.Handle("/_gomuks/", apiHandler)
|
||||
if frontend, err := fs.Sub(web.Frontend, "dist"); err != nil {
|
||||
gmx.Log.Warn().Msg("Frontend not found")
|
||||
} else {
|
||||
router.Handle("/", http.FileServerFS(frontend))
|
||||
}
|
||||
gmx.Server = &http.Server{
|
||||
Addr: gmx.Config.Web.ListenAddress,
|
||||
Handler: router,
|
||||
}
|
||||
go func() {
|
||||
err := gmx.Server.ListenAndServe()
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
gmx.Log.Info().Str("address", gmx.Config.Web.ListenAddress).Msg("Server started")
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidHeader = mautrix.RespError{ErrCode: "FI.MAU.GOMUKS.INVALID_HEADER", StatusCode: http.StatusBadRequest}
|
||||
ErrMissingCookie = mautrix.RespError{ErrCode: "FI.MAU.GOMUKS.MISSING_COOKIE", Err: "Missing gomuks_auth cookie", StatusCode: http.StatusUnauthorized}
|
||||
ErrInvalidCookie = mautrix.RespError{ErrCode: "FI.MAU.GOMUKS.INVALID_COOKIE", Err: "Invalid gomuks_auth cookie", StatusCode: http.StatusUnauthorized}
|
||||
)
|
||||
|
||||
type tokenData struct {
|
||||
Username string `json:"username"`
|
||||
Expiry jsontime.Unix `json:"expiry"`
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) validateAuth(token string) bool {
|
||||
if len(token) > 100 {
|
||||
return false
|
||||
}
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 2 {
|
||||
return false
|
||||
}
|
||||
rawJSON, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
checksum, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
hasher := hmac.New(sha256.New, []byte(gmx.Config.Web.TokenKey))
|
||||
hasher.Write(rawJSON)
|
||||
if !hmac.Equal(hasher.Sum(nil), checksum) {
|
||||
return false
|
||||
}
|
||||
|
||||
var td tokenData
|
||||
err = json.Unmarshal(rawJSON, &td)
|
||||
return err == nil && td.Username == gmx.Config.Web.Username && td.Expiry.After(time.Now())
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) generateToken() (string, time.Time) {
|
||||
expiry := time.Now().Add(7 * 24 * time.Hour)
|
||||
data := exerrors.Must(json.Marshal(tokenData{
|
||||
Username: gmx.Config.Web.Username,
|
||||
Expiry: jsontime.U(expiry),
|
||||
}))
|
||||
hasher := hmac.New(sha256.New, []byte(gmx.Config.Web.TokenKey))
|
||||
hasher.Write(data)
|
||||
checksum := hasher.Sum(nil)
|
||||
return base64.RawURLEncoding.EncodeToString(data) + "." + base64.RawURLEncoding.EncodeToString(checksum), expiry
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) writeTokenCookie(w http.ResponseWriter) {
|
||||
token, expiry := gmx.generateToken()
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "gomuks_auth",
|
||||
Value: token,
|
||||
Expires: expiry,
|
||||
HttpOnly: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) Authenticate(w http.ResponseWriter, r *http.Request) {
|
||||
authCookie, err := r.Cookie("gomuks_auth")
|
||||
if err == nil && gmx.validateAuth(authCookie.Value) {
|
||||
gmx.writeTokenCookie(w)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else if username, password, ok := r.BasicAuth(); !ok {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="gomuks web" charset="UTF-8"`)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
} else {
|
||||
usernameHash := sha256.Sum256([]byte(username))
|
||||
expectedUsernameHash := sha256.Sum256([]byte(gmx.Config.Web.Username))
|
||||
usernameCorrect := hmac.Equal(usernameHash[:], expectedUsernameHash[:])
|
||||
passwordCorrect := bcrypt.CompareHashAndPassword([]byte(gmx.Config.Web.PasswordHash), []byte(password)) == nil
|
||||
if usernameCorrect && passwordCorrect {
|
||||
gmx.writeTokenCookie(w)
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isUserFetch(header http.Header) bool {
|
||||
return header.Get("Sec-Fetch-Site") == "none" &&
|
||||
header.Get("Sec-Fetch-Mode") == "navigate" &&
|
||||
header.Get("Sec-Fetch-Dest") == "document" &&
|
||||
header.Get("Sec-Fetch-User") == "?1"
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) AuthMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Sec-Fetch-Site") != "same-origin" && !isUserFetch(r.Header) {
|
||||
ErrInvalidHeader.WithMessage("Invalid Sec-Fetch-Site header").Write(w)
|
||||
return
|
||||
}
|
||||
if r.URL.Path != "/auth" {
|
||||
authCookie, err := r.Cookie("gomuks_auth")
|
||||
if err != nil {
|
||||
ErrMissingCookie.Write(w)
|
||||
return
|
||||
} else if !gmx.validateAuth(authCookie.Value) {
|
||||
ErrInvalidCookie.Write(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
|
@ -18,8 +18,16 @@ import { createRoot } from "react-dom/client"
|
|||
import App from "./App.tsx"
|
||||
import "./index.css"
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<App/>
|
||||
</StrictMode>,
|
||||
)
|
||||
fetch("/_gomuks/auth", { method: "POST" }).then(resp => {
|
||||
if (resp.ok) {
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<App/>
|
||||
</StrictMode>,
|
||||
)
|
||||
} else {
|
||||
window.alert("Authentication failed: " + resp.statusText)
|
||||
}
|
||||
}, err => {
|
||||
window.alert("Authentication failed: " + err)
|
||||
})
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/hicli"
|
||||
"maunium.net/go/mautrix/hicli/database"
|
||||
|
@ -49,6 +48,10 @@ func writeCmd(ctx context.Context, conn *websocket.Conn, cmd *hicli.JSONCommand)
|
|||
const StatusEventsStuck = 4001
|
||||
|
||||
func (gmx *Gomuks) HandleWebsocket(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Sec-Fetch-Mode") != "websocket" {
|
||||
ErrInvalidHeader.WithMessage("Invalid Sec-Fetch-Dest header").Write(w)
|
||||
return
|
||||
}
|
||||
var conn *websocket.Conn
|
||||
log := zerolog.Ctx(r.Context())
|
||||
recoverPanic := func(context string) bool {
|
||||
|
|
Loading…
Add table
Reference in a new issue