diff --git a/admin/api/exercice.go b/admin/api/exercice.go
index 681e1e3a..1fb3a562 100644
--- a/admin/api/exercice.go
+++ b/admin/api/exercice.go
@@ -21,8 +21,8 @@ func init() {
router.GET("/api/exercices/:eid/files", apiHandler(exerciceHandler(listExerciceFiles)))
router.POST("/api/exercices/:eid/files", apiHandler(exerciceHandler(createExerciceFile)))
- router.GET("/api/exercices/:eid/files/:fid", apiHandler(fileHandler(showExerciceFile)))
- router.DELETE("/api/exercices/:eid/files/:fid", apiHandler(fileHandler(deleteExerciceFile)))
+ router.GET("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(showExerciceFile)))
+ router.DELETE("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(deleteExerciceFile)))
router.GET("/api/exercices/:eid/hints", apiHandler(exerciceHandler(listExerciceHints)))
router.POST("/api/exercices/:eid/hints", apiHandler(exerciceHandler(createExerciceHint)))
@@ -233,6 +233,11 @@ func deleteExerciceQuiz(quiz fic.MCQ, _ fic.Exercice, _ []byte) (interface{}, er
return quiz.Delete()
}
+type uploadedFile struct {
+ URI string
+ Digest string
+}
+
func createExerciceFile(exercice fic.Exercice, body []byte) (interface{}, error) {
var uf uploadedFile
if err := json.Unmarshal(body, &uf); err != nil {
diff --git a/admin/api/file.go b/admin/api/file.go
index e2179371..b73058f1 100644
--- a/admin/api/file.go
+++ b/admin/api/file.go
@@ -1,8 +1,50 @@
package api
-import ()
+import (
+ "encoding/json"
-type uploadedFile struct {
- URI string
- Digest string
+ "srs.epita.fr/fic-server/libfic"
+
+ "github.com/julienschmidt/httprouter"
+)
+
+func init() {
+ router.GET("/api/files/", apiHandler(listFiles))
+ router.DELETE("/api/files/", apiHandler(clearFiles))
+
+ router.GET("/api/files/:fileid", apiHandler(fileHandler(showFile)))
+ router.PUT("/api/files/:fileid", apiHandler(fileHandler(updateFile)))
+ router.DELETE("/api/files/:fileid", apiHandler(fileHandler(deleteFile)))
+}
+
+func listFiles(_ httprouter.Params, body []byte) (interface{}, error) {
+ // List all files
+ return fic.GetFiles()
+}
+
+func clearFiles(_ httprouter.Params, _ []byte) (interface{}, error) {
+ return fic.ClearFiles()
+}
+
+func showFile(file fic.EFile, _ []byte) (interface{}, error) {
+ return file, nil
+}
+
+func updateFile(file fic.EFile, body []byte) (interface{}, error) {
+ var uf fic.EFile
+ if err := json.Unmarshal(body, &uf); err != nil {
+ return nil, err
+ }
+
+ uf.Id = file.Id
+
+ if _, err := uf.Update(); err != nil {
+ return nil, err
+ } else {
+ return uf, nil
+ }
+}
+
+func deleteFile(file fic.EFile, _ []byte) (interface{}, error) {
+ return file.Delete()
}
diff --git a/admin/api/handlers.go b/admin/api/handlers.go
index 228970bd..49a2ccd8 100644
--- a/admin/api/handlers.go
+++ b/admin/api/handlers.go
@@ -213,7 +213,7 @@ func quizHandler(f func(fic.MCQ,fic.Exercice,[]byte) (interface{}, error)) func
}
}
-func fileHandler(f func(fic.EFile,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
+func exerciceFileHandler(f func(fic.EFile,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
return func (ps httprouter.Params, body []byte) (interface{}, error) {
var exercice fic.Exercice
exerciceHandler(func (ex fic.Exercice, _[]byte) (interface{}, error) {
@@ -248,6 +248,18 @@ func eventHandler(f func(fic.Event,[]byte) (interface{}, error)) func (httproute
}
}
+func fileHandler(f func(fic.EFile,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
+ return func (ps httprouter.Params, body []byte) (interface{}, error) {
+ if fileid, err := strconv.Atoi(string(ps.ByName("fileid"))); err != nil {
+ return nil, err
+ } else if file, err := fic.GetFile(fileid); err != nil {
+ return nil, err
+ } else {
+ return f(file, body)
+ }
+ }
+}
+
func notFound(ps httprouter.Params, _ []byte) (interface{}, error) {
return nil, nil
}
diff --git a/admin/index.go b/admin/index.go
index 7aab5c96..cacb7b98 100644
--- a/admin/index.go
+++ b/admin/index.go
@@ -33,6 +33,7 @@ const indextpl = `
Équipes
Thèmes
Exercices
+ Fichiers
Public
Événements
Paramètres
diff --git a/admin/static.go b/admin/static.go
index ee9d4b17..75127168 100644
--- a/admin/static.go
+++ b/admin/static.go
@@ -19,6 +19,9 @@ func init() {
api.Router().GET("/events/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
http.ServeFile(w, r, path.Join(StaticDir, "index.html"))
})
+ api.Router().GET("/files/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+ http.ServeFile(w, r, path.Join(StaticDir, "index.html"))
+ })
api.Router().GET("/public/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
http.ServeFile(w, r, path.Join(StaticDir, "index.html"))
})
diff --git a/admin/static/index.html b/admin/static/index.html
index 9601ce03..4e101efc 100644
--- a/admin/static/index.html
+++ b/admin/static/index.html
@@ -31,6 +31,7 @@
Équipes
Thèmes
Exercices
+ Fichiers
Public
Événements
Paramètres
diff --git a/admin/static/js/app.js b/admin/static/js/app.js
index 43a2a034..b6a11ddf 100644
--- a/admin/static/js/app.js
+++ b/admin/static/js/app.js
@@ -49,6 +49,10 @@ angular.module("FICApp", ["ngRoute", "ngResource", "ngSanitize"])
controller: "PublicController",
templateUrl: "views/public.html"
})
+ .when("/files", {
+ controller: "FilesListController",
+ templateUrl: "views/file-list.html"
+ })
.when("/events", {
controller: "EventsListController",
templateUrl: "views/event-list.html"
@@ -72,6 +76,9 @@ angular.module("FICApp")
'update': {method: 'PUT'},
})
})
+ .factory("File", function($resource) {
+ return $resource("/api/files/:fileId", { fileId: '@id' })
+ })
.factory("ROSettings", function($resource) {
return $resource("/api/settings-ro.json")
})
@@ -525,6 +532,37 @@ angular.module("FICApp")
};
})
+ .controller("FilesListController", function($scope, File, $location, $http, $rootScope) {
+ $scope.files = File.query();
+ $scope.fields = ["id", "path", "name", "checksum", "size"];
+
+ $scope.clearFiles = function(id) {
+ File.delete(function() {
+ $scope.files = [];
+ });
+ };
+ $scope.checksum = function(f) {
+ $http({
+ url: "/api/files/" + f.id + "/checksum",
+ method: "GET"
+ }).then(function(response) {
+ if (response.data)
+ $rootScope.newBox('danger', response.data);
+ }, function(response) {
+ $scope.inSync = false;
+ $rootScope.newBox('danger', 'An error occurs when checking files:', response.data);
+ })
+ };
+ $scope.checksumAll = function() {
+ angular.forEach($scope.files, function(file) {
+ $scope.checksum(file);
+ });
+ };
+ $scope.show = function(f) {
+ $location.url("/exercices/" + f.idExercice);
+ };
+ })
+
.controller("EventsListController", function($scope, Event, $location) {
$scope.events = Event.query();
$scope.fields = ["id", "kind", "txt", "time"];
diff --git a/admin/static/views/file-list.html b/admin/static/views/file-list.html
new file mode 100644
index 00000000..937f28f6
--- /dev/null
+++ b/admin/static/views/file-list.html
@@ -0,0 +1,22 @@
+
+ Fichiers
+
+
+
+
+
+
+
+
+ {{ field }}
+ |
+
+
+
+
+
+ {{ file[field] }}
+ |
+
+
+
diff --git a/libfic/file.go b/libfic/file.go
index fdecadaa..8967e684 100644
--- a/libfic/file.go
+++ b/libfic/file.go
@@ -49,6 +49,11 @@ func GetFiles() ([]EFile, error) {
}
}
+func GetFile(id int) (f EFile, err error) {
+ err = DBQueryRow("SELECT id_file, origin, path, name, cksum, size FROM exercice_files WHERE id_file = ?", id).Scan(&f.Id, &f.origin, &f.Path, &f.Name, &f.Checksum, &f.Size)
+ return f, err
+}
+
func GetFileByPath(path string) (EFile, error) {
path = strings.TrimPrefix(path, FilesDir)
@@ -94,13 +99,13 @@ func (e Exercice) GetFileByPath(path string) (EFile, error) {
return f, nil
}
-func (e Exercice) ImportFile(filePath string, origin string, digest []byte) (interface{}, error) {
- if digest == nil && !OptionalDigest {
- return EFile{}, errors.New("No digest given.")
+func checkFileHash(filePath string, digest []byte) ([]byte, int64, error) {
+ if digest == nil {
+ return []byte{}, 0, errors.New("No digest given.")
} else if fi, err := os.Stat(filePath); err != nil {
- return EFile{}, err
+ return []byte{}, fi.Size(), err
} else if fd, err := os.Open(filePath); err != nil {
- return EFile{}, err
+ return []byte{}, fi.Size(), err
} else {
defer fd.Close()
@@ -110,41 +115,49 @@ func (e Exercice) ImportFile(filePath string, origin string, digest []byte) (int
w := io.MultiWriter(hash160, hash512)
if _, err := io.Copy(w, reader); err != nil {
- return EFile{}, err
+ return []byte{}, fi.Size(), err
}
result160 := hash160.Sum(nil)
result512 := hash512.Sum(nil)
if len(digest) != len(result512) {
if len(digest) != len(result160) {
- return EFile{}, errors.New("Digests doesn't match: calculated: sha1:" + hex.EncodeToString(result160) + " & blake2b:" + hex.EncodeToString(result512) + " vs. given: " + hex.EncodeToString(digest))
+ return []byte{}, fi.Size(), errors.New("Digests doesn't match: calculated: sha1:" + hex.EncodeToString(result160) + " & blake2b:" + hex.EncodeToString(result512) + " vs. given: " + hex.EncodeToString(digest))
} else if StrongDigest {
- return EFile{}, errors.New("Invalid digests: SHA-1 checksums are no more accepted. Calculated sha1:" + hex.EncodeToString(result160) + " & blake2b:" + hex.EncodeToString(result512) + " vs. given: " + hex.EncodeToString(digest))
+ return []byte{}, fi.Size(), errors.New("Invalid digests: SHA-1 checksums are no more accepted. Calculated sha1:" + hex.EncodeToString(result160) + " & blake2b:" + hex.EncodeToString(result512) + " vs. given: " + hex.EncodeToString(digest))
}
for k := range result160 {
if result160[k] != digest[k] {
- return EFile{}, errors.New("Digests doesn't match: calculated: sha1:" + hex.EncodeToString(result160) + " & blake2b:" + hex.EncodeToString(result512) + " vs. given: " + hex.EncodeToString(digest))
+ return []byte{}, fi.Size(), errors.New("Digests doesn't match: calculated: sha1:" + hex.EncodeToString(result160) + " & blake2b:" + hex.EncodeToString(result512) + " vs. given: " + hex.EncodeToString(digest))
}
}
} else {
for k := range result512 {
if result512[k] != digest[k] {
- return EFile{}, errors.New("Digests doesn't match: calculated: " + hex.EncodeToString(result512) + " vs. given: " + hex.EncodeToString(digest))
+ return []byte{}, fi.Size(), errors.New("Digests doesn't match: calculated: " + hex.EncodeToString(result512) + " vs. given: " + hex.EncodeToString(digest))
}
}
}
+ return result512, fi.Size(), nil
+ }
+}
+
+func (e Exercice) ImportFile(filePath string, origin string, digest []byte) (interface{}, error) {
+ if result512, size, err := checkFileHash(filePath, digest); !OptionalDigest && err != nil {
+ return EFile{}, err
+ } else {
dPath := strings.TrimPrefix(filePath, FilesDir)
if f, err := e.GetFileByPath(dPath); err != nil {
- return e.AddFile(dPath, origin, path.Base(filePath), result512, fi.Size())
+ return e.AddFile(dPath, origin, path.Base(filePath), result512, size)
} else {
// Don't need to update Path and Name, because they are related to dPath
f.IdExercice = e.Id
f.origin = origin
f.Checksum = result512
- f.Size = fi.Size()
+ f.Size = size
if _, err := f.Update(); err != nil {
return nil, err
@@ -195,6 +208,16 @@ func (e Exercice) WipeFiles() (int64, error) {
}
}
+func ClearFiles() (int64, error) {
+ if res, err := DBExec("DELETE FROM exercice_files"); err != nil {
+ return 0, err
+ } else if nb, err := res.RowsAffected(); err != nil {
+ return 0, err
+ } else {
+ return nb, err
+ }
+}
+
func (f EFile) GetOrigin() string {
return f.origin
}