mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-14 14:57:27 +00:00
Adds a browser-based file manager so members can use their files without an SSH key. Served on a loopback HTTP server (AGENTBBS_FILES_WEB_ADDR, default 127.0.0.1:8092) that Caddy fronts at files.<host>. Members sign in with their webmail username + password, verified against the Mailu IMAP backend (mailbox.VerifyLogin), and browse the same virtual /me + /public areas as SFTP — no home directory is ever exposed. Upload/download/mkdir/delete with the private-area quota enforced; reuses internal/files confinement (fs.go). setup.sh renders the files.<DOMAIN> Caddy site + env knob. Unit tests cover the auth gate and an upload/list/download/delete round trip. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
60 lines
2 KiB
Go
60 lines
2 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/charmbracelet/log"
|
|
"github.com/profullstack/agentbbs/internal/files"
|
|
"github.com/profullstack/agentbbs/internal/mailbox"
|
|
"github.com/profullstack/agentbbs/internal/store"
|
|
)
|
|
|
|
// startFilesWeb serves the browser-based file manager (files.<host>) on a
|
|
// loopback address Caddy reverse-proxies. Members sign in with their webmail
|
|
// password — no SSH key needed — and browse the same /me and /public areas as
|
|
// SFTP. No-op when Files is disabled (a.files == nil).
|
|
func (a *app) startFilesWeb() {
|
|
if a.files == nil {
|
|
return
|
|
}
|
|
addr := env("AGENTBBS_FILES_WEB_ADDR", "127.0.0.1:8092")
|
|
title := env("AGENTBBS_FILES_WEB_TITLE", "files."+strings.TrimPrefix(a.host, "bbs."))
|
|
h := a.files.WebHandler(files.WebConfig{Authenticate: a.filesWebAuth, Title: title})
|
|
srv := &http.Server{Addr: addr, Handler: h, ReadHeaderTimeout: 10 * time.Second}
|
|
go func() {
|
|
log.Info("files web listening", "addr", addr)
|
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
log.Error("files web", "err", err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// filesWebAuth validates a member's webmail credentials against the Mailu IMAP
|
|
// backend (the same login Roundcube uses), then maps them to the account. The
|
|
// username may be a bare handle or a full address; only the local part matters.
|
|
func (a *app) filesWebAuth(user, pass string) (store.User, bool, error) {
|
|
name := strings.ToLower(strings.TrimSpace(user))
|
|
if at := strings.IndexByte(name, '@'); at >= 0 {
|
|
name = name[:at]
|
|
}
|
|
if name == "" || pass == "" {
|
|
return store.User{}, false, nil
|
|
}
|
|
u, ok, err := a.st.UserByName(name)
|
|
if err != nil {
|
|
return store.User{}, false, err
|
|
}
|
|
if !ok || u.Banned {
|
|
return store.User{}, false, nil
|
|
}
|
|
imapAddr := env("AGENTBBS_MAIL_IMAP_ADDR", a.mailHost+":993")
|
|
plaintext := os.Getenv("AGENTBBS_MAIL_IMAP_PLAINTEXT") == "1"
|
|
if err := mailbox.VerifyLogin(imapAddr, name+"@"+a.mailDomain, pass, plaintext); err != nil {
|
|
return store.User{}, false, nil // credentials rejected
|
|
}
|
|
return u, true, nil
|
|
}
|