Returns every character image with when it was last changed, in Unix seconds, so clients can cache the images and download only the ones that changed.
91 lines
2.3 KiB
Go
91 lines
2.3 KiB
Go
package backend
|
|
|
|
import (
|
|
"errors"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// CharacterService serves character images from a single directory.
|
|
type CharacterService struct {
|
|
root *os.Root
|
|
}
|
|
|
|
// NewCharacterService opens the characters directory. Files outside it can never be served.
|
|
func NewCharacterService(path string) (*CharacterService, error) {
|
|
root, err := os.OpenRoot(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &CharacterService{root: root}, nil
|
|
}
|
|
|
|
// List returns the file names of all character images.
|
|
func (c *CharacterService) List() ([]string, error) {
|
|
entries, err := fs.ReadDir(c.root.FS(), ".")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
characters := []string{}
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() && isImage(entry.Name()) {
|
|
characters = append(characters, entry.Name())
|
|
}
|
|
}
|
|
return characters, nil
|
|
}
|
|
|
|
// CharacterDetails is a character image with the time its file was last changed, so a
|
|
// client that caches images knows when to download one again.
|
|
type CharacterDetails struct {
|
|
Name string `json:"name" doc:"File name of the character image"`
|
|
Modified int64 `json:"modified" doc:"When the image was last changed, in Unix seconds"`
|
|
}
|
|
|
|
// ListDetails returns all character images with the time each was last changed.
|
|
func (c *CharacterService) ListDetails() ([]CharacterDetails, error) {
|
|
entries, err := fs.ReadDir(c.root.FS(), ".")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
characters := []CharacterDetails{}
|
|
for _, entry := range entries {
|
|
if entry.IsDir() || !isImage(entry.Name()) {
|
|
continue
|
|
}
|
|
info, err := entry.Info()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
characters = append(characters, CharacterDetails{Name: entry.Name(), Modified: info.ModTime().Unix()})
|
|
}
|
|
return characters, nil
|
|
}
|
|
|
|
// Open opens the character image with the given file name.
|
|
func (c *CharacterService) Open(name string) (*os.File, error) {
|
|
if name == "" || name != filepath.Base(name) || !isImage(name) {
|
|
return nil, ErrInvalidName
|
|
}
|
|
f, err := c.root.Open(name)
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
return nil, ErrNotFound
|
|
}
|
|
return f, err
|
|
}
|
|
|
|
// Close releases the characters directory.
|
|
func (c *CharacterService) Close() error {
|
|
return c.root.Close()
|
|
}
|
|
|
|
func isImage(name string) bool {
|
|
switch strings.ToLower(filepath.Ext(name)) {
|
|
case ".jpg", ".jpeg", ".png":
|
|
return true
|
|
}
|
|
return false
|
|
}
|