admin: Implement sychronization backends
We are now able, depending on configuration, to retrieve files from either WebDAV or local file system.
This commit is contained in:
parent
6237f7755a
commit
8f7de926d3
7 changed files with 281 additions and 160 deletions
98
admin/sync/file.go
Normal file
98
admin/sync/file.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base32"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/dchest/blake2b"
|
||||
)
|
||||
|
||||
type Importer interface {
|
||||
Kind() string
|
||||
exists(filename string) bool
|
||||
toURL(filename string) string
|
||||
getFile(filename string, writer *bufio.Writer) error
|
||||
listDir(filename string) ([]string, error)
|
||||
}
|
||||
|
||||
var GlobalImporter Importer
|
||||
|
||||
func getFile(i Importer, URI string, writer *bufio.Writer) error {
|
||||
// Import file if it exists
|
||||
if i.exists(URI) {
|
||||
return i.getFile(URI, writer)
|
||||
}
|
||||
|
||||
// Try to find file parts
|
||||
dirname := path.Dir(URI)
|
||||
if i.exists(dirname) {
|
||||
filename := path.Base(URI)
|
||||
if files, err := i.listDir(dirname); err != nil {
|
||||
return err
|
||||
} else {
|
||||
for _, fname := range []string{filename, filename + "."} {
|
||||
found := false
|
||||
for _, file := range files {
|
||||
if matched, _ := path.Match(fname + "[0-9][0-9]", file); matched {
|
||||
found = true
|
||||
if err := i.getFile(path.Join(dirname, file), writer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New(fmt.Sprintf("%q: no such file or directory", i.toURL(URI)))
|
||||
}
|
||||
|
||||
func ImportFile(URI string, next func(string, string) (interface{}, error)) (interface{}, error) {
|
||||
hash := blake2b.Sum512([]byte(URI))
|
||||
dest := path.Join(fic.FilesDir, strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(hash[:])), path.Base(URI))
|
||||
|
||||
// If the present file is still valide, don't erase it
|
||||
if _, err := os.Stat(dest); !os.IsNotExist(err) {
|
||||
if r, err := next(dest, URI); err == nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
if err := os.Remove(dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure no more file is registered with this path
|
||||
if f, err := fic.GetFileByPath(dest); err == nil {
|
||||
f.Delete()
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(path.Dir(dest), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Write file
|
||||
if fdto, err := os.Create(dest); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer fdto.Close()
|
||||
writer := bufio.NewWriter(fdto)
|
||||
if err := getFile(GlobalImporter, URI, writer); err != nil {
|
||||
os.Remove(dest)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return next(dest, URI)
|
||||
}
|
96
admin/sync/importer_cloud.go
Normal file
96
admin/sync/importer_cloud.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/studio-b12/gowebdav"
|
||||
)
|
||||
|
||||
var CloudDAVBase string = "https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2018"
|
||||
var CloudUsername string = "fic"
|
||||
var CloudPassword string = ""
|
||||
|
||||
type CloudImporter struct {
|
||||
baseDAV url.URL
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
func NewCloudImporter(baseDAV string, username string, password string) (*CloudImporter, error) {
|
||||
if r, err := url.Parse(baseDAV); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &CloudImporter{*r, username, password}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i CloudImporter) Kind() string {
|
||||
return "cloud file importer: " + i.baseDAV.String()
|
||||
}
|
||||
|
||||
func (i CloudImporter) exists(filename string) bool {
|
||||
fullURL := i.baseDAV
|
||||
fullURL.Path = path.Join(fullURL.Path, filename)
|
||||
|
||||
client := http.Client{}
|
||||
if req, err := http.NewRequest("HEAD", fullURL.String(), nil); err == nil {
|
||||
req.SetBasicAuth(i.username, i.password)
|
||||
|
||||
if resp, err := client.Do(req); err == nil {
|
||||
defer resp.Body.Close()
|
||||
|
||||
return resp.StatusCode == http.StatusOK
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (i CloudImporter) toURL(filename string) string {
|
||||
fullURL := i.baseDAV
|
||||
fullURL.Path = path.Join(fullURL.Path, filename)
|
||||
return fullURL.String()
|
||||
}
|
||||
|
||||
func (i CloudImporter) getFile(filename string, writer *bufio.Writer) error {
|
||||
fullURL := i.baseDAV
|
||||
fullURL.Path = path.Join(fullURL.Path, filename)
|
||||
|
||||
client := http.Client{}
|
||||
if req, err := http.NewRequest("GET", fullURL.String(), nil); err != nil {
|
||||
return err
|
||||
} else {
|
||||
req.SetBasicAuth(i.username, i.password)
|
||||
if resp, err := client.Do(req); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(resp.Status)
|
||||
} else {
|
||||
reader := bufio.NewReader(resp.Body)
|
||||
reader.WriteTo(writer)
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i CloudImporter) listDir(filename string) ([]string, error) {
|
||||
client := gowebdav.NewClient(i.baseDAV.String(), i.username, i.password)
|
||||
|
||||
if files, err := client.ReadDir(filename); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
res := make([]string, 0)
|
||||
for _, file := range files {
|
||||
res = append(res, file.Name())
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
}
|
49
admin/sync/importer_localfs.go
Normal file
49
admin/sync/importer_localfs.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
type LocalImporter struct {
|
||||
Base string
|
||||
}
|
||||
|
||||
func (i LocalImporter) Kind() string {
|
||||
return "local file importer: " + i.Base
|
||||
}
|
||||
|
||||
func (i LocalImporter) exists(filename string) bool {
|
||||
_, err := os.Stat(path.Join(i.Base, filename))
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func (i LocalImporter) toURL(filename string) string {
|
||||
return path.Join(i.Base, filename)
|
||||
}
|
||||
|
||||
func (i LocalImporter) getFile(filename string, writer *bufio.Writer) error {
|
||||
if fd, err := os.Open(path.Join(i.Base, filename)); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer fd.Close()
|
||||
reader := bufio.NewReader(fd)
|
||||
reader.WriteTo(writer)
|
||||
writer.Flush()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i LocalImporter) listDir(filename string) ([]string, error) {
|
||||
if files, err := ioutil.ReadDir(path.Join(i.Base, filename)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
res := make([]string, 0)
|
||||
for _, file := range files {
|
||||
res = append(res, file.Name())
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
}
|
Reference in a new issue