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:
nemunaire 2017-11-27 02:45:33 +01:00 committed by Pierre-Olivier Mercier
parent 6237f7755a
commit 8f7de926d3
7 changed files with 281 additions and 160 deletions

98
admin/sync/file.go Normal file
View 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)
}

View 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
}
}

View 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
}
}