mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 22:37:28 +00:00
feat(files): web file browser at files.<host> with webmail-password login
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>
This commit is contained in:
parent
1dbb2c70e2
commit
cddd9819cc
7 changed files with 835 additions and 1 deletions
60
cmd/agentbbs/fileweb.go
Normal file
60
cmd/agentbbs/fileweb.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -258,6 +258,10 @@ func main() {
|
|||
// Caddy proxies wss://host/play to it.
|
||||
go a.serveGameWS(env("AGENTBBS_GAME_WS_ADDR", "127.0.0.1:8090"))
|
||||
|
||||
// Web file browser (files.<host>): webmail-password login over the same
|
||||
// /me + /public storage as SFTP. Loopback; Caddy proxies files.<host> to it.
|
||||
a.startFilesWeb()
|
||||
|
||||
// News (NNTP) server: the members-only Usenet network (docs/news.md). The
|
||||
// loopback plaintext listener backs the in-BBS news@ reader; the public
|
||||
// NNTPS listener (:563, TLS) serves desktop newsreaders and agents. Free for
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue