feat: initial sharing impl

This commit is contained in:
2025-12-27 19:27:08 +00:00
parent 94458c2f1e
commit 1a1fc4743a
23 changed files with 4019 additions and 1232 deletions

View File

@@ -7,6 +7,7 @@ import (
"github.com/get-drexa/drexa/internal/httperr"
"github.com/get-drexa/drexa/internal/reqctx"
"github.com/get-drexa/drexa/internal/user"
"github.com/get-drexa/drexa/internal/virtualfs"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/uptrace/bun"
@@ -15,6 +16,7 @@ import (
type HTTPHandler struct {
accountService *Service
authService *auth.Service
vfs *virtualfs.VirtualFS
db *bun.DB
authMiddleware fiber.Handler
cookieConfig auth.CookieConfig
@@ -46,17 +48,11 @@ type registerAccountResponse struct {
RefreshToken string `json:"refreshToken,omitempty" example:"dR4nD0mUu1DkZXlCeXRlc0FuZFJhbmRvbURhdGFIZXJlMTIzNDU2Nzg5MGFi"`
}
const currentAccountKey = "currentAccount"
func CurrentAccount(c *fiber.Ctx) *Account {
return c.Locals(currentAccountKey).(*Account)
}
func NewHTTPHandler(accountService *Service, authService *auth.Service, db *bun.DB, authMiddleware fiber.Handler, cookieConfig auth.CookieConfig) *HTTPHandler {
func NewHTTPHandler(accountService *Service, authService *auth.Service, vfs *virtualfs.VirtualFS, db *bun.DB, authMiddleware fiber.Handler, cookieConfig auth.CookieConfig) *HTTPHandler {
return &HTTPHandler{accountService: accountService, authService: authService, db: db, authMiddleware: authMiddleware, cookieConfig: cookieConfig}
}
func (h *HTTPHandler) RegisterRoutes(api fiber.Router) fiber.Router {
func (h *HTTPHandler) RegisterRoutes(api fiber.Router) *ScopedRouter {
api.Get("/accounts", h.authMiddleware, h.listAccounts)
api.Post("/accounts", h.registerAccount)
@@ -66,7 +62,7 @@ func (h *HTTPHandler) RegisterRoutes(api fiber.Router) fiber.Router {
account.Get("/", h.getAccount)
return account
return &ScopedRouter{virtualfs.ScopedRouter{account}}
}
func (h *HTTPHandler) accountMiddleware(c *fiber.Ctx) error {
@@ -85,7 +81,22 @@ func (h *HTTPHandler) accountMiddleware(c *fiber.Ctx) error {
return httperr.Internal(err)
}
c.Locals(currentAccountKey, account)
root, err := h.vfs.FindRootDirectory(c.Context(), h.db, account.ID)
if err != nil {
return httperr.Internal(err)
}
scope := &virtualfs.Scope{
AccountID: account.ID,
RootNodeID: root.ID,
AllowedOps: virtualfs.AllAllowedOps,
AllowedNodes: nil,
ActorKind: virtualfs.ScopeActorAccount,
ActorID: u.ID,
}
reqctx.SetVFSAccessScope(c, scope)
reqctx.SetCurrentAccount(c, account)
return c.Next()
}
@@ -120,8 +131,8 @@ func (h *HTTPHandler) listAccounts(c *fiber.Ctx) error {
// @Failure 404 {string} string "Account not found"
// @Router /accounts/{accountID} [get]
func (h *HTTPHandler) getAccount(c *fiber.Ctx) error {
account := CurrentAccount(c)
if account == nil {
account, ok := reqctx.CurrentAccount(c).(*Account)
if !ok || account == nil {
return c.SendStatus(fiber.StatusNotFound)
}
return c.JSON(account)

View File

@@ -0,0 +1,21 @@
package account
import "github.com/get-drexa/drexa/internal/virtualfs"
// ScopedRouter is a router with auth + account middleware applied.
// Routes registered on this router have access to:
// - The authenticated user via reqctx.AuthenticatedUser()
// - The current account via reqctx.CurrentAccount()
// - The VFS scope via reqctx.VFSAccessScope()
//
// This embeds virtualfs.ScopedRouter, so it can be passed to functions
// that only require VFS scope by calling VFSRouter().
type ScopedRouter struct {
virtualfs.ScopedRouter
}
// VFSRouter returns the embedded virtualfs.ScopedRouter for use with
// functions that only require VFS scope access.
func (r *ScopedRouter) VFSRouter() *virtualfs.ScopedRouter {
return &r.ScopedRouter
}

View File

@@ -59,7 +59,7 @@ func (s *Service) Register(ctx context.Context, db bun.IDB, opts RegisterOptions
return nil, nil, err
}
_, err = s.vfs.CreateDirectory(ctx, db, acc.ID, uuid.Nil, virtualfs.RootDirectoryName)
_, err = s.vfs.CreateRootDirectory(ctx, db, acc.ID)
if err != nil {
return nil, nil, err
}