mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #44330, Resolves #44331 # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] Added/updated automated tests. (I'd defer integration tests to a separate PR since this one is pretty large already.) - [x] QA'd all new/changed functionality manually. I've tested this on both the setup flow and the organization settings page. I haven't had the time to test this on other places where we render the logo (macOS setup experience / MDM migration dialog). https://github.com/user-attachments/assets/95d4eae5-3da6-40f4-98a1-8575b97d96b3 ## New Fleet configuration settings - [x] Setting(s) is/are explicitly excluded from GitOps. Will handle GitOps in a separate PR. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Organizations can upload custom logos for light and dark modes. * Registration and Org Settings support logo file upload, preview, per-mode replace/delete, and validation (size & image formats). * Activity feed records logo changes/deletions; site nav displays uploaded logos per theme. * File uploader/preview adds a Fleet logo graphic option and improved logo validation. * Config/GitOps outputs now include separate dark/light logo fields. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
package filesystem
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
)
|
|
|
|
const orgLogosPrefix = "org-logos"
|
|
|
|
type orgLogoNotFoundError struct{}
|
|
|
|
var _ fleet.NotFoundError = (*orgLogoNotFoundError)(nil)
|
|
|
|
func (orgLogoNotFoundError) Error() string { return "org logo not found" }
|
|
func (orgLogoNotFoundError) IsNotFound() bool { return true }
|
|
|
|
type OrgLogoStore struct {
|
|
rootDir string
|
|
}
|
|
|
|
func NewOrgLogoStore(rootDir string) (*OrgLogoStore, error) {
|
|
dir := filepath.Join(rootDir, orgLogosPrefix)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return nil, err
|
|
}
|
|
return &OrgLogoStore{rootDir: rootDir}, nil
|
|
}
|
|
|
|
func (s *OrgLogoStore) pathFor(mode fleet.OrgLogoMode) string {
|
|
return filepath.Join(s.rootDir, orgLogosPrefix, string(mode))
|
|
}
|
|
|
|
func (s *OrgLogoStore) Get(ctx context.Context, mode fleet.OrgLogoMode) (io.ReadCloser, int64, error) {
|
|
p := s.pathFor(mode)
|
|
f, err := os.Open(p)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, 0, orgLogoNotFoundError{}
|
|
}
|
|
return nil, 0, ctxerr.Wrap(ctx, err, "open org logo")
|
|
}
|
|
st, err := f.Stat()
|
|
if err != nil {
|
|
_ = f.Close()
|
|
return nil, 0, ctxerr.Wrap(ctx, err, "stat org logo")
|
|
}
|
|
return f, st.Size(), nil
|
|
}
|
|
|
|
func (s *OrgLogoStore) Put(ctx context.Context, mode fleet.OrgLogoMode, content io.ReadSeeker) error {
|
|
p := s.pathFor(mode)
|
|
f, err := os.Create(p)
|
|
if err != nil {
|
|
return ctxerr.Wrap(ctx, err, "create org logo file")
|
|
}
|
|
defer f.Close()
|
|
if _, err := io.Copy(f, content); err != nil {
|
|
return ctxerr.Wrap(ctx, err, "write org logo file")
|
|
}
|
|
if err := f.Close(); err != nil {
|
|
return ctxerr.Wrap(ctx, err, "close org logo file")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *OrgLogoStore) Delete(ctx context.Context, mode fleet.OrgLogoMode) error {
|
|
p := s.pathFor(mode)
|
|
if err := os.Remove(p); err != nil && !os.IsNotExist(err) {
|
|
return ctxerr.Wrap(ctx, err, "remove org logo file")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *OrgLogoStore) Exists(ctx context.Context, mode fleet.OrgLogoMode) (bool, error) {
|
|
p := s.pathFor(mode)
|
|
if _, err := os.Stat(p); err != nil {
|
|
if os.IsNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, ctxerr.Wrap(ctx, err, "stat org logo")
|
|
}
|
|
return true, nil
|
|
}
|