mirror of https://github.com/jlelse/GoBlog
Simple blogging system written in Go
https://goblog.app
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
5.6 KiB
251 lines
5.6 KiB
package main |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"io" |
|
"os" |
|
"path/filepath" |
|
"strings" |
|
"time" |
|
|
|
"github.com/jlaffaye/ftp" |
|
) |
|
|
|
func (a *goBlog) initMediaStorage() { |
|
a.mediaStorageInit.Do(func() { |
|
type initFunc func() mediaStorage |
|
for _, fc := range []initFunc{a.initBunnyCdnMediaStorage, a.initFtpMediaStorage, a.initLocalMediaStorage} { |
|
a.mediaStorage = fc() |
|
if a.mediaStorage != nil { |
|
break |
|
} |
|
} |
|
}) |
|
} |
|
|
|
func (a *goBlog) mediaStorageEnabled() bool { |
|
a.initMediaStorage() |
|
return a.mediaStorage != nil |
|
} |
|
|
|
type mediaStorageSaveFunc func(filename string, file io.Reader) (location string, err error) |
|
|
|
func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) { |
|
a.initMediaStorage() |
|
if a.mediaStorage == nil { |
|
return "", errors.New("no media storage configured") |
|
} |
|
loc, err := a.mediaStorage.save(filename, f) |
|
if err != nil { |
|
return "", err |
|
} |
|
return a.getFullAddress(loc), nil |
|
} |
|
|
|
func (a *goBlog) deleteMediaFile(filename string) error { |
|
a.initMediaStorage() |
|
if a.mediaStorage == nil { |
|
return errors.New("no media storage configured") |
|
} |
|
return a.mediaStorage.delete(filepath.Base(filename)) |
|
} |
|
|
|
type mediaFile struct { |
|
Name string |
|
Location string |
|
Time time.Time |
|
Size int64 |
|
} |
|
|
|
func (a *goBlog) mediaFiles() ([]*mediaFile, error) { |
|
a.initMediaStorage() |
|
if a.mediaStorage == nil { |
|
return nil, errors.New("no media storage configured") |
|
} |
|
return a.mediaStorage.files() |
|
} |
|
|
|
func (a *goBlog) mediaFileLocation(name string) string { |
|
a.initMediaStorage() |
|
if a.mediaStorage == nil { |
|
return "" |
|
} |
|
return a.mediaStorage.location(name) |
|
} |
|
|
|
type mediaStorage interface { |
|
save(filename string, file io.Reader) (location string, err error) |
|
delete(filename string) (err error) |
|
files() (files []*mediaFile, err error) |
|
location(filename string) (location string) |
|
} |
|
|
|
type localMediaStorage struct { |
|
mediaURL string // optional |
|
path string // required |
|
} |
|
|
|
func (a *goBlog) initLocalMediaStorage() mediaStorage { |
|
ms := &localMediaStorage{ |
|
path: mediaFilePath, |
|
} |
|
if config := a.cfg.Micropub.MediaStorage; config != nil && config.MediaURL != "" { |
|
ms.mediaURL = config.MediaURL |
|
} |
|
return ms |
|
} |
|
|
|
func (l *localMediaStorage) save(filename string, file io.Reader) (location string, err error) { |
|
if err = saveToFile(file, filepath.Join(l.path, filename)); err != nil { |
|
return "", err |
|
} |
|
return l.location(filename), nil |
|
} |
|
|
|
func (l *localMediaStorage) delete(filename string) (err error) { |
|
if err = os.MkdirAll(l.path, 0777); err != nil { |
|
return err |
|
} |
|
return os.Remove(filepath.Join(l.path, filename)) |
|
} |
|
|
|
func (l *localMediaStorage) files() (files []*mediaFile, err error) { |
|
if err = os.MkdirAll(l.path, 0777); err != nil { |
|
return nil, err |
|
} |
|
entries, err := os.ReadDir(l.path) |
|
if err != nil { |
|
return nil, err |
|
} |
|
for _, e := range entries { |
|
fi, er := e.Info() |
|
if er != nil { |
|
continue |
|
} |
|
if fi.Mode().IsRegular() { |
|
files = append(files, &mediaFile{ |
|
Name: fi.Name(), |
|
Location: l.location(fi.Name()), |
|
Time: fi.ModTime(), |
|
Size: int64(fi.Size()), |
|
}) |
|
} |
|
} |
|
return files, nil |
|
} |
|
|
|
func (l *localMediaStorage) location(name string) string { |
|
if l.mediaURL != "" { |
|
return fmt.Sprintf("%s/%s", l.mediaURL, name) |
|
} |
|
return fmt.Sprintf("/m/%s", name) |
|
} |
|
|
|
func (a *goBlog) initBunnyCdnMediaStorage() mediaStorage { |
|
config := a.cfg.Micropub.MediaStorage |
|
if config == nil || config.BunnyStorageName == "" || config.BunnyStorageKey == "" || config.MediaURL == "" { |
|
return nil |
|
} |
|
address := "storage.bunnycdn.com:21" |
|
if config.BunnyStorageRegion != "" { |
|
address = fmt.Sprintf("%s.%s", strings.ToLower(config.BunnyStorageRegion), address) |
|
} |
|
return &ftpMediaStorage{ |
|
address: address, |
|
user: config.BunnyStorageName, |
|
password: config.BunnyStorageKey, |
|
mediaURL: config.MediaURL, |
|
} |
|
} |
|
|
|
type ftpMediaStorage struct { |
|
address string // required |
|
user string // required |
|
password string // required |
|
mediaURL string // required |
|
} |
|
|
|
func (a *goBlog) initFtpMediaStorage() mediaStorage { |
|
config := a.cfg.Micropub.MediaStorage |
|
if config == nil || config.FTPAddress == "" || config.FTPUser == "" || config.FTPPassword == "" { |
|
return nil |
|
} |
|
return &ftpMediaStorage{ |
|
address: config.FTPAddress, |
|
user: config.FTPUser, |
|
password: config.FTPPassword, |
|
mediaURL: config.MediaURL, |
|
} |
|
} |
|
|
|
func (f *ftpMediaStorage) save(filename string, file io.Reader) (location string, err error) { |
|
c, err := f.connection() |
|
defer func() { |
|
if c != nil { |
|
_ = c.Quit() |
|
} |
|
}() |
|
if err != nil { |
|
return "", err |
|
} |
|
if err = c.Stor(filename, file); err != nil { |
|
return "", err |
|
} |
|
return f.location(filename), nil |
|
} |
|
|
|
func (f *ftpMediaStorage) delete(filename string) (err error) { |
|
c, err := f.connection() |
|
defer func() { |
|
if c != nil { |
|
_ = c.Quit() |
|
} |
|
}() |
|
if err != nil { |
|
return err |
|
} |
|
return c.Delete(filename) |
|
} |
|
|
|
func (f *ftpMediaStorage) files() (files []*mediaFile, err error) { |
|
c, err := f.connection() |
|
defer func() { |
|
if c != nil { |
|
_ = c.Quit() |
|
} |
|
}() |
|
if err != nil { |
|
return nil, err |
|
} |
|
w := c.Walk("") |
|
for w.Next() { |
|
if s := w.Stat(); s.Type == ftp.EntryTypeFile { |
|
files = append(files, &mediaFile{ |
|
Name: s.Name, |
|
Location: f.location(s.Name), |
|
Time: s.Time, |
|
Size: int64(s.Size), |
|
}) |
|
} |
|
} |
|
return files, nil |
|
} |
|
|
|
func (f *ftpMediaStorage) location(name string) string { |
|
return fmt.Sprintf("%s/%s", f.mediaURL, name) |
|
} |
|
|
|
func (f *ftpMediaStorage) connection() (*ftp.ServerConn, error) { |
|
if f.address == "" || f.user == "" || f.password == "" { |
|
return nil, errors.New("missing FTP config") |
|
} |
|
c, err := ftp.Dial(f.address, ftp.DialWithTimeout(5*time.Second)) |
|
if err != nil { |
|
return nil, err |
|
} |
|
if err = c.Login(f.user, f.password); err != nil { |
|
return nil, err |
|
} |
|
return c, nil |
|
}
|
|
|