Compare commits
69 Commits
ced3fd2ded
...
3aea59f1ad
Author | SHA1 | Date | |
---|---|---|---|
3aea59f1ad | |||
526d693ffd | |||
df8a759134 | |||
03e48b749c | |||
97f7e3fa59 | |||
e421c91ac2 | |||
baccc54d02 | |||
e8e87c9958 | |||
08a31898df | |||
b409fa6806 | |||
63b4cdc622 | |||
650f933993 | |||
603b226955 | |||
55e829fa64 | |||
45a0504c44 | |||
ad7489e558 | |||
57c3cd8fd6 | |||
24686a6a24 | |||
407b67f4c2 | |||
c28d974105 | |||
ffb69663b6 | |||
4ec4f47951 | |||
96707e3a29 | |||
a4001759f6 | |||
f15cd29f78 | |||
7e41ddd664 | |||
89b7710544 | |||
79ec20d11c | |||
09206df20a | |||
a14c151b04 | |||
68dad00930 | |||
c74eadc801 | |||
|
376e112130 | ||
|
1d2a09f612 | ||
|
6a35bd6345 | ||
|
a463e88a90 | ||
|
3d066fbdeb | ||
|
7dd3f64a08 | ||
bd5050b24a | |||
0ab453811c | |||
e71dc24a27 | |||
fb5147fac3 | |||
f073ea0fd0 | |||
34cf1789f3 | |||
9e75386038 | |||
a1b0e6a79b | |||
092d2256f7 | |||
28b4e7e529 | |||
32632322d4 | |||
4473166ee7 | |||
12feb91d48 | |||
03d02669ea | |||
c1924c0e92 | |||
7692f92aa4 | |||
724f985770 | |||
fa1b21e49f | |||
6b9283f7ca | |||
2a0a0dc9d4 | |||
ad6e59d8eb | |||
6a1120898b | |||
e178f7a80f | |||
895e34fef4 | |||
148deb77ec | |||
5321d499b2 | |||
f6d2794fbd | |||
ba37309ee5 | |||
6dfe4115c3 | |||
0296c9cc85 | |||
180fcbfa33 |
Dockerfile-repochecker
admin
api
static
sync
configs
fickit-backend.ymlfickit-frontend.ymlfickit-prepare.ymlfickit-update.ymlfrontend/fic
go.modgo.sumlibfic
qa
@ -23,7 +23,7 @@ RUN go get -d -v ./repochecker && \
|
||||
|
||||
ENV GRAMMALECTE_VERSION 2.1.1
|
||||
|
||||
ADD https://grammalecte.net/zip/Grammalecte-fr-v$GRAMMALECTE_VERSION.zip /srv/grammalecte.zip
|
||||
ADD https://web.archive.org/web/20240926154729if_/https://grammalecte.net/zip/Grammalecte-fr-v$GRAMMALECTE_VERSION.zip /srv/grammalecte.zip
|
||||
|
||||
RUN mkdir /srv/grammalecte && cd /srv/grammalecte && unzip /srv/grammalecte.zip && sed -i 's/if sys.version_info.major < (3, 7):/if False:/' /srv/grammalecte/grammalecte-server.py
|
||||
|
||||
|
@ -62,6 +62,8 @@ func declareExercicesRoutes(router *gin.RouterGroup) {
|
||||
apiFlagsRoutes.POST("/try", tryExerciceFlag)
|
||||
apiFlagsRoutes.DELETE("/", deleteExerciceFlag)
|
||||
apiFlagsRoutes.GET("/dependancies", showExerciceFlagDeps)
|
||||
apiFlagsRoutes.GET("/statistics", showExerciceFlagStats)
|
||||
apiFlagsRoutes.DELETE("/tries", deleteExerciceFlagTries)
|
||||
apiFlagsRoutes.GET("/choices/", listFlagChoices)
|
||||
apiFlagsChoicesRoutes := apiExercicesRoutes.Group("/choices/:cid")
|
||||
apiFlagsChoicesRoutes.Use(FlagChoiceHandler)
|
||||
@ -77,6 +79,8 @@ func declareExercicesRoutes(router *gin.RouterGroup) {
|
||||
apiQuizRoutes.PUT("", updateExerciceQuiz)
|
||||
apiQuizRoutes.DELETE("", deleteExerciceQuiz)
|
||||
apiQuizRoutes.GET("/dependancies", showExerciceQuizDeps)
|
||||
apiQuizRoutes.GET("/statistics", showExerciceQuizStats)
|
||||
apiQuizRoutes.DELETE("/tries", deleteExerciceQuizTries)
|
||||
|
||||
apiExercicesRoutes.GET("/tags", listExerciceTags)
|
||||
apiExercicesRoutes.POST("/tags", addExerciceTag)
|
||||
@ -852,6 +856,60 @@ func showExerciceFlagDeps(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, deps)
|
||||
}
|
||||
|
||||
func showExerciceFlagStats(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
flag := c.MustGet("flag-key").(*fic.FlagKey)
|
||||
|
||||
history, err := exercice.GetHistory()
|
||||
if err != nil {
|
||||
log.Println("Unable to getExerciceHistory:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving exercice history"})
|
||||
return
|
||||
}
|
||||
|
||||
var completed int64
|
||||
|
||||
for _, hline := range history {
|
||||
if hline["kind"].(string) == "flag_found" {
|
||||
if *hline["secondary"].(*int) == flag.Id {
|
||||
completed += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tries, err := flag.NbTries()
|
||||
if err != nil {
|
||||
log.Println("Unable to nbTries:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag tries"})
|
||||
return
|
||||
}
|
||||
|
||||
teams, err := flag.TeamsOnIt()
|
||||
if err != nil {
|
||||
log.Println("Unable to teamsOnIt:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag related teams"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"completed": completed,
|
||||
"tries": tries,
|
||||
"teams": teams,
|
||||
})
|
||||
}
|
||||
|
||||
func deleteExerciceFlagTries(c *gin.Context) {
|
||||
flag := c.MustGet("flag-key").(*fic.FlagKey)
|
||||
|
||||
err := flag.DeleteTries()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.AbortWithStatusJSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func tryExerciceFlag(c *gin.Context) {
|
||||
flag := c.MustGet("flag-key").(*fic.FlagKey)
|
||||
|
||||
@ -1022,6 +1080,60 @@ func showExerciceQuizDeps(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, deps)
|
||||
}
|
||||
|
||||
func showExerciceQuizStats(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
quiz := c.MustGet("flag-quiz").(*fic.MCQ)
|
||||
|
||||
history, err := exercice.GetHistory()
|
||||
if err != nil {
|
||||
log.Println("Unable to getExerciceHistory:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving exercice history"})
|
||||
return
|
||||
}
|
||||
|
||||
var completed int64
|
||||
|
||||
for _, hline := range history {
|
||||
if hline["kind"].(string) == "mcq_found" {
|
||||
if *hline["secondary"].(*int) == quiz.Id {
|
||||
completed += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tries, err := quiz.NbTries()
|
||||
if err != nil {
|
||||
log.Println("Unable to nbTries:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag tries"})
|
||||
return
|
||||
}
|
||||
|
||||
teams, err := quiz.TeamsOnIt()
|
||||
if err != nil {
|
||||
log.Println("Unable to teamsOnIt:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag related teams"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"completed": completed,
|
||||
"tries": tries,
|
||||
"teams": teams,
|
||||
})
|
||||
}
|
||||
|
||||
func deleteExerciceQuizTries(c *gin.Context) {
|
||||
quiz := c.MustGet("flag-quiz").(*fic.MCQ)
|
||||
|
||||
err := quiz.DeleteTries()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.AbortWithStatusJSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
func updateExerciceQuiz(c *gin.Context) {
|
||||
quiz := c.MustGet("flag-quiz").(*fic.MCQ)
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
@ -143,12 +144,27 @@ func listFiles(c *gin.Context) {
|
||||
}
|
||||
|
||||
func clearFiles(c *gin.Context) {
|
||||
_, err := fic.ClearFiles()
|
||||
err := os.RemoveAll(fic.FilesDir)
|
||||
if err != nil {
|
||||
log.Println("Unable to remove files:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = os.MkdirAll(fic.FilesDir, 0751)
|
||||
if err != nil {
|
||||
log.Println("Unable to create FILES:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
_, err = fic.ClearFiles()
|
||||
if err != nil {
|
||||
log.Println("Unable to clean DB files:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Les fichiers ont bien été effacés. Mais il n'a pas été possible d'effacer la base de données. Refaites une synchronisation maintenant. " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
|
||||
|
@ -41,5 +41,27 @@ func declareRepositoriesRoutes(router *gin.RouterGroup) {
|
||||
}
|
||||
c.JSON(http.StatusOK, mod)
|
||||
})
|
||||
|
||||
router.DELETE("/repositories/*repopath", func(c *gin.Context) {
|
||||
di, ok := sync.GlobalImporter.(sync.DeletableImporter)
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusNotImplemented, gin.H{"errmsg": "Not implemented"})
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(c.Param("repopath"), "..") {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Repopath contains invalid characters"})
|
||||
return
|
||||
}
|
||||
|
||||
repopath := strings.TrimPrefix(c.Param("repopath"), "/")
|
||||
|
||||
err := di.DeleteDir(repopath)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@ -26,7 +25,6 @@ func declareSettingsRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/challenge.json", getChallengeInfo)
|
||||
router.PUT("/challenge.json", saveChallengeInfo)
|
||||
|
||||
router.GET("/settings-ro.json", getROSettings)
|
||||
router.GET("/settings.json", getSettings)
|
||||
router.PUT("/settings.json", saveSettings)
|
||||
router.DELETE("/settings.json", func(c *gin.Context) {
|
||||
@ -98,24 +96,6 @@ func fullGeneration(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func getROSettings(c *gin.Context) {
|
||||
syncMtd := "Disabled"
|
||||
if sync.GlobalImporter != nil {
|
||||
syncMtd = sync.GlobalImporter.Kind()
|
||||
}
|
||||
|
||||
var syncId *string
|
||||
if sync.GlobalImporter != nil {
|
||||
syncId = sync.GlobalImporter.Id()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"sync-type": reflect.TypeOf(sync.GlobalImporter).Name(),
|
||||
"sync-id": syncId,
|
||||
"sync": syncMtd,
|
||||
})
|
||||
}
|
||||
|
||||
func GetChallengeInfo() (*settings.ChallengeInfo, error) {
|
||||
var challengeinfo string
|
||||
var err error
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/generation"
|
||||
@ -17,6 +18,8 @@ import (
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
var lastSyncError = ""
|
||||
|
||||
func flatifySyncErrors(errs error) (ret []string) {
|
||||
for _, err := range multierr.Errors(errs) {
|
||||
ret = append(ret, err.Error())
|
||||
@ -27,12 +30,37 @@ func flatifySyncErrors(errs error) (ret []string) {
|
||||
func declareSyncRoutes(router *gin.RouterGroup) {
|
||||
apiSyncRoutes := router.Group("/sync")
|
||||
|
||||
// Return the global sync status
|
||||
apiSyncRoutes.GET("/status", func(c *gin.Context) {
|
||||
syncMtd := "Disabled"
|
||||
if sync.GlobalImporter != nil {
|
||||
syncMtd = sync.GlobalImporter.Kind()
|
||||
}
|
||||
|
||||
var syncId *string
|
||||
if sync.GlobalImporter != nil {
|
||||
syncId = sync.GlobalImporter.Id()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"sync-type": reflect.TypeOf(sync.GlobalImporter).Name(),
|
||||
"sync-id": syncId,
|
||||
"sync": syncMtd,
|
||||
"pullMutex": !sync.OneGitPullStatus(),
|
||||
"syncMutex": !sync.OneDeepSyncStatus() && !sync.OneThemeDeepSyncStatus(),
|
||||
"progress": sync.DeepSyncProgress,
|
||||
"lastError": lastSyncError,
|
||||
})
|
||||
})
|
||||
|
||||
// Base sync checks if the local directory is in sync with remote one.
|
||||
apiSyncRoutes.POST("/base", func(c *gin.Context) {
|
||||
err := sync.GlobalImporter.Sync()
|
||||
if err != nil {
|
||||
lastSyncError = err.Error()
|
||||
c.JSON(http.StatusExpectationFailed, gin.H{"errmsg": err.Error()})
|
||||
} else {
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, true)
|
||||
}
|
||||
})
|
||||
@ -45,15 +73,10 @@ func declareSyncRoutes(router *gin.RouterGroup) {
|
||||
})
|
||||
|
||||
// Deep sync: a fully recursive synchronization (can be limited by theme).
|
||||
apiSyncRoutes.GET("/deep", func(c *gin.Context) {
|
||||
if sync.DeepSyncProgress == 0 {
|
||||
c.AbortWithStatusJSON(http.StatusTooEarly, gin.H{"errmsg": "Pas de synchronisation en cours"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"progress": sync.DeepSyncProgress})
|
||||
})
|
||||
apiSyncRoutes.POST("/deep", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, sync.SyncDeep(sync.GlobalImporter))
|
||||
r := sync.SyncDeep(sync.GlobalImporter)
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, r)
|
||||
})
|
||||
|
||||
apiSyncDeepRoutes := apiSyncRoutes.Group("/deep/:thid")
|
||||
@ -66,6 +89,7 @@ func declareSyncRoutes(router *gin.RouterGroup) {
|
||||
}
|
||||
sync.EditDeepReport(&sync.SyncReport{Exercices: st}, false)
|
||||
sync.DeepSyncProgress = 255
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, st)
|
||||
})
|
||||
apiSyncDeepRoutes.POST("", func(c *gin.Context) {
|
||||
@ -79,6 +103,7 @@ func declareSyncRoutes(router *gin.RouterGroup) {
|
||||
}
|
||||
sync.EditDeepReport(&sync.SyncReport{Themes: map[string][]string{theme.Name: st}}, false)
|
||||
sync.DeepSyncProgress = 255
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, st)
|
||||
})
|
||||
|
||||
@ -90,6 +115,7 @@ func declareSyncRoutes(router *gin.RouterGroup) {
|
||||
|
||||
apiSyncRoutes.POST("/themes", func(c *gin.Context) {
|
||||
_, errs := sync.SyncThemes(sync.GlobalImporter)
|
||||
lastSyncError = ""
|
||||
c.JSON(http.StatusOK, flatifySyncErrors(errs))
|
||||
})
|
||||
|
||||
@ -258,10 +284,12 @@ func autoSync(c *gin.Context) {
|
||||
|
||||
if !IsProductionEnv {
|
||||
if err := sync.GlobalImporter.Sync(); err != nil {
|
||||
lastSyncError = err.Error()
|
||||
log.Println("Unable to sync.GI.Sync:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to perform the pull."})
|
||||
return
|
||||
}
|
||||
lastSyncError = ""
|
||||
}
|
||||
|
||||
themes, err := fic.GetThemes()
|
||||
|
@ -230,9 +230,6 @@ angular.module("FICApp")
|
||||
.factory("File", function ($resource) {
|
||||
return $resource("api/files/:fileId", { fileId: '@id' })
|
||||
})
|
||||
.factory("ROSettings", function ($resource) {
|
||||
return $resource("api/settings-ro.json")
|
||||
})
|
||||
.factory("Settings", function ($resource) {
|
||||
return $resource("api/settings.json", null, {
|
||||
'update': { method: 'PUT' },
|
||||
@ -351,6 +348,9 @@ angular.module("FICApp")
|
||||
.factory("ExerciceFlagDeps", function ($resource) {
|
||||
return $resource("api/exercices/:exerciceId/flags/:flagId/dependancies", { exerciceId: '@idExercice', flagId: '@id' })
|
||||
})
|
||||
.factory("ExerciceFlagStats", function ($resource) {
|
||||
return $resource("api/exercices/:exerciceId/flags/:flagId/statistics", { exerciceId: '@idExercice', flagId: '@id' })
|
||||
})
|
||||
.factory("ExerciceMCQFlag", function ($resource) {
|
||||
return $resource("api/exercices/:exerciceId/quiz/:mcqId", { exerciceId: '@idExercice', mcqId: '@id' }, {
|
||||
update: { method: 'PUT' }
|
||||
@ -358,6 +358,9 @@ angular.module("FICApp")
|
||||
})
|
||||
.factory("ExerciceMCQDeps", function ($resource) {
|
||||
return $resource("api/exercices/:exerciceId/quiz/:mcqId/dependancies", { exerciceId: '@idExercice', mcqId: '@id' })
|
||||
})
|
||||
.factory("ExerciceMCQStats", function ($resource) {
|
||||
return $resource("api/exercices/:exerciceId/quiz/:mcqId/statistics", { exerciceId: '@idExercice', mcqId: '@id' })
|
||||
});
|
||||
|
||||
angular.module("FICApp")
|
||||
@ -614,9 +617,18 @@ angular.module("FICApp")
|
||||
|
||||
response.enableExerciceDepend = response.unlockedChallengeDepth >= 0;
|
||||
response.disabledsubmitbutton = response.disablesubmitbutton && response.disablesubmitbutton.length > 0;
|
||||
if (response.end) {
|
||||
$scope.duration = (response.end - response.start)/60000;
|
||||
}
|
||||
})
|
||||
$scope.challenge = SettingsChallenge.get();
|
||||
$scope.duration = 360;
|
||||
$scope.durationChange = function(endChanged) {
|
||||
if (endChanged)
|
||||
$scope.duration = (new Date($scope.config.end).getTime() - new Date($scope.config.start).getTime())/60000;
|
||||
else
|
||||
$scope.config.end = new Date(new Date($scope.config.start).getTime() + $scope.duration * 60000);
|
||||
}
|
||||
$scope.activateTime = "";
|
||||
$scope.challenge.$promise.then(function (c) {
|
||||
if (c.duration)
|
||||
@ -651,7 +663,7 @@ angular.module("FICApp")
|
||||
$scope.saveChallengeInfo = function () {
|
||||
this.challenge.duration = $scope.duration;
|
||||
this.challenge.$update(function (response) {
|
||||
$scope.addToast('success', 'Infos du challenge mises à jour avec succès !');
|
||||
$scope.addToast('success', 'Infos du challenge mises à jour avec succès !');
|
||||
}, function (response) {
|
||||
$scope.addToast('danger', 'An error occurs when saving challenge info:', response.data.errmsg);
|
||||
});
|
||||
@ -725,9 +737,9 @@ angular.module("FICApp")
|
||||
"teams": "En validant, vous supprimerez l'ensemble des équipes enregistreées.",
|
||||
"game": "En validant, vous supprimerez toutes les tentatives, les validations, ... faites par les équipes.",
|
||||
}
|
||||
$scope.addToast('warning', txts[type], 'Êtes-vous sûr de vouloir continuer ?',
|
||||
$scope.addToast('warning', txts[type], 'Êtes-vous sûr de vouloir continuer ?',
|
||||
function () {
|
||||
if (confirm("Êtes-vous vraiment sûr ?\n" + txts[type])) {
|
||||
if (confirm("Êtes-vous vraiment sûr ?\n" + txts[type])) {
|
||||
$http.post("api/reset", { "type": type }).then(function (time) {
|
||||
$scope.addToast('success', type + 'reseted');
|
||||
$location.url("/");
|
||||
@ -739,7 +751,7 @@ angular.module("FICApp")
|
||||
});
|
||||
};
|
||||
$scope.switchToProd = function () {
|
||||
$scope.addToast('warning', "Activer le mode challenge ?", "L'activation du mode challenge est temporaire (vous devriez plutôt relancer le daemon avec l'option `-4real`). Ce mode permet d'éviter les mauvaises manipulations et désactive le hook git de synchronisation automatique. Êtes-vous sûr de vouloir continuer ?",
|
||||
$scope.addToast('warning', "Activer le mode challenge ?", "L'activation du mode challenge est temporaire (vous devriez plutôt relancer le daemon avec l'option `-4real`). Ce mode permet d'éviter les mauvaises manipulations et désactive le hook git de synchronisation automatique. Êtes-vous sûr de vouloir continuer ?",
|
||||
function () {
|
||||
$http.put("api/prod", true).then(function (time) {
|
||||
$rootScope.refreshSettings()
|
||||
@ -755,7 +767,27 @@ angular.module("FICApp")
|
||||
$http.get("api/repositories").then(function (response) {
|
||||
$scope.repositories = response.data.repositories;
|
||||
});
|
||||
|
||||
$scope.deleteRepository = function(repo) {
|
||||
$http.delete("api/repositories/" + repo.path).then(function (response) {
|
||||
$scope.repositories[$scope.repositories.indexOf(repo)].hash = "- DELETED -";
|
||||
});
|
||||
};
|
||||
})
|
||||
.component('teamLink', {
|
||||
bindings: {
|
||||
idTeam: '=',
|
||||
},
|
||||
controller: function (Team) {
|
||||
var ctrl = this;
|
||||
ctrl.team = {};
|
||||
|
||||
ctrl.$onInit = function () {
|
||||
ctrl.team = Team.get({teamId: ctrl.idTeam});
|
||||
};
|
||||
},
|
||||
template: `<a href="/teams/{{ $ctrl.idTeam }}">{{ $ctrl.team.name }}</a> `
|
||||
})
|
||||
.component('repositoryUptodate', {
|
||||
bindings: {
|
||||
repository: '<',
|
||||
@ -781,9 +813,8 @@ angular.module("FICApp")
|
||||
template: `<span class="badge {{ $ctrl.color }}">{{ $ctrl.status.hash }}</span> <small>{{ $ctrl.status.text }}</small>`
|
||||
})
|
||||
|
||||
.controller("SyncController", function ($scope, $rootScope, ROSettings, $location, $http, $interval) {
|
||||
.controller("SyncController", function ($scope, $rootScope, $location, $http, $interval) {
|
||||
$scope.displayDangerousActions = false;
|
||||
$scope.configro = ROSettings.get();
|
||||
|
||||
var needRefreshSyncReportWhenReady = false;
|
||||
var refreshSyncReport = function () {
|
||||
@ -794,36 +825,34 @@ angular.module("FICApp")
|
||||
};
|
||||
refreshSyncReport()
|
||||
|
||||
$scope.deepSyncInProgress = false;
|
||||
|
||||
var progressInterval = $interval(function () {
|
||||
$http.get("api/sync/deep").then(function (response) {
|
||||
$http.get("api/sync/status").then(function (response) {
|
||||
if (response.data.progress && response.data.progress != 255)
|
||||
needRefreshSyncReportWhenReady = true;
|
||||
else if (needRefreshSyncReportWhenReady)
|
||||
refreshSyncReport();
|
||||
if (response.data && response.data.progress) {
|
||||
$scope.syncPercent = Math.floor(response.data.progress * 100 / 255);
|
||||
$scope.syncProgress = Math.floor(response.data.progress * 100 / 255) + " %";
|
||||
$scope.deepSyncInProgress = response.data.pullMutex && response.data.syncMutex;
|
||||
} else {
|
||||
$scope.syncProgress = response.data;
|
||||
$scope.syncPercent = 0;
|
||||
}
|
||||
$scope.syncStatus = response.data;
|
||||
}, function (response) {
|
||||
$scope.syncPercent = 0;
|
||||
if (response.data && response.data.errmsg)
|
||||
$scope.syncProgress = response.data.errmsg;
|
||||
else
|
||||
$scope.syncProgress = response.data;
|
||||
$scope.syncStatus = response.data;
|
||||
})
|
||||
}, 1500);
|
||||
$scope.$on('$destroy', function () { $interval.cancel(progressInterval); });
|
||||
|
||||
$scope.deepSyncInProgress = false;
|
||||
$scope.deepSync = function (theme) {
|
||||
if (theme) {
|
||||
question = 'Faire une synchronisation intégrale du thème ' + theme.name + ' ?'
|
||||
question = 'Faire une synchronisation intégrale du thème ' + theme.name + ' ?'
|
||||
url = "api/sync/deep/" + theme.id
|
||||
} else {
|
||||
question = 'Faire une synchronisation intégrale ?'
|
||||
question = 'Faire une synchronisation intégrale ?'
|
||||
url = "api/sync/deep"
|
||||
}
|
||||
$scope.addToast('warning', question, '',
|
||||
@ -839,7 +868,7 @@ angular.module("FICApp")
|
||||
});
|
||||
};
|
||||
$scope.speedyDeepSync = function () {
|
||||
$scope.addToast('warning', 'Faire une synchronisation profonde rapide, sans s\'occuper des fichiers ?', '',
|
||||
$scope.addToast('warning', 'Faire une synchronisation profonde rapide, sans s\'occuper des fichiers ?', '',
|
||||
function () {
|
||||
$scope.deepSyncInProgress = true;
|
||||
$http.post("api/sync/speed").then(function () {
|
||||
@ -852,7 +881,7 @@ angular.module("FICApp")
|
||||
});
|
||||
};
|
||||
$scope.baseSync = function () {
|
||||
$scope.addToast('warning', 'Tirer les mises à jour du dépôt ?', '',
|
||||
$scope.addToast('warning', 'Tirer les mises à jour du dépôt ?', '',
|
||||
function () {
|
||||
$scope.deepSyncInProgress = true;
|
||||
$http.post("api/sync/base").then(function () {
|
||||
@ -865,7 +894,7 @@ angular.module("FICApp")
|
||||
});
|
||||
};
|
||||
$scope.syncVideos = function () {
|
||||
$scope.addToast('warning', 'Synchroniser les vidéos de résolution ?', 'ATTENTION il ne faut pas lancer cette synchronisation durant le challenge. Seulement une fois le challenge terminé, cela permet de rendre les vidéos accessibles dans l\'interface joueurs.',
|
||||
$scope.addToast('warning', 'Synchroniser les vidéos de résolution ?', 'ATTENTION il ne faut pas lancer cette synchronisation durant le challenge. Seulement une fois le challenge terminé, cela permet de rendre les vidéos accessibles dans l\'interface joueurs.',
|
||||
function () {
|
||||
$scope.deepSyncInProgress = true;
|
||||
$http.post("api/sync/videos").then(function () {
|
||||
@ -1110,7 +1139,7 @@ angular.module("FICApp")
|
||||
},
|
||||
{
|
||||
type: "countdown",
|
||||
params: { color: "success", end: null, lead: "Go, go, go !", title: "Le challenge forensic va bientôt commencer !" },
|
||||
params: { color: "success", end: null, lead: "Go, go, go !", title: "Le challenge forensic va bientôt commencer !" },
|
||||
},
|
||||
];
|
||||
$scope.display.side = [
|
||||
@ -1229,8 +1258,8 @@ angular.module("FICApp")
|
||||
show: true,
|
||||
shadow: "#E8CF5C",
|
||||
end: new Date($rootScope.getSrvTime().getTime() + 1802000).toISOString(),
|
||||
before: "Heure joyeuse : chaque résolution compte double !",
|
||||
after: "Heure joyeuse terminée !",
|
||||
before: "Heure joyeuse : chaque résolution compte double !",
|
||||
after: "Heure joyeuse terminée !",
|
||||
}
|
||||
}
|
||||
else if (scene == "freehintquarter") {
|
||||
@ -1238,8 +1267,8 @@ angular.module("FICApp")
|
||||
show: true,
|
||||
shadow: "#3DD28F",
|
||||
end: new Date($rootScope.getSrvTime().getTime() + 902000).toISOString(),
|
||||
before: "Quart d'heure facile : indices dévoilés !",
|
||||
after: "Quart d'heure facile terminée !",
|
||||
before: "Quart d'heure facile : indices dévoilés !",
|
||||
after: "Quart d'heure facile terminée !",
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1358,6 +1387,7 @@ angular.module("FICApp")
|
||||
$scope.files = File.query();
|
||||
$scope.errfnd = null;
|
||||
$scope.errzip = null;
|
||||
$scope.clearFilesWIP = false;
|
||||
$scope.fields = ["id", "path", "name", "size"];
|
||||
|
||||
$scope.clearFiles = function (id) {
|
||||
@ -1366,6 +1396,21 @@ angular.module("FICApp")
|
||||
$scope.files = [];
|
||||
});
|
||||
};
|
||||
$scope.clearFilesDir = function () {
|
||||
$scope.addToast('warning', 'Êtes-vous sûr de vouloir continuer ?', "Ceci va supprimer tout le contenu du dossier FILES. Il s'agit des fichiers ci-dessous, il faudra refaire une synchronisation ensuite.",
|
||||
function () {
|
||||
$scope.clearFilesWIP = true;
|
||||
$http({
|
||||
url: "api/files",
|
||||
method: "DELETE"
|
||||
}).then(function (response) {
|
||||
$scope.clearFilesWIP = false;
|
||||
}, function (response) {
|
||||
$scope.clearFilesWIP = false;
|
||||
$scope.addToast('danger', 'An error occurs when trying to clear files:', response.data.errmsg);
|
||||
});
|
||||
});
|
||||
};
|
||||
$scope.gunzipFile = function (f) {
|
||||
f.gunzipWIP = true;
|
||||
$http({
|
||||
@ -2260,7 +2305,7 @@ angular.module("FICApp")
|
||||
method: "POST"
|
||||
}).then(function (response) {
|
||||
flag.test_str = "";
|
||||
$scope.addToast('success', "Flag Ok !");
|
||||
$scope.addToast('success', "Flag Ok !");
|
||||
}, function (response) {
|
||||
flag.test_str = "";
|
||||
$scope.addToast('danger', 'An error occurs: ', response.data.errmsg);
|
||||
@ -2329,6 +2374,18 @@ angular.module("FICApp")
|
||||
}
|
||||
})
|
||||
|
||||
.controller("ExerciceFlagStatsController", function ($scope, $routeParams, ExerciceFlagStats, $http) {
|
||||
$scope.init = function (flag) {
|
||||
$scope.flag_id = flag.id;
|
||||
$scope.stats = ExerciceFlagStats.get({ exerciceId: $routeParams.exerciceId, flagId: flag.id });
|
||||
}
|
||||
$scope.deleteTries = function () {
|
||||
$http.delete(`/api/exercices/${$routeParams.exerciceId}/flags/${$scope.flag_id}/tries`).then(function () {
|
||||
$scope.stats = ExerciceFlagStats.get({ exerciceId: $routeParams.exerciceId, flagId: $scope.flag_id });
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
.controller("ExerciceMCQFlagsController", function ($scope, ExerciceMCQFlag, $routeParams, $rootScope) {
|
||||
$scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId });
|
||||
|
||||
@ -2366,6 +2423,18 @@ angular.module("FICApp")
|
||||
}
|
||||
})
|
||||
|
||||
.controller("ExerciceMCQStatsController", function ($scope, $routeParams, ExerciceMCQStats, $http) {
|
||||
$scope.init = function (mcq) {
|
||||
$scope.mcq_id = mcq.id;
|
||||
$scope.stats = ExerciceMCQStats.get({ exerciceId: $routeParams.exerciceId, mcqId: mcq.id });
|
||||
}
|
||||
$scope.deleteTries = function () {
|
||||
$http.delete(`/api/exercices/${$routeParams.exerciceId}/quiz/${$scope.mcq_id}/tries`).then(function () {
|
||||
$scope.stats = ExerciceMCQStats.get({ exerciceId: $routeParams.exerciceId, mcqId: $scope.mcq_id });
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
.controller("TeamsListController", function ($scope, $rootScope, Team, $location, $http) {
|
||||
$scope.teams = Team.query();
|
||||
$scope.fields = ["id", "name"];
|
||||
|
@ -73,12 +73,28 @@
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div ng-controller="ExerciceFlagDepsController" ng-init="init(flag)">
|
||||
Dépendances :
|
||||
<strong>Dépendances :</strong>
|
||||
<ul ng-if="deps.length > 0">
|
||||
<dependancy ng-repeat="dep in deps" dep="dep"></dependancy>
|
||||
</ul>
|
||||
<span ng-if="deps.length == 0"> sans</span>
|
||||
</div>
|
||||
<hr>
|
||||
<div ng-controller="ExerciceFlagStatsController" ng-init="init(flag)">
|
||||
<strong>Statistiques</strong>
|
||||
<ul>
|
||||
<li>Validés : {{ stats["completed"] }}</li>
|
||||
<li>
|
||||
Tentés : {{ stats["tries"] }}
|
||||
<button type="button" ng-click="deleteTries()" class="btn btn-sm btn-danger" ng-if="stats['tries'] > 0"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
|
||||
</li>
|
||||
<li>
|
||||
Équipes :
|
||||
<span ng-if="stats['teams'].length == 0">aucune</span>
|
||||
<team-link ng-repeat="team in stats['teams']" id-team="team"></team-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4" ng-controller="ExerciceFlagChoicesController">
|
||||
<div class="btn-toolbar justify-content-end mb-2" role="toolbar">
|
||||
@ -168,6 +184,22 @@
|
||||
<dependancy ng-repeat="dep in deps" dep="dep"></dependancy>
|
||||
</ul>
|
||||
<span ng-if="deps.length == 0"> sans</span>
|
||||
<hr>
|
||||
<div ng-controller="ExerciceMCQStatsController" ng-init="init(q)">
|
||||
<strong>Statistiques</strong>
|
||||
<ul>
|
||||
<li>Validés : {{ stats["completed"] }}</li>
|
||||
<li>
|
||||
Tentés : {{ stats["tries"] }}
|
||||
<button type="button" ng-click="deleteTries()" class="btn btn-sm btn-danger" ng-if="stats['tries'] > 0"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
|
||||
</li>
|
||||
<li>
|
||||
Équipes :
|
||||
<span ng-if="stats['teams'].length == 0">aucune</span>
|
||||
<team-link ng-repeat="team in stats['teams']" id-team="team"></team-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<small class="text-muted" ng-if="errzip > 0"><span class="glyphicon glyphicon-exclamation-sign"></span> <ng-pluralize count="errzip" when="{'one': '{} décompression problématique', 'other': '{} décompressions problématiques'}"></ng-pluralize></small>
|
||||
<button type="button" ng-click="checksumAll()" class="float-right btn btn-sm" ng-class="{'btn-secondary': errfnd === null, 'btn-success': errfnd === 0, 'btn-danger': errfnd > 0}"><span class="glyphicon glyphicon-flash" aria-hidden="true"></span> Vérifier les fichiers</button>
|
||||
<button type="button" ng-click="gunzipFiles()" class="float-right btn btn-sm mx-1" ng-class="{'btn-secondary': errzip === null, 'btn-success': errzip === 0, 'btn-danger': errzip > 0}" title="Décompresse tous les fichiers compressés afin d'afficher la bonne taille au moment du téléchargement"><span class="glyphicon glyphicon-compressed" aria-hidden="true"></span> Gunzip</button>
|
||||
<button type="button" ng-click="clearFilesDir()" class="float-right btn btn-danger btn-sm mx-1" title="Supprime l'arborescence des fichiers"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Nuke files</button>
|
||||
</h2>
|
||||
|
||||
<p><input type="search" class="form-control" placeholder="Search" ng-model="query" autofocus></p>
|
||||
@ -29,13 +30,13 @@
|
||||
<div class="spinner-border spinner-border-sm" role="status" ng-if="file.gunzipWIP"></div>
|
||||
</button>
|
||||
</td>
|
||||
<td ng-repeat="field in fields">
|
||||
<td ng-repeat="field in fields" class="text-truncate" style="max-width: 30vw" title="{{ file[field] }}">
|
||||
{{ file[field] }}
|
||||
<span ng-if="field == 'id' && file.err !== undefined && file.err !== true" title="{{ file.err }}" class="glyphicon glyphicon-exclamation-sign"></span>
|
||||
</td>
|
||||
<td>
|
||||
{{ file.checksum | bto16 }}
|
||||
<div ng-if="file.checksum_shown">{{ file.checksum_shown | bto16 }}</div>
|
||||
<td style="max-width: 100px">
|
||||
<div class="text-truncate" title="{{ file.checksum | bto16 }}">{{ file.checksum | bto16 }}</div>
|
||||
<div class="text-truncate" ng-if="file.checksum_shown" title="{{ file.checksum_shown | bto16 }}">{{ file.checksum_shown | bto16 }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -9,16 +9,20 @@
|
||||
<tr>
|
||||
<th>Chemin</th>
|
||||
<th>Branche</th>
|
||||
<th>Commit</th>
|
||||
<th>Plus récent</th>
|
||||
<th>Commit <span class="text-muted">Plus récent</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="repository in repositories">
|
||||
<td>{{ repository.path }}</td>
|
||||
<td>{{ repository.branch }}</td>
|
||||
<td>{{ repository.hash }}</td>
|
||||
<td><repository-uptodate repository="repository" /></td>
|
||||
<td>
|
||||
{{ repository.hash }}<br>
|
||||
<repository-uptodate repository="repository" />
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" ng-click="deleteRepository(repository)" class="btn btn-sm btn-danger"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -35,7 +35,7 @@
|
||||
<label for="startTime" class="col-sm-3 col-form-label col-form-label-sm" ng-class="{'text-primary font-weight-bold': config.start != dist_config.start}">Début du challenge</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input type="datetime-local" class="form-control form-control-sm" id="startTime" ng-model="config.start" ng-class="{'border-primary': config.start != dist_config.start}">
|
||||
<input type="datetime-local" class="form-control form-control-sm" id="startTime" ng-model="config.start" ng-change="durationChange()" ng-class="{'border-primary': config.start != dist_config.start}">
|
||||
<div class="input-group-append">
|
||||
<button ng-click="launchChallenge()" class="btn btn-sm btn-secondary" type="button"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Lancer le challenge</button>
|
||||
</div>
|
||||
@ -46,14 +46,14 @@
|
||||
<div class="form-group row">
|
||||
<label for="endTime" class="col-sm-3 col-form-label col-form-label-sm" ng-class="{'text-primary font-weight-bold': config.end != dist_config.end}">Fin du challenge</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="datetime-local" class="form-control form-control-sm" id="endTime" ng-model="config.end" ng-class="{'border-primary': config.end != dist_config.end}">
|
||||
<input type="datetime-local" class="form-control form-control-sm" id="endTime" ng-model="config.end" ng-change="durationChange(true)" ng-class="{'border-primary': config.end != dist_config.end}">
|
||||
</div>
|
||||
<div class="col-sm-1 text-right">
|
||||
<label for="duration" class="col-form-label col-form-label-sm">Durée</label>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="text" class="form-control form-control-sm" id="duration" ng-model="duration" integer>
|
||||
<input type="number" class="form-control form-control-sm" id="duration" ng-model="duration" ng-change="durationChange()" integer>
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text">min</span>
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<div class="badge badge-success align-self-center" ng-if="syncReport" title="{{ syncReport._updated[syncReport._updated.length-1] }}">
|
||||
Dernier import : {{ syncReport._updated[syncReport._updated.length-1] | date:"medium" }}
|
||||
</div>
|
||||
<a ng-if="configro['sync-type'] === 'GitImporter'" href="repositories" class="btn btn-secondary">
|
||||
<a ng-if="syncStatus['sync-type'] === 'GitImporter'" href="repositories" class="btn btn-secondary">
|
||||
Voir les dépôts
|
||||
</a>
|
||||
</div>
|
||||
@ -32,17 +32,25 @@
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-2">Type</dt>
|
||||
<dd class="col-10" ng-bind="configro['sync-type']"></dd>
|
||||
<dd class="col-10" ng-bind="syncStatus['sync-type']"></dd>
|
||||
<dt class="col-2">Synchronisation</dt>
|
||||
<dd class="col-10" title="{{ configro['sync'] }}" ng-bind="configro.sync"></dd>
|
||||
<dt class="col-2" ng-if="configro['sync-id']">ID</dt>
|
||||
<dd class="col-10" ng-if="configro['sync-id']">{{ configro['sync-id'] }}</dd>
|
||||
<dt class="col-2" ng-if="configro['sync']">Statut</dt>
|
||||
<dd class="col-10" ng-if="configro['sync']">{{ syncProgress }}</dd>
|
||||
<dd class="col-10" title="{{ syncStatus['sync'] }}" ng-bind="syncStatus.sync"></dd>
|
||||
<dt class="col-2" ng-if="syncStatus['sync-id']">ID</dt>
|
||||
<dd class="col-10" ng-if="syncStatus['sync-id']">
|
||||
{{ syncStatus['sync-id'] }}
|
||||
<button ng-if="syncStatus['sync-type'] === 'GitImporter'" type="button" class="btn btn-sm btn-dark" ng-click="baseSync()" ng-disabled="deepSyncInProgress"><span class="glyphicon glyphicon-repeat" aria-hidden="true"></span> Pull</button>
|
||||
</dd>
|
||||
<dt class="col-2" ng-if="syncStatus['sync']">Statut</dt>
|
||||
<dd class="col-10" ng-if="syncStatus['sync']">
|
||||
<span ng-if="(syncStatus.syncMutex !== undefined && syncStatus.syncMutex) || (syncPercent > 0 && syncPercent < 100)">{{ syncPercent }} %</span>
|
||||
<span class="badge badge-pill" ng-class="{'badge-success': !syncStatus.pullMutex, 'badge-danger': syncStatus.pullMutex}" ng-if="syncStatus.pullMutex !== undefined">Pull</span>
|
||||
<span class="badge badge-pill" ng-class="{'badge-success': !syncStatus.syncMutex, 'badge-danger': syncStatus.syncMutex}" ng-if="syncStatus.syncMutex !== undefined">Synchronisation</span>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<div class="d-flex justify-content-around" ng-if="configro.sync">
|
||||
<button ng-if="configro['sync-type'] === 'GitImporter'" type="button" class="btn btn-info" ng-click="baseSync()" ng-disabled="deepSyncInProgress"><span class="glyphicon glyphicon-repeat" aria-hidden="true"></span> Pull</button>
|
||||
<pre style="background: black; color: white;" class="p-2" ng-if="syncStatus.lastError">{{ syncStatus.lastError }}</pre>
|
||||
|
||||
<div class="d-flex justify-content-around" ng-if="syncStatus.sync">
|
||||
<div class="btn-group dropright">
|
||||
<button type="button" class="btn btn-secondary" ng-click="deepSync()" ng-disabled="deepSyncInProgress"><span class="glyphicon glyphicon-import" aria-hidden="true"></span> Synchronisation intégrale</button>
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" ng-disabled="deepSyncInProgress">
|
||||
|
@ -279,6 +279,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
|
||||
}
|
||||
|
||||
type importFlag struct {
|
||||
origin ExerciceFlag
|
||||
Line int
|
||||
Flag fic.Flag
|
||||
JustifyOf *fic.MCQ_entry
|
||||
@ -392,8 +393,9 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
||||
errs = multierr.Append(errs, berrs)
|
||||
if addedFlag != nil {
|
||||
ret = append(ret, importFlag{
|
||||
Line: nline + 1,
|
||||
Flag: addedFlag,
|
||||
origin: flag,
|
||||
Line: nline + 1,
|
||||
Flag: addedFlag,
|
||||
})
|
||||
}
|
||||
} else if flag.Type == "key" || strings.HasPrefix(flag.Type, "number") || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "radio" || flag.Type == "vector" {
|
||||
@ -401,6 +403,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
||||
errs = multierr.Append(errs, berrs)
|
||||
if addedFlag != nil {
|
||||
ret = append(ret, importFlag{
|
||||
origin: flag,
|
||||
Line: nline + 1,
|
||||
Flag: *addedFlag,
|
||||
Choices: choices,
|
||||
@ -462,6 +465,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
||||
errs = multierr.Append(errs, berrs)
|
||||
if addedFlag != nil {
|
||||
ret = append(ret, importFlag{
|
||||
origin: flag,
|
||||
Line: nline + 1,
|
||||
Flag: *addedFlag,
|
||||
JustifyOf: entry,
|
||||
@ -479,8 +483,9 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
||||
}
|
||||
|
||||
ret = append([]importFlag{importFlag{
|
||||
Line: nline + 1,
|
||||
Flag: &addedFlag,
|
||||
origin: flag,
|
||||
Line: nline + 1,
|
||||
Flag: &addedFlag,
|
||||
}}, ret...)
|
||||
}
|
||||
return
|
||||
@ -570,6 +575,10 @@ func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string, exce
|
||||
if int64(fk.ChoicesCost) >= exercice.Gain {
|
||||
errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag's choice_cost is higher than exercice gain")))
|
||||
}
|
||||
|
||||
if raw, ok := flag.origin.Raw.(string); ok && raw == fk.Placeholder {
|
||||
errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag's placeholder and raw are identical")))
|
||||
}
|
||||
}
|
||||
|
||||
// Check dependency loop
|
||||
|
@ -24,6 +24,22 @@ var oneThemeDeepSync sync.Mutex
|
||||
// DeepSyncProgress expose the progression of the depp synchronization (0 = 0%, 255 = 100%).
|
||||
var DeepSyncProgress uint8
|
||||
|
||||
func OneDeepSyncStatus() bool {
|
||||
if oneDeepSync.TryLock() {
|
||||
oneDeepSync.Unlock()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func OneThemeDeepSyncStatus() bool {
|
||||
if oneThemeDeepSync.TryLock() {
|
||||
oneThemeDeepSync.Unlock()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type SyncReport struct {
|
||||
DateStart time.Time `json:"_started"`
|
||||
DateEnd time.Time `json:"_ended"`
|
||||
|
@ -12,6 +12,14 @@ var gitRemoteRe = regexp.MustCompile(`^(?:(?:git@|https://)([\w.@]+)(?:/|:))((?:
|
||||
|
||||
var oneGitPull sync.Mutex
|
||||
|
||||
func OneGitPullStatus() bool {
|
||||
if oneGitPull.TryLock() {
|
||||
oneGitPull.Unlock()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func countFileInDir(dirname string) (int, error) {
|
||||
files, err := os.ReadDir(dirname)
|
||||
if err != nil {
|
||||
@ -57,6 +65,10 @@ func (i GitImporter) Kind() string {
|
||||
return "git originated from " + i.Remote + " on " + i.li.Kind()
|
||||
}
|
||||
|
||||
func (i GitImporter) DeleteDir(filename string) error {
|
||||
return i.li.DeleteDir(filename)
|
||||
}
|
||||
|
||||
func getForgeBaseLink(remote string) (u *url.URL, err error) {
|
||||
res := gitRemoteRe.FindStringSubmatch(remote)
|
||||
u, err = url.Parse(res[2])
|
||||
|
@ -113,3 +113,11 @@ func (i LocalImporter) ListDir(filename string) ([]string, error) {
|
||||
func (i LocalImporter) Stat(filename string) (os.FileInfo, error) {
|
||||
return os.Stat(path.Join(i.Base, filename))
|
||||
}
|
||||
|
||||
type DeletableImporter interface {
|
||||
DeleteDir(filename string) error
|
||||
}
|
||||
|
||||
func (i LocalImporter) DeleteDir(filename string) error {
|
||||
return os.RemoveAll(path.Join(i.Base, filename))
|
||||
}
|
||||
|
@ -20,3 +20,24 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCUKevt/f1n2byv5oH43iQsZ7b4kAATHlHNUF6WMQjk
|
||||
# ?
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICo5yumHfQbMwhZAtEZByQR0xIVcoealS7g4MNTMEVaX roote@roote-VirtualBox
|
||||
|
||||
# cyril.blin
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCit9xqC+/EcC0D+x4Irg/AgnTx9rJDbE4FdKK2Z2vE0nSPzp1MbAtijVi5ndvr/JPlY3jUHeGEZBJHmADXeOvdJl1nvkqry/69phfr4nDYacvH0v66nDTRipqmCmebaYOkfXYG55oy40+6C6DwAGETTYq+PIaRcA/mSf6V9UxKBfnLVqdml7LFYEo1SbihAIFd0EZkwq1wevXdVmrDwF7VLiCin/5Axa6LUOe4l1SAYBpsV8pCY3PQ/KxpgCyJuYj2szhOl0shTPiV48f194xGtYrpx1uGhOHRDx6Rm/5LKY/5DUvKbHCa/ZAdUSoMTnd1TshAPJe1sYKSAAI1xPVmffOgF/Jh98QEuAuFmHfZXVgPdvApJ9r9Ea7gEyN6Xe37emkW1Dond4ARdNdaslVu0iwV6bQnDOGcEdAl3x3seRVRAiPAKp2tAEtVEqu7uFwX6v2mmpE5/uw8rfsl/wNgttjuYa/kJURNkto3bN02XNfzOnXXZ3bRtbNjHEyJQuM= cyril@nixos
|
||||
|
||||
# victor.chartraire
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJgDuQ4FrDuDjoo1Xv7pA0WEev1hhgJ8lXpT/17QXsds celian@DESKTOP-BQA641E
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPfUVWWSUrfuQYofWwvotQip4uqG34dF1Ybn4tTU1l0n celian@DESKTOP-BQA641E
|
||||
|
||||
# hugo.rubio
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC68cnL/66U9dKfGZe3Bfr+ggosVXZh5OymRuHEdl26lYr3uJAQ7tKtfgXXyfuIW87vxGuzZUOEAnDafJx0J24BVyTwNjJVRarB8efHVIvlL/S9hHPVKWaQyo1ZIgizCiRdljJLWweZKjRE6Yr8xPc6zlC7grQ0wG/WGtAKaedhxYUZcCkdxPMz9Jf2ufx23DB/ALUZ63KVwbWIof8LPtlBP3G74ooeuykR+BTH+6NinoOWlL94JsSTAhuVnKsSnG1eQ742Cv4XhhGleAStP3wFhyakdsvCOfw9bKN/z3dXA0nZGzwOqvHyI9OJx01GLHhyykfRL7PtpD8MMwzyq3r5 hugo0@LAPTOP-C4LN612T
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1BINotXNgrTmHrep0BbxtUIAvCXOjSN8GrfTq03Sfd ryugo@LAPTOP-C4LN612T
|
||||
|
||||
# alexandra.delin
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOJGg902VI7w6nOSiGm6qyUnPn9w3sD3VKlMin3fbYK6 alex@AlexandraSRS
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIArw17NpbJRNf8m9eji5K+GcD8oiyJi/3ygceojEVtBL alexa@Alexandra_SRS
|
||||
|
||||
# maxence.michot
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCqiBbRBWhnG5+LU/pKevR5oJV3+2ZQRQ6Fu87F8873DkKP0okvAYqVXYEDIm/bq9+VocZHI8y9O+zbCXwIqdWRGESJFiM7r1L3HtimFlmWhzM+nzduS8dWGcGGa9XCw+OBHFMGUFEMyzNbi/YcF7QGybOGETdcyzXQng1JtW4IURdrFJm/0/EavRwO2O2xTexmHv8l7JAe0ChteeuDatADrWgNnoUXIqeQQIsPxKQJ9D4QZ7+QrD5K2iLCx8sqC8Eu10p7cRqB3pCcBzKc6MEOgA1FXJzYgNtWkkLR9s7cOVjKL6mL3DuYQla3VaSvkTh8SAKEkadgYXpYifF6ygL298EmhQIrqRdjDYAm+VDGfCvmtcEW4NM68tt74xLuhdDutLq9Riqx4n2pGPo8ws8ecCokcu5/ESGxpZwYmY+nMj8392yH4sgmW/nrs0WmAqawdwlDj09LIqbYXiGMd5zYYOu8V2nuMKG0zRtRESKbs04Uw2DUd/gTwfP2l5YqTn3K93xDNU2iZY+yLJai3vYaBLHzt6PEvESdhAceYBMTIN2iA6hGZfaMWJ4JHImoqCgwbnJw6qrIPHaj1SCWKWLPMjjYZbwndkH3Iy4oJ6gA6I/vT9YrzNvGYY7rVspa+O0RM+huVH1a0YoXSSzx6dop1k1ZWm7VwRCUJQ2ri+L2jw== thecr@maxence-portable-desktop
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMwyRf67AH/rUzSFsKpFfGDD/La4JqpYk9xCM04kJ6+P maxence@vokunaav
|
||||
|
||||
# leo.blanc-di-pasquale
|
||||
ssh-rsa AAAAB4NzaC1yc2EAAAADAQABAAABgQDBovLxpJA0+gJiAVWrpWmjy9fyEQwOXv3Ytou7C4Y1O3lAyvmmHfBWuW1ZqIY/8yKIpJeSLCoGeN5kyMD9ITvMVcfSAxnDHXj04Q4rJzBGjQ/LF/CInx+HHjJWzGVgJC76m0BS7J0J7g5gAxfprGKEb3Z1kJmCTRQ470Azv6WIPDCw7aLNd1o5JqAsrmWSh/LwYBSapyp3Tk0KjcbxRifvnBJfYLHwNm1ZoaLCGAT3u3TKIifawEomx5QEel/211FaEdbglkeDtRL0YuvKQSuZCf4KSg7xSRQoWHmSuPothE9eQGzEKpJONKViqkxBo12O04cBEhYzQBh7GHmH3U3/9weNUX4EaKJWkfqh43eAnWohN07IXDnANYPRxWL4ITv+MNBWxYL5Zp3Zr85rhJZYhkyKEfcGjvolHaESegnsndhC74QW3eOXazuscAeROQbiMShk5iBIceTsEFTqI02/+XqTs9gXkuq3H3StJ6+9cavDwRyObGx5nVHmhQYnMEE= leo21@Aled
|
||||
|
@ -20,6 +20,8 @@ escape_newline () {
|
||||
sed 's/$/\\n/g' | tr -d '\n'
|
||||
}
|
||||
|
||||
which mkisofs > /dev/null 2> /dev/null || { echo "Please install genisoimage (Debian/Ubuntu) or cdrkit (Alpine)" >&2; exit 1; }
|
||||
|
||||
if [ $# -gt 0 ]
|
||||
then
|
||||
which jq > /dev/null 2> /dev/null || { echo "Please install jq" >&2; exit 1; }
|
||||
|
@ -21,6 +21,8 @@ OLD_KEY=$(cat /run/config/dm-crypt/key)
|
||||
[ "${NEW_KEY}" != "${OLD_KEY}" ] && {
|
||||
read -p "DM-CRYPT key changed in metadata, are you sure you want to erase it? (y/N) " V
|
||||
[ "$V" != "y" ] && [ "$V" != "Y" ] && while true; do
|
||||
mv /boot/imgs/fickit-metadata.iso /boot/imgs/fickit-metadata.iso.skipped
|
||||
cp /boot/imgs/fickit-metadata.iso.bak /boot/imgs/fickit-metadata.iso
|
||||
echo
|
||||
echo "Metadata drive not erased"
|
||||
echo
|
||||
|
@ -4,51 +4,51 @@ kernel:
|
||||
cmdline: "console=ttyS0 console=tty0"
|
||||
|
||||
init:
|
||||
- linuxkit/init:v1.0.0
|
||||
- linuxkit/runc:6062483d748609d505f2bcde4e52ee64a3329f5f
|
||||
- linuxkit/containerd:v1.0.0
|
||||
- linuxkit/ca-certificates:v1.0.0
|
||||
- linuxkit/getty:v1.0.0
|
||||
- linuxkit/init:7135424f6836ee166d1199e88cfb95ee88efaf91
|
||||
- linuxkit/runc:efcece75889aec4e2de0d95ba27ccc46438522b3
|
||||
- linuxkit/containerd:ce79d5d4ab9c46f4763735c6e4ab5c51c3feb5d8
|
||||
- linuxkit/ca-certificates:d4cc1b82c73d272e94d0e71ea375fe56b0c0626a
|
||||
- linuxkit/getty:bae9e3d4861173bacf78f14a4fe44997a430d13b
|
||||
- nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67
|
||||
- nemunaire/kexec:839b4eedfce02a56c581dec2383dc6faff120855
|
||||
|
||||
onboot:
|
||||
- name: mod
|
||||
image: linuxkit/modprobe:v1.0.0
|
||||
image: linuxkit/modprobe:e3de97ac10970edee33faa78d9780117174bd1ac
|
||||
command: ["/bin/sh", "-c", "modprobe xhci_pci ahci intel_lpss_pci i2c_i801 megaraid_sas tg3 bnxt_en"]
|
||||
|
||||
- name: sysctl
|
||||
image: linuxkit/sysctl:v1.0.0
|
||||
image: linuxkit/sysctl:c5f4b4895844b993dce4e8b35fd8263a6b557807
|
||||
binds:
|
||||
- /etc/sysctl.d/01-fic.conf:/etc/sysctl.d/01-fic.conf:ro
|
||||
|
||||
# Metadata
|
||||
- name: metadata
|
||||
image: linuxkit/metadata:v1.0.0
|
||||
image: linuxkit/metadata:f35b5aafc7d19bb6a44a900840727902dad78e44
|
||||
command: ["/usr/bin/metadata", "-v", "cdrom"]
|
||||
|
||||
# Filesystem
|
||||
- name: swap
|
||||
image: linuxkit/swap:v1.0.0
|
||||
image: linuxkit/swap:8a1fd15d56b6ddf67d6d8ce25361178e1f36128b
|
||||
command: ["/sbin/swapon", "/dev/sda3"]
|
||||
- name: dm-crypt
|
||||
image: linuxkit/dm-crypt:d49723bc9d10c5ada9e03b0670f4e57416d5d084
|
||||
image: linuxkit/dm-crypt:19fa6affe9da03afc91694e36d72a4924c65a0e0
|
||||
command: ["/usr/bin/crypto", "-l", "crypt_fic", "/dev/sda4"]
|
||||
binds:
|
||||
- /dev:/dev
|
||||
- /run/config/dm-crypt:/etc/dm-crypt
|
||||
- name: mount
|
||||
image: linuxkit/mount:v1.0.0
|
||||
image: linuxkit/mount:4413ebd50bfbe026058e4a60463259cece2b8bb5
|
||||
command: ["/usr/bin/mountie", "-device", "/dev/mapper/crypt_fic", "/var/lib/fic" ]
|
||||
|
||||
# Network
|
||||
# - name: dhcpcd
|
||||
# image: linuxkit/dhcpcd:v1.0.0
|
||||
# image: linuxkit/dhcpcd:330839488cd122db3c44738e265c035c9729a963
|
||||
# command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
|
||||
# - name: ntp
|
||||
# image: linuxkit/openntpd:v1.0.0
|
||||
# image: linuxkit/openntpd:da26954c2f98a274091e5ed0bbdd2079a77a47c1
|
||||
- name: synchro-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 10.10.10.1/29 dev eth2; ip link set eth2 up;" ]
|
||||
net: new
|
||||
runtime:
|
||||
@ -57,7 +57,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/synchro
|
||||
- name: qa-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip link show eth1 2> /dev/null && { ip a add 10.10.10.1/29 dev eth1; ip link set eth1 up; }; ip a add 172.17.0.6/24 dev vethin-qa; ip link set vethin-qa up" ]
|
||||
net: new
|
||||
runtime:
|
||||
@ -69,7 +69,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/fic-qa
|
||||
- name: admin-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
#command: ["/bin/sh", "-c", "ip link add link eth3 name adminiface type vlan id 99; ip a add 172.16.99.219/24 dev adminiface; ip link set eth3 up; ip link set adminiface up; ip r add default via 172.16.99.1; ip a add 172.17.0.2/24 dev vethin-admin; ip link set vethin-admin up; ping -W 10 -c 1 172.16.99.1;" ]
|
||||
command: ["/bin/sh", "-c", "ip link set eth3 up; while read IP; do ip a add ${IP} dev eth3; done < /run/config/ip_config/backend-admin; ip r add default via $(cat /run/config/ip_config/backend-router); ip a add 172.17.0.2/24 dev vethin-admin; ip link set vethin-admin up; echo 'Waiting for' $(cat /run/config/ip_config/backend-router); ping -W 10 -c 1 $(cat /run/config/ip_config/backend-router); ip link show eth1 2> /dev/null && { ip a add 10.0.0.1/24 dev eth1; ip link set eth1 up; };" ]
|
||||
net: new
|
||||
@ -85,7 +85,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/fic-admin
|
||||
- name: checker-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 172.17.0.3/24 dev vethin-checker; ip link set vethin-checker up;" ]
|
||||
net: new
|
||||
runtime:
|
||||
@ -96,7 +96,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/fic-checker
|
||||
- name: generator-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 172.17.0.5/24 dev vethin-generat; ip link set vethin-generat up;" ]
|
||||
net: new
|
||||
runtime:
|
||||
@ -107,7 +107,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/fic-generator
|
||||
- name: mysql-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 172.17.0.4/24 dev vethin-db; ip link set vethin-db up;" ]
|
||||
net: new
|
||||
runtime:
|
||||
@ -118,7 +118,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/db
|
||||
- name: bridge-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 172.17.0.1/24 dev br0; ip link set veth-admin master br0; ip link set veth-checker master br0; ip link set veth-generator master br0; ip link set veth-db master br0; ip link set veth-qa master br0; ip link set br0 up; ip link set veth-admin up; ip link set veth-checker up; ip link set veth-generator up; ip link set veth-db up; ip link set veth-qa up;" ]
|
||||
runtime:
|
||||
interfaces:
|
||||
@ -126,7 +126,7 @@ onboot:
|
||||
add: bridge
|
||||
|
||||
- name: firewall-synchro
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-synchro.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ]
|
||||
binds:
|
||||
- /etc/iptables/rules-synchro.v4:/etc/iptables/rules-synchro.v4:ro
|
||||
@ -136,7 +136,7 @@ onboot:
|
||||
mkdir:
|
||||
- /var/lib/fic/teams
|
||||
- name: firewall-admin
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-admin.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ]
|
||||
binds:
|
||||
- /etc/iptables/rules-admin.v4:/etc/iptables/rules-admin.v4:ro
|
||||
@ -153,17 +153,26 @@ onboot:
|
||||
mkdir:
|
||||
- /var/lib/fic/secrets
|
||||
|
||||
- name: create-ssh-keys
|
||||
image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285
|
||||
command: ["/bin/sh", "-c", "touch /etc/ssh/sshd_config && ssh-keygen -A"]
|
||||
binds:
|
||||
- /var/lib/fic/ssh:/etc/ssh
|
||||
runtime:
|
||||
mkdir:
|
||||
- /var/lib/fic/ssh
|
||||
|
||||
services:
|
||||
# - name: getty
|
||||
# image: linuxkit/getty:v1.0.0
|
||||
# image: linuxkit/getty:bae9e3d4861173bacf78f14a4fe44997a430d13b
|
||||
# env:
|
||||
# - INSECURE=true
|
||||
|
||||
# Enable acpi to shutdown on power events
|
||||
- name: acpid
|
||||
image: linuxkit/acpid:v1.0.0
|
||||
image: linuxkit/acpid:6379700e2f3341250432e37a4cac36e35c7caac8
|
||||
- name: rngd
|
||||
image: linuxkit/rngd:v1.0.0
|
||||
image: linuxkit/rngd:814d1a3a76e84eae01a94575c038fd22652f94e3
|
||||
- name: db
|
||||
image: mariadb:11
|
||||
command: ["/bin/bash", "/usr/local/bin/docker-entrypoint.sh", "mariadbd"]
|
||||
@ -228,7 +237,7 @@ services:
|
||||
- /var/lib/fic/generator:/srv/GENERATOR:ro
|
||||
- /var/lib/fic/pki:/srv/PKI
|
||||
- /var/lib/fic/settings:/srv/SETTINGS
|
||||
- /var/lib/fic/submissions:/srv/submissions:ro
|
||||
- /var/lib/fic/submissions:/srv/submissions
|
||||
- /var/lib/fic/sync:/srv/SYNC
|
||||
- /var/lib/fic/teams:/srv/TEAMS
|
||||
net: /run/netns/fic-admin
|
||||
@ -269,7 +278,10 @@ services:
|
||||
binds:
|
||||
- /etc/hosts:/etc/hosts:ro
|
||||
- /var/lib/fic/generator:/srv/GENERATOR:ro
|
||||
# Uncomment this to disallow registrations
|
||||
- /var/lib/fic/teams:/srv/TEAMS:ro
|
||||
# Uncomment this to allow registrations
|
||||
#- /var/lib/fic/teams:/srv/TEAMS
|
||||
- /var/lib/fic/secrets/mysql_password:/run/secrets/mysql_password:ro
|
||||
- /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro
|
||||
- /var/lib/fic/submissions:/srv/submissions
|
||||
@ -361,7 +373,6 @@ services:
|
||||
- /var/lib/fic/files
|
||||
- /var/lib/fic/pki/shared
|
||||
- /var/lib/fic/settingsdist
|
||||
- /var/lib/fic/ssh
|
||||
- /var/lib/fic/submissions
|
||||
- /var/lib/fic/teams
|
||||
- /var/log/frontend
|
||||
|
@ -4,47 +4,47 @@ kernel:
|
||||
cmdline: "console=ttyS0 console=tty0"
|
||||
|
||||
init:
|
||||
- linuxkit/init:v1.0.0
|
||||
- linuxkit/runc:6062483d748609d505f2bcde4e52ee64a3329f5f
|
||||
- linuxkit/containerd:v1.0.0
|
||||
- linuxkit/ca-certificates:v1.0.0
|
||||
- linuxkit/getty:v1.0.0
|
||||
- linuxkit/init:7135424f6836ee166d1199e88cfb95ee88efaf91
|
||||
- linuxkit/runc:efcece75889aec4e2de0d95ba27ccc46438522b3
|
||||
- linuxkit/containerd:ce79d5d4ab9c46f4763735c6e4ab5c51c3feb5d8
|
||||
- linuxkit/ca-certificates:d4cc1b82c73d272e94d0e71ea375fe56b0c0626a
|
||||
- linuxkit/getty:bae9e3d4861173bacf78f14a4fe44997a430d13b
|
||||
- nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67
|
||||
- nemunaire/kexec:839b4eedfce02a56c581dec2383dc6faff120855
|
||||
- nemunaire/fic-frontend-ui:latest
|
||||
|
||||
onboot:
|
||||
- name: mod
|
||||
image: linuxkit/modprobe:v1.0.0
|
||||
image: linuxkit/modprobe:e3de97ac10970edee33faa78d9780117174bd1ac
|
||||
command: ["/bin/sh", "-c", "modprobe xhci_pci ahci intel_lpss_pci i2c_i801 megaraid_sas tg3 bnxt_en"]
|
||||
|
||||
- name: sysctl
|
||||
image: linuxkit/sysctl:v1.0.0
|
||||
image: linuxkit/sysctl:c5f4b4895844b993dce4e8b35fd8263a6b557807
|
||||
|
||||
# Metadata
|
||||
- name: metadata
|
||||
image: linuxkit/metadata:v1.0.0
|
||||
image: linuxkit/metadata:f35b5aafc7d19bb6a44a900840727902dad78e44
|
||||
command: ["/usr/bin/metadata", "-v", "cdrom"]
|
||||
|
||||
# Filesystem
|
||||
- name: swap
|
||||
image: linuxkit/swap:v1.0.0
|
||||
image: linuxkit/swap:8a1fd15d56b6ddf67d6d8ce25361178e1f36128b
|
||||
command: ["/sbin/swapon", "/dev/sda3"]
|
||||
- name: dm-crypt
|
||||
image: linuxkit/dm-crypt:d49723bc9d10c5ada9e03b0670f4e57416d5d084
|
||||
image: linuxkit/dm-crypt:19fa6affe9da03afc91694e36d72a4924c65a0e0
|
||||
command: ["/usr/bin/crypto", "-l", "crypt_fic", "/dev/sda4"]
|
||||
binds:
|
||||
- /dev:/dev
|
||||
- /run/config/dm-crypt:/etc/dm-crypt
|
||||
- name: mount
|
||||
image: linuxkit/mount:v1.0.0
|
||||
image: linuxkit/mount:4413ebd50bfbe026058e4a60463259cece2b8bb5
|
||||
command: ["/usr/bin/mountie", "-device", "/dev/mapper/crypt_fic", "/var/lib/fic" ]
|
||||
|
||||
# Network
|
||||
# - name: ntp
|
||||
# image: linuxkit/openntpd:v1.0.0
|
||||
# image: linuxkit/openntpd:da26954c2f98a274091e5ed0bbdd2079a77a47c1
|
||||
- name: nginx-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 172.17.1.2/24 dev vethin-nginx; ip link set vethin-nginx up;" ]
|
||||
net: new
|
||||
runtime:
|
||||
@ -55,7 +55,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/nginx
|
||||
- name: frontal-ip-setup # without bonding
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip link set name bond-frontal eth3; ip link set bond-frontal up; while read IP; do ip a add ${IP} dev bond-frontal; done < /run/config/ip_config/frontend-players; ip r add default via $(cat /run/config/ip_config/frontend-router); ip link add link bond-frontal name internet type vlan id 4; ip a add 10.10.10.2/29 dev internet; ip link set internet up;" ]
|
||||
net: /run/netns/nginx
|
||||
binds:
|
||||
@ -67,7 +67,7 @@ onboot:
|
||||
- name: eth3
|
||||
# - name: eth4
|
||||
# - name: frontal-ip-setup # with bonding
|
||||
# image: linuxkit/ip:v1.0.0
|
||||
# image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
# command: ["/bin/sh", "-c", "ip link set dev bond-frontal type bond mode balance-alb; ip link set bond-frontal up; ifenslave bond-frontal eth1 eth2 eth3 eth4; while read IP; do ip a add ${IP} dev bond-frontal; done < /run/config/ip_config/frontend-players; ip r add default via $(cat /run/config/ip_config/frontend-router); ip link add link bond-frontal name internet type vlan id 4; ip link set internet up; sysctl -w net.ipv4.ip_forward=1;" ]
|
||||
# net: /run/netns/nginx
|
||||
# binds:
|
||||
@ -81,7 +81,7 @@ onboot:
|
||||
# - name: bond-frontal
|
||||
# add: bond
|
||||
- name: receiver-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 172.17.1.3/24 dev vethin-receiver; ip link set vethin-receiver up;" ]
|
||||
net: new
|
||||
runtime:
|
||||
@ -92,7 +92,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/fic-receiver
|
||||
- name: sshd-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 10.10.10.2/29 dev eth2; ip link set eth2 up;" ]
|
||||
net: new
|
||||
runtime:
|
||||
@ -101,7 +101,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/sshd
|
||||
- name: auth-ip-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 172.17.1.4/24 dev vethin-auth; ip link set vethin-auth up;" ]
|
||||
net: new
|
||||
runtime:
|
||||
@ -112,7 +112,7 @@ onboot:
|
||||
bindNS:
|
||||
net: /run/netns/auth
|
||||
- name: bridge-setup
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/sh", "-c", "ip a add 172.17.1.1/24 dev br0; ip link set veth-nginx master br0; ip link set veth-receiver master br0; ip link set veth-auth master br0; ip link set br0 up; ip link set veth-nginx up; ip link set veth-receiver up; ip link set veth-auth up;" ]
|
||||
runtime:
|
||||
interfaces:
|
||||
@ -120,7 +120,7 @@ onboot:
|
||||
add: bridge
|
||||
|
||||
- name: firewall-frontal
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-frontal.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6; [ -f /run/config/remote_sync/destination ] && /sbin/iptables -I OUTPUT 7 -o bond-frontal -d $(cat /run/config/remote_sync/destination | tr -d '\n') -p tcp -m tcp --dport https -j ACCEPT;" ]
|
||||
binds:
|
||||
- /etc/iptables/rules-frontal.v4:/etc/iptables/rules-frontal.v4:ro
|
||||
@ -129,26 +129,35 @@ onboot:
|
||||
- /run/config/remote_sync/:/run/config/remote_sync/:ro
|
||||
net: /run/netns/nginx
|
||||
- name: firewall-sshd
|
||||
image: linuxkit/ip:v1.0.0
|
||||
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
|
||||
command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-sshd.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ]
|
||||
binds:
|
||||
- /etc/iptables/rules-sshd.v4:/etc/iptables/rules-sshd.v4:ro
|
||||
- /etc/iptables/rules.v6:/etc/iptables/rules.v6:ro
|
||||
net: /run/netns/sshd
|
||||
|
||||
- name: create-ssh-keys
|
||||
image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285
|
||||
command: ["/bin/sh", "-c", "touch /etc/ssh/sshd_config && ssh-keygen -A"]
|
||||
binds:
|
||||
- /var/lib/fic/ssh:/etc/ssh
|
||||
runtime:
|
||||
mkdir:
|
||||
- /var/lib/fic/ssh
|
||||
|
||||
services:
|
||||
# - name: getty
|
||||
# image: linuxkit/getty:v1.0.0
|
||||
# image: linuxkit/getty:bae9e3d4861173bacf78f14a4fe44997a430d13b
|
||||
# env:
|
||||
# - INSECURE=true
|
||||
|
||||
# Enable acpi to shutdown on power events
|
||||
- name: acpid
|
||||
image: linuxkit/acpid:v1.0.0
|
||||
image: linuxkit/acpid:6379700e2f3341250432e37a4cac36e35c7caac8
|
||||
- name: rngd
|
||||
image: linuxkit/rngd:v1.0.0
|
||||
image: linuxkit/rngd:814d1a3a76e84eae01a94575c038fd22652f94e3
|
||||
- name: dhcpcd
|
||||
image: linuxkit/dhcpcd:v1.0.0
|
||||
image: linuxkit/dhcpcd:330839488cd122db3c44738e265c035c9729a963
|
||||
net: /run/netns/nginx
|
||||
binds:
|
||||
- /etc/dhcpcd.conf:/dhcpcd.conf:ro
|
||||
@ -257,7 +266,6 @@ services:
|
||||
- /var/lib/fic/files
|
||||
- /var/lib/fic/pki
|
||||
- /var/lib/fic/settingsdist
|
||||
- /var/lib/fic/ssh
|
||||
- /var/lib/fic/submissions
|
||||
- /var/lib/fic/teams
|
||||
|
||||
|
@ -7,9 +7,9 @@ kernel:
|
||||
init:
|
||||
- nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67
|
||||
- nemunaire/syslinux:086f221f281d577d300949aa1094fb20c5cd90dc
|
||||
- linuxkit/format:v1.0.0
|
||||
- linuxkit/dm-crypt:d49723bc9d10c5ada9e03b0670f4e57416d5d084
|
||||
- linuxkit/metadata:v1.0.0
|
||||
- linuxkit/format:8f487d728959192289e0783784fc2b185eadbc82
|
||||
- linuxkit/dm-crypt:ad2a05dcffa28ef809a61aa27ba230c82f02f603
|
||||
- linuxkit/metadata:83cda7b43112b201613084ea8b7fab585b6e5549
|
||||
- alpine:latest
|
||||
|
||||
files:
|
||||
|
@ -6,7 +6,7 @@ kernel:
|
||||
|
||||
init:
|
||||
- nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67
|
||||
- linuxkit/metadata:v1.0.0
|
||||
- linuxkit/metadata:f35b5aafc7d19bb6a44a900840727902dad78e44
|
||||
- alpine:latest
|
||||
|
||||
|
||||
|
582
frontend/fic/package-lock.json
generated
582
frontend/fic/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,9 +12,9 @@
|
||||
"@sveltejs/adapter-static": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@sveltestrap/sveltestrap": "^6.2.1",
|
||||
"@sveltestrap/sveltestrap": "^7.0.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"prettier": "^3.0.0",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
|
@ -27,15 +27,15 @@
|
||||
</CardBody>
|
||||
<ListGroup flush class="border-secondary">
|
||||
{#each files as file, index}
|
||||
<ListGroupItem tag="a" href={file.path} target={(file.name.endsWith(".txt") || file.name.endsWith(".jpg") || file.name.endsWith(".png") || file.name.endsWith(".pdf"))?"_blank":"_self"} class="d-flex">
|
||||
<ListGroupItem tag="a" href={file.path} target={(file.name.endsWith(".txt") || file.name.endsWith(".xml") || file.name.endsWith(".jpg") || file.name.endsWith(".png") || file.name.endsWith(".pdf"))?"_blank":"_self"} class="d-flex">
|
||||
<h1 class="me-3">
|
||||
<Icon name="arrow-down-circle" />
|
||||
</h1>
|
||||
<div style="min-width: 0">
|
||||
<h4 class="fw-bold"><samp>{file.name}</samp></h4>
|
||||
{#if file.disclamer}
|
||||
<div class="file-disclamer text-warning">
|
||||
{file.disclamer}
|
||||
{#if file.disclaimer}
|
||||
<div class="file-disclaimer text-warning">
|
||||
{file.disclaimer}
|
||||
</div>
|
||||
{/if}
|
||||
<nobr>
|
||||
@ -61,10 +61,10 @@
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.file-disclamer {
|
||||
.file-disclaimer {
|
||||
display: none;
|
||||
}
|
||||
:global(.list-group-item:hover .file-disclamer) {
|
||||
:global(.list-group-item:hover .file-disclaimer) {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
@ -2,6 +2,12 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
||||
|
||||
/** @type {import('vite').UserConfig} */
|
||||
const config = {
|
||||
server: {
|
||||
hmr: {
|
||||
port: 10000
|
||||
}
|
||||
},
|
||||
|
||||
plugins: [sveltekit()]
|
||||
};
|
||||
|
||||
|
36
go.mod
36
go.mod
@ -1,24 +1,26 @@
|
||||
module srs.epita.fr/fic-server
|
||||
|
||||
go 1.18
|
||||
go 1.21
|
||||
|
||||
toolchain go1.23.4
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.0
|
||||
github.com/asticode/go-astisub v0.30.0
|
||||
github.com/asticode/go-astisub v0.32.0
|
||||
github.com/cenkalti/dominantcolor v1.0.3
|
||||
github.com/gin-contrib/sessions v1.0.1
|
||||
github.com/gin-contrib/sessions v1.0.2
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-git/go-git/v5 v5.12.0
|
||||
github.com/go-git/go-git/v5 v5.13.2
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/studio-b12/gowebdav v0.9.0
|
||||
github.com/studio-b12/gowebdav v0.10.0
|
||||
github.com/u2takey/ffmpeg-go v0.5.0
|
||||
github.com/yuin/goldmark v1.7.8
|
||||
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/image v0.22.0
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
golang.org/x/image v0.24.0
|
||||
golang.org/x/oauth2 v0.26.0
|
||||
gopkg.in/fsnotify.v1 v1.4.7
|
||||
)
|
||||
|
||||
@ -26,7 +28,7 @@ require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.5 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||
github.com/asticode/go-astikit v0.20.0 // indirect
|
||||
github.com/asticode/go-astits v1.8.0 // indirect
|
||||
@ -38,14 +40,14 @@ require (
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
@ -67,20 +69,20 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/u2takey/go-utils v0.3.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
|
58
go.sum
58
go.sum
@ -31,6 +31,10 @@ github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCv
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
|
||||
github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
|
||||
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
|
||||
@ -64,6 +68,8 @@ github.com/asticode/go-astisub v0.29.0 h1:1Pjz+TkaAwjPPoH88bQ6nnoXjin+WO2zZV95C/
|
||||
github.com/asticode/go-astisub v0.29.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
|
||||
github.com/asticode/go-astisub v0.30.0 h1:z4k2Y+V+rlCE8qk3uw/nie56KXxJaL1/GwTP+9F2GMM=
|
||||
github.com/asticode/go-astisub v0.30.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
|
||||
github.com/asticode/go-astisub v0.32.0 h1:i1RHVQyTxSAuX0X3YC5zIyWruVZorS3cDXxqxYa0qss=
|
||||
github.com/asticode/go-astisub v0.32.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
|
||||
github.com/asticode/go-astits v1.8.0 h1:rf6aiiGn/QhlFjNON1n5plqF3Fs025XLUwiQ0NB6oZg=
|
||||
github.com/asticode/go-astits v1.8.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ=
|
||||
github.com/aws/aws-sdk-go v1.38.20 h1:QbzNx/tdfATbdKfubBpkt84OM6oBkxQZRw6+bW2GyeA=
|
||||
@ -107,6 +113,8 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -130,6 +138,8 @@ github.com/gin-contrib/sessions v1.0.0 h1:r5GLta4Oy5xo9rAwMHx8B4wLpeRGHMdz9NafzJ
|
||||
github.com/gin-contrib/sessions v1.0.0/go.mod h1:DN0f4bvpqMQElDdi+gNGScrP2QEI04IErRyMFyorUOI=
|
||||
github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI=
|
||||
github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM=
|
||||
github.com/gin-contrib/sessions v1.0.2 h1:UaIjUvTH1cMeOdj3in6dl+Xb6It8RiKRF9Z1anbUyCA=
|
||||
github.com/gin-contrib/sessions v1.0.2/go.mod h1:KxKxWqWP5LJVDCInulOl4WbLzK2KSPlLesfZ66wRvMs=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
@ -158,6 +168,10 @@ github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8
|
||||
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
|
||||
github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||
@ -187,6 +201,10 @@ github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3c
|
||||
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
|
||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
|
||||
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
|
||||
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
|
||||
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
@ -340,6 +358,8 @@ github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI=
|
||||
github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@ -368,6 +388,8 @@ github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2
|
||||
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
|
||||
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
|
||||
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -398,6 +420,8 @@ github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 h1:VsBj3UD2xyA
|
||||
github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
|
||||
github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU=
|
||||
github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
|
||||
github.com/studio-b12/gowebdav v0.10.0 h1:Yewz8FFiadcGEu4hxS/AAJQlHelndqln1bns3hcJIYc=
|
||||
github.com/studio-b12/gowebdav v0.10.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/u2takey/ffmpeg-go v0.4.1 h1:l5ClIwL3N2LaH1zF3xivb3kP2HW95eyG5xhHE1JdZ9Y=
|
||||
@ -522,6 +546,14 @@ golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
|
||||
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
|
||||
golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
|
||||
@ -567,6 +599,10 @@ golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
|
||||
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
|
||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
@ -633,6 +669,10 @@ golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y=
|
||||
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
|
||||
golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU=
|
||||
@ -681,6 +721,10 @@ golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
|
||||
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -690,6 +734,10 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -768,6 +816,12 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@ -815,6 +869,10 @@ golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
21
libfic/db.go
21
libfic/db.go
@ -414,6 +414,7 @@ CREATE TABLE IF NOT EXISTS exercice_solved(
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS exercice_tries(
|
||||
id_try INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
id_exercice INTEGER NOT NULL,
|
||||
id_team INTEGER NOT NULL,
|
||||
time TIMESTAMP NOT NULL,
|
||||
@ -423,6 +424,26 @@ CREATE TABLE IF NOT EXISTS exercice_tries(
|
||||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
|
||||
FOREIGN KEY(id_team) REFERENCES teams(id_team)
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS exercice_tries_flags(
|
||||
id_try INTEGER NOT NULL,
|
||||
id_flag INTEGER NOT NULL,
|
||||
FOREIGN KEY(id_try) REFERENCES exercice_tries(id_try) ON DELETE CASCADE,
|
||||
FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag) ON DELETE CASCADE
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS exercice_tries_mcq(
|
||||
id_try INTEGER NOT NULL,
|
||||
id_mcq INTEGER NOT NULL,
|
||||
FOREIGN KEY(id_try) REFERENCES exercice_tries(id_try) ON DELETE CASCADE,
|
||||
FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq) ON DELETE CASCADE
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package fic
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
@ -444,11 +445,25 @@ func (e *Exercice) GetOrdinal() (int, error) {
|
||||
}
|
||||
|
||||
// NewTry registers a solving attempt for the given Team.
|
||||
func (e *Exercice) NewTry(t *Team, cksum []byte) error {
|
||||
if _, err := DBExec("INSERT INTO exercice_tries (id_exercice, id_team, time, cksum) VALUES (?, ?, ?, ?)", e.Id, t.Id, time.Now(), cksum); err != nil {
|
||||
return err
|
||||
func (e *Exercice) NewTry(t *Team, cksum []byte, flags ...Flag) (int64, error) {
|
||||
if res, err := DBExec("INSERT INTO exercice_tries (id_exercice, id_team, time, cksum) VALUES (?, ?, ?, ?)", e.Id, t.Id, time.Now(), cksum); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nil
|
||||
return res.LastInsertId()
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Exercice) NewTryFlag(tryid int64, flags ...Flag) {
|
||||
for _, flag := range flags {
|
||||
if fk, ok := flag.(*FlagKey); ok {
|
||||
if _, err := DBExec("INSERT INTO exercice_tries_flags (id_try, id_flag) VALUES (?, ?)", tryid, fk.Id); err != nil {
|
||||
log.Println("Unable to add detailed try: ", err.Error())
|
||||
}
|
||||
} else if fm, ok := flag.(*MCQ); ok {
|
||||
if _, err := DBExec("INSERT INTO exercice_tries_mcq (id_try, id_mcq) VALUES (?, ?)", tryid, fm.Id); err != nil {
|
||||
log.Println("Unable to add detailed try: ", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -550,7 +565,7 @@ func (e *Exercice) MCQSolved() (res []int64) {
|
||||
// CheckResponse, given both flags and MCQ responses, figures out if thoses are correct (or if they are previously solved).
|
||||
// In the meanwhile, CheckResponse registers good answers given (but it does not mark the challenge as solved at the end).
|
||||
func (e *Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq map[int]bool, t *Team) (bool, error) {
|
||||
if err := e.NewTry(t, cksum); err != nil {
|
||||
if tryId, err := e.NewTry(t, cksum); err != nil {
|
||||
return false, err
|
||||
} else if flags, err := e.GetFlagKeys(); err != nil {
|
||||
return false, err
|
||||
@ -565,6 +580,10 @@ func (e *Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
|
||||
|
||||
// Check MCQs
|
||||
for _, mcq := range mcqs {
|
||||
if mcq.HasOneEntry(respmcq) {
|
||||
e.NewTryFlag(tryId, mcq)
|
||||
}
|
||||
|
||||
if d := mcq.Check(respmcq); d > 0 {
|
||||
if !PartialValidation || t.HasPartiallySolved(mcq) == nil {
|
||||
valid = false
|
||||
@ -589,15 +608,21 @@ func (e *Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
|
||||
for _, flag := range flags {
|
||||
if res, ok := respflags[flag.Id]; !ok && (!PartialValidation || t.HasPartiallySolved(flag) == nil) {
|
||||
valid = valid && flag.IsOptionnal()
|
||||
} else if flag.Check([]byte(res)) != 0 {
|
||||
if !PartialValidation || t.HasPartiallySolved(flag) == nil {
|
||||
valid = valid && flag.IsOptionnal()
|
||||
} else if ok {
|
||||
if len(res) > 0 {
|
||||
e.NewTryFlag(tryId, flag)
|
||||
}
|
||||
} else {
|
||||
err := flag.FoundBy(t)
|
||||
if err == nil {
|
||||
// err is unicity issue, probably flag already found
|
||||
goodResponses += 1
|
||||
|
||||
if flag.Check([]byte(res)) != 0 {
|
||||
if !PartialValidation || t.HasPartiallySolved(flag) == nil {
|
||||
valid = valid && flag.IsOptionnal()
|
||||
}
|
||||
} else {
|
||||
err := flag.FoundBy(t)
|
||||
if err == nil {
|
||||
// err is unicity issue, probably flag already found
|
||||
goodResponses += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,8 @@ func (e *Exercice) AppendHistoryItem(tId int64, kind string, secondary *int64) e
|
||||
if kind == "tries" {
|
||||
bid := make([]byte, 5)
|
||||
binary.LittleEndian.PutUint32(bid, rand.Uint32())
|
||||
return (&Exercice{Id: e.Id}).NewTry(team, bid)
|
||||
_, err = (&Exercice{Id: e.Id}).NewTry(team, bid)
|
||||
return err
|
||||
} else if kind == "hint" && secondary != nil {
|
||||
return team.OpenHint(&EHint{Id: *secondary})
|
||||
} else if kind == "wchoices" && secondary != nil {
|
||||
|
@ -392,7 +392,12 @@ func (f *EFile) GetDepends() ([]Flag, error) {
|
||||
|
||||
// CheckFileOnDisk recalculates the hash of the file on disk.
|
||||
func (f *EFile) CheckFileOnDisk() error {
|
||||
if _, size, err := checkFileHash(path.Join(FilesDir, f.Path), f.Checksum); err != nil {
|
||||
firstChecksum := f.Checksum
|
||||
if len(f.ChecksumShown) > 0 {
|
||||
firstChecksum = f.ChecksumShown
|
||||
}
|
||||
|
||||
if _, size, err := checkFileHash(path.Join(FilesDir, f.Path), firstChecksum); size > 0 && err != nil {
|
||||
return err
|
||||
} else if size == 0 {
|
||||
if _, _, err := checkFileHash(path.Join(FilesDir, f.Path+".gz"), f.Checksum); err != nil {
|
||||
@ -400,9 +405,17 @@ func (f *EFile) CheckFileOnDisk() error {
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(FilesDir, f.Path+".gz")); !os.IsNotExist(err) {
|
||||
if _, _, err = checkFileHash(path.Join(FilesDir, f.Path+".gz"), f.Checksum); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GunzipFileOnDisk gunzip a compressed file.
|
||||
|
@ -14,6 +14,9 @@ type Flag interface {
|
||||
Check(val interface{}) int
|
||||
IsOptionnal() bool
|
||||
FoundBy(t *Team) error
|
||||
NbTries() (int64, error)
|
||||
TeamsOnIt() ([]int64, error)
|
||||
DeleteTries() error
|
||||
}
|
||||
|
||||
// GetFlag returns a list of flags comming with the challenge.
|
||||
|
@ -230,6 +230,54 @@ func (k *FlagKey) RecoverId() (Flag, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// NbTries returns the flag resolution statistics.
|
||||
func (k *FlagKey) NbTries() (tries int64, err error) {
|
||||
err = DBQueryRow("SELECT COUNT(*) AS tries FROM exercice_tries_flags WHERE id_flag = ?", k.Id).Scan(&tries)
|
||||
return
|
||||
}
|
||||
|
||||
func (k *FlagKey) TeamsOnIt() ([]int64, error) {
|
||||
if rows, err := DBQuery("SELECT DISTINCT M.id_team FROM exercice_tries_flags F INNER JOIN exercice_tries T ON T.id_try = F.id_try INNER JOIN teams M ON M.id_team = T.id_team WHERE id_flag = ?", k.Id); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
teams := []int64{}
|
||||
for rows.Next() {
|
||||
var idteam int64
|
||||
if err := rows.Scan(&idteam); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
teams = append(teams, idteam)
|
||||
}
|
||||
|
||||
return teams, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (k *FlagKey) DeleteTries() error {
|
||||
if rows, err := DBQuery("SELECT id_try FROM exercice_tries_flags WHERE id_flag = ?", k.Id); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var idtry int64
|
||||
err = rows.Scan(&idtry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = DBExec("DELETE FROM exercice_tries WHERE id_try = ?", idtry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AddFlagKey creates and fills a new struct Flag, from a hashed flag, and registers it into the database.
|
||||
func (k *FlagKey) Create(e *Exercice) (Flag, error) {
|
||||
// Check the regexp compile
|
||||
|
@ -71,6 +71,18 @@ func (k *FlagLabel) RecoverId() (Flag, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (k *FlagLabel) NbTries() (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (k *FlagLabel) TeamsOnIt() ([]int64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (k *FlagLabel) DeleteTries() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFlagLabel creates and fills a new struct Flag and registers it into the database.
|
||||
func (k *FlagLabel) Create(e *Exercice) (Flag, error) {
|
||||
if res, err := DBExec("INSERT INTO exercice_flag_labels (id_exercice, ordre, label, variant) VALUES (?, ?, ?, ?)", e.Id, k.Order, k.Label, k.Variant); err != nil {
|
||||
|
@ -136,6 +136,54 @@ func (m *MCQ) RecoverId() (Flag, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// NbTries returns the MCQ resolution statistics.
|
||||
func (m *MCQ) NbTries() (tries int64, err error) {
|
||||
err = DBQueryRow("SELECT COUNT(*) AS tries FROM exercice_tries_mcq WHERE id_mcq = ?", m.Id).Scan(&tries)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *MCQ) TeamsOnIt() ([]int64, error) {
|
||||
if rows, err := DBQuery("SELECT DISTINCT M.id_team FROM exercice_tries_mcq F INNER JOIN exercice_tries T ON T.id_try = F.id_try INNER JOIN teams M ON M.id_team = T.id_team WHERE id_mcq = ?", m.Id); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
teams := []int64{}
|
||||
for rows.Next() {
|
||||
var idteam int64
|
||||
if err := rows.Scan(&idteam); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
teams = append(teams, idteam)
|
||||
}
|
||||
|
||||
return teams, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MCQ) DeleteTries() error {
|
||||
if rows, err := DBQuery("SELECT id_try FROM exercice_tries_mcq WHERE id_mcq = ?", m.Id); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var idtry int64
|
||||
err = rows.Scan(&idtry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = DBExec("DELETE FROM exercice_tries WHERE id_try = ?", idtry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create registers a MCQ into the database and recursively add its entries.
|
||||
func (m *MCQ) Create(e *Exercice) (Flag, error) {
|
||||
if res, err := DBExec("INSERT INTO exercice_mcq (id_exercice, ordre, title) VALUES (?, ?, ?)", e.Id, m.Order, m.Title); err != nil {
|
||||
@ -207,7 +255,7 @@ func (m *MCQ) AddEntry(e *MCQ_entry) (*MCQ_entry, error) {
|
||||
|
||||
// Update applies modifications back to the database.
|
||||
func (n *MCQ_entry) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE mcq_entries SET label = ?, response = ? WHERE id_mcq = ?", n.Label, n.Response, n.Id); err != nil {
|
||||
if res, err := DBExec("UPDATE mcq_entries SET label = ?, response = ? WHERE id_mcq_entry = ?", n.Label, n.Response, n.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
@ -319,6 +367,24 @@ func (m *MCQ) IsOptionnal() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the given vals contains at least a response for the given MCQ.
|
||||
func (m *MCQ) HasOneEntry(v interface{}) bool {
|
||||
var vals map[int]bool
|
||||
if va, ok := v.(map[int]bool); !ok {
|
||||
return false
|
||||
} else {
|
||||
vals = va
|
||||
}
|
||||
|
||||
for _, n := range m.Entries {
|
||||
if _, ok := vals[n.Id]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the given vals are the expected ones to validate this flag.
|
||||
func (m *MCQ) Check(v interface{}) int {
|
||||
var vals map[int]bool
|
||||
|
@ -37,6 +37,7 @@ type myTeamFile struct {
|
||||
Checksum string `json:"checksum"`
|
||||
Compressed bool `json:"compressed,omitempty"`
|
||||
Size int64 `json:"size"`
|
||||
Disclaimer string `json:"disclaimer,omitempty"`
|
||||
}
|
||||
type myTeamHint struct {
|
||||
HintId int64 `json:"id"`
|
||||
@ -213,7 +214,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
||||
cksum = f.ChecksumShown
|
||||
compressed = true
|
||||
}
|
||||
exercice.Files = append(exercice.Files, myTeamFile{path.Join(FilesDir, f.Path), f.Name, hex.EncodeToString(cksum), compressed, f.Size})
|
||||
exercice.Files = append(exercice.Files, myTeamFile{path.Join(FilesDir, f.Path), f.Name, hex.EncodeToString(cksum), compressed, f.Size, f.Disclaimer})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -269,6 +269,7 @@ func GitLab_ExportQA(c *gin.Context) {
|
||||
|
||||
gitlabid, err := GitLab_getExerciceId(exercice)
|
||||
if err != nil {
|
||||
log.Println("Unable to retrieve Gitlab exercice id:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
@ -301,6 +302,7 @@ func GitLab_ExportQA(c *gin.Context) {
|
||||
oauth2Token := c.MustGet("gitlab-token").(*oauth2.Token)
|
||||
iid, err := gitlab_newIssue(c.Request.Context(), oauth2Token, gitlabid, &issue)
|
||||
if err != nil {
|
||||
log.Println("Unable to create Gitlab issue:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
106
qa/api/todo.go
106
qa/api/todo.go
@ -21,6 +21,7 @@ func declareTodoRoutes(router *gin.RouterGroup) {
|
||||
|
||||
func declareTodoManagerRoutes(router *gin.RouterGroup) {
|
||||
router.POST("/qa_assign_work", assignWork)
|
||||
router.DELETE("/qa_assign_work", deleteAssignedWork)
|
||||
router.POST("/qa_my_exercices.json", addQAView)
|
||||
router.POST("/qa_work.json", createQATodo)
|
||||
|
||||
@ -241,10 +242,13 @@ func deleteQATodo(c *gin.Context) {
|
||||
}
|
||||
|
||||
type QAAssignWork struct {
|
||||
Turns int `json:"turns"`
|
||||
Start int `json:"start"`
|
||||
TeamPrefix string `json:"team_prefix"`
|
||||
TeamAssistants string `json:"team_assistants"`
|
||||
Turns int `json:"turns"`
|
||||
Start int `json:"start"`
|
||||
TeamPrefix string `json:"team_prefix"`
|
||||
TeamAssistants string `json:"team_assistants"`
|
||||
OnlyExercices bool `json:"only_exercices"`
|
||||
WithoutStandaloneExercices bool `json:"without_standalone_exercices"`
|
||||
WithoutThemes bool `json:"without_themes"`
|
||||
}
|
||||
|
||||
func assignWork(c *gin.Context) {
|
||||
@ -278,19 +282,80 @@ func assignWork(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
exercices, err := fic.GetExercices()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetExercices: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list exercices: %s", err.Error())})
|
||||
return
|
||||
var themes []*fic.Theme
|
||||
if !uaw.OnlyExercices && !uaw.WithoutThemes {
|
||||
themes, err = fic.GetThemes()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetThemes: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list themes: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var exercices []*fic.Exercice
|
||||
if uaw.OnlyExercices {
|
||||
exercices, err = fic.GetExercices()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetExercices: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list exercices: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
if uaw.WithoutStandaloneExercices || uaw.WithoutThemes {
|
||||
for i := len(exercices) - 1; i >= 0; i-- {
|
||||
if (exercices[i].IdTheme == nil && uaw.WithoutStandaloneExercices) || (exercices[i].IdTheme != nil && uaw.WithoutThemes) {
|
||||
exercices = append(exercices[:i], exercices[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !uaw.WithoutStandaloneExercices {
|
||||
exercices, err = (&fic.Theme{URLId: "_", Path: "exercices"}).GetExercices()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetStandaloneExercices: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list exercices: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Struct to store reported team (due to owned exercice)
|
||||
var teamIdStack []int64
|
||||
|
||||
for i := 0; i < uaw.Turns; i++ {
|
||||
lasttid := 0
|
||||
for tid, theme := range themes {
|
||||
lasttid = tid
|
||||
team := teams[(uaw.Start+tid+uaw.Turns*i)%len(teams)]
|
||||
|
||||
if len(teamIdStack) > 0 {
|
||||
teamIdStack = append(teamIdStack, team.Id)
|
||||
team, _ = fic.GetTeam(teamIdStack[0])
|
||||
teamIdStack = append([]int64{}, teamIdStack[1:]...)
|
||||
}
|
||||
|
||||
j := 0
|
||||
// Find a team not responsible for this exercice
|
||||
for (strings.Contains(theme.Path, "grp") && strings.Contains(theme.Path, "-grp"+strings.TrimPrefix(team.Name, uaw.TeamPrefix)+"-")) || (!strings.Contains(theme.Path, "grp") && strings.HasPrefix(theme.Path, strings.TrimPrefix(team.Name, uaw.TeamPrefix)+"-")) {
|
||||
j += 1
|
||||
teamIdStack = append(teamIdStack, team.Id)
|
||||
team = teams[(uaw.Start+tid+uaw.Turns*i+j)%len(teams)]
|
||||
}
|
||||
|
||||
exercices, err := theme.GetExercices()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
for _, ex := range exercices {
|
||||
_, err := team.NewQATodo(ex.Id)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for eid, ex := range exercices {
|
||||
team := teams[(uaw.Start+eid+uaw.Turns*i)%len(teams)]
|
||||
team := teams[(uaw.Start+lasttid+eid+uaw.Turns*i)%len(teams)]
|
||||
|
||||
if len(teamIdStack) > 0 {
|
||||
teamIdStack = append(teamIdStack, team.Id)
|
||||
@ -316,3 +381,24 @@ func assignWork(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, "true")
|
||||
}
|
||||
|
||||
func deleteAssignedWork(c *gin.Context) {
|
||||
teams, err := fic.GetTeams()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetTeams: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list teams: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
for _, team := range teams {
|
||||
todos, err := team.GetQATodo()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Todo not found."})
|
||||
return
|
||||
}
|
||||
|
||||
for _, t := range todos {
|
||||
t.Delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ func showVersion(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"version": 0.3,
|
||||
"version": 0.4,
|
||||
"auth": map[string]interface{}{
|
||||
"name": ficteam,
|
||||
"id_team": teamid,
|
||||
|
@ -42,6 +42,14 @@ func NewApp(baseURL string) App {
|
||||
|
||||
api.DeclareRoutes(baserouter)
|
||||
declareStaticRoutes(baserouter, baseURL)
|
||||
|
||||
// In case of /baseURL/baseURL, redirect to /baseURL
|
||||
baserouter.GET(baseURL, func(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, baseURL)
|
||||
})
|
||||
baserouter.GET(baseURL+"/*path", func(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, baseURL+c.Param("path"))
|
||||
})
|
||||
} else {
|
||||
declareStaticRoutes(router.Group(""), "")
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func serveOrReverse(forced_url string, baseURL string) func(c *gin.Context) {
|
||||
if u, err := url.Parse(DevProxy); err != nil {
|
||||
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
|
||||
} else {
|
||||
if forced_url != "" {
|
||||
if forced_url != "" && forced_url != "/" {
|
||||
u.Path = path.Join(u.Path, forced_url)
|
||||
} else {
|
||||
u.Path = path.Join(u.Path, strings.TrimPrefix(c.Request.URL.Path, baseURL))
|
||||
|
431
qa/ui/package-lock.json
generated
431
qa/ui/package-lock.json
generated
@ -8,7 +8,7 @@
|
||||
"name": "qa",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@sveltestrap/sveltestrap": "^6.2.1",
|
||||
"@sveltestrap/sveltestrap": "^7.0.0",
|
||||
"bootstrap": "^5.2.2",
|
||||
"bootstrap-icons": "^1.9.1"
|
||||
},
|
||||
@ -17,7 +17,7 @@
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"prettier": "^3.0.0",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
@ -430,17 +430,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
||||
"integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
|
||||
"integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
|
||||
}
|
||||
@ -469,13 +472,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz",
|
||||
"integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==",
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz",
|
||||
"integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/object-schema": "^2.1.4",
|
||||
"@eslint/object-schema": "^2.1.5",
|
||||
"debug": "^4.3.1",
|
||||
"minimatch": "^3.1.2"
|
||||
},
|
||||
@ -484,11 +487,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz",
|
||||
"integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==",
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz",
|
||||
"integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
@ -518,9 +524,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz",
|
||||
"integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==",
|
||||
"version": "9.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz",
|
||||
"integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -528,9 +534,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/object-schema": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
|
||||
"integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz",
|
||||
"integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -538,12 +544,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz",
|
||||
"integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==",
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz",
|
||||
"integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.10.0",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -574,6 +581,20 @@
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
|
||||
"integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/module-importer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
|
||||
@ -589,9 +610,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/retry": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
|
||||
"integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz",
|
||||
"integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -603,9 +624,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
||||
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
@ -668,9 +689,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
|
||||
"integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.0.tgz",
|
||||
"integrity": "sha512-G2fUQQANtBPsNwiVFg4zKiPQyjVKZCUdQUol53R8E71J7AsheRMV/Yv/nB8giOcOVqP7//eB5xPqieBYZe9bGg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -682,9 +703,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
|
||||
"integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.0.tgz",
|
||||
"integrity": "sha512-qhFwQ+ljoymC+j5lXRv8DlaJYY/+8vyvYmVx074zrLsu5ZGWYsJNLjPPVJJjhZQpyAKUGPydOq9hRLLNvh1s3A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -696,9 +717,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
|
||||
"integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.0.tgz",
|
||||
"integrity": "sha512-44n/X3lAlWsEY6vF8CzgCx+LQaoqWGN7TzUfbJDiTIOjJm4+L2Yq+r5a8ytQRGyPqgJDs3Rgyo8eVL7n9iW6AQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -710,9 +731,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
|
||||
"integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.0.tgz",
|
||||
"integrity": "sha512-F9ct0+ZX5Np6+ZDztxiGCIvlCaW87HBdHcozUfsHnj1WCUTBUubAoanhHUfnUHZABlElyRikI0mgcw/qdEm2VQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -723,10 +744,38 @@
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.0.tgz",
|
||||
"integrity": "sha512-JpsGxLBB2EFXBsTLHfkZDsXSpSmKD3VxXCgBQtlPcuAqB8TlqtLcbeMhxXQkCDv1avgwNjF8uEIbq5p+Cee0PA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.0.tgz",
|
||||
"integrity": "sha512-wegiyBT6rawdpvnD9lmbOpx5Sph+yVZKHbhnSP9MqUEDX08G4UzMU+D87jrazGE7lRSyTRs6NEYHtzfkJ3FjjQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
|
||||
"integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.0.tgz",
|
||||
"integrity": "sha512-3pA7xecItbgOs1A5H58dDvOUEboG5UfpTq3WzAdF54acBbUM+olDJAPkgj1GRJ4ZqE12DZ9/hNS2QZk166v92A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -738,9 +787,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
|
||||
"integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.0.tgz",
|
||||
"integrity": "sha512-Y7XUZEVISGyge51QbYyYAEHwpGgmRrAxQXO3siyYo2kmaj72USSG8LtlQQgAtlGfxYiOwu+2BdbPjzEpcOpRmQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -752,9 +801,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.0.tgz",
|
||||
"integrity": "sha512-r7/OTF5MqeBrZo5omPXcTnjvv1GsrdH8a8RerARvDFiDwFpDVDnJyByYM/nX+mvks8XXsgPUxkwe/ltaX2VH7w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -766,9 +815,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
|
||||
"integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.0.tgz",
|
||||
"integrity": "sha512-HJbifC9vex9NqnlodV2BHVFNuzKL5OnsV2dvTw6e1dpZKkNjPG6WUq+nhEYV6Hv2Bv++BXkwcyoGlXnPrjAKXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -779,10 +828,24 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.0.tgz",
|
||||
"integrity": "sha512-VAEzZTD63YglFlWwRj3taofmkV1V3xhebDXffon7msNz4b14xKsz7utO6F8F4cqt8K/ktTl9rm88yryvDpsfOw==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.0.tgz",
|
||||
"integrity": "sha512-Sts5DST1jXAc9YH/iik1C9QRsLcCoOScf3dfbY5i4kH9RJpKxiTBXqm7qU5O6zTXBTEZry69bGszr3SMgYmMcQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@ -794,9 +857,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.0.tgz",
|
||||
"integrity": "sha512-qhlXeV9AqxIyY9/R1h1hBD6eMvQCO34ZmdYvry/K+/MBs6d1nRFLm6BOiITLVI+nFAAB9kUB6sdJRKyVHXnqZw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@ -808,9 +871,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.0.tgz",
|
||||
"integrity": "sha512-8ZGN7ExnV0qjXa155Rsfi6H8M4iBBwNLBM9lcVS+4NcSzOFaNqmt7djlox8pN1lWrRPMRRQ8NeDlozIGx3Omsw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@ -822,9 +885,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.0.tgz",
|
||||
"integrity": "sha512-VDzNHtLLI5s7xd/VubyS10mq6TxvZBp+4NRWoW+Hi3tgV05RtVm4qK99+dClwTN1McA6PHwob6DEJ6PlXbY83A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -836,9 +899,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
|
||||
"integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.0.tgz",
|
||||
"integrity": "sha512-qcb9qYDlkxz9DxJo7SDhWxTWV1gFuwznjbTiov289pASxlfGbaOD54mgbs9+z94VwrXtKTu+2RqwlSTbiOqxGg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -850,9 +913,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.0.tgz",
|
||||
"integrity": "sha512-pFDdotFDMXW2AXVbfdUEfidPAk/OtwE/Hd4eYMTNVVaCQ6Yl8et0meDaKNL63L44Haxv4UExpv9ydSf3aSayDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -864,9 +927,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.0.tgz",
|
||||
"integrity": "sha512-/TG7WfrCAjeRNDvI4+0AAMoHxea/USWhAzf9PVDFHbcqrQ7hMMKp4jZIy4VEjk72AAfN5k4TiSMRXRKf/0akSw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@ -878,9 +941,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.0.tgz",
|
||||
"integrity": "sha512-5hqO5S3PTEO2E5VjCePxv40gIgyS2KvO7E7/vvC/NbIW4SIRamkMr1hqj+5Y67fbBWv/bQLB6KelBQmXlyCjWA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -892,9 +955,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@sveltejs/adapter-static": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.6.tgz",
|
||||
"integrity": "sha512-MGJcesnJWj7FxDcB/GbrdYD3q24Uk0PIL4QIX149ku+hlJuj//nxUbb0HxUTpjkecWfHjVveSUnUaQWnPRXlpg==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz",
|
||||
"integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
@ -902,25 +965,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/kit": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.9.0.tgz",
|
||||
"integrity": "sha512-W3E7ed3ChB6kPqRs2H7tcHp+Z7oiTFC6m+lLyAQQuyXeqw6LdNuuwEUla+5VM0OGgqQD+cYD6+7Xq80vVm17Vg==",
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.17.1.tgz",
|
||||
"integrity": "sha512-CpoGSLqE2MCmcQwA2CWJvOsZ9vW+p/1H3itrFykdgajUNAEyQPbsaSn7fZb6PLHQwe+07njxje9ss0fjZoCAyw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.6.0",
|
||||
"cookie": "^0.6.0",
|
||||
"devalue": "^5.1.0",
|
||||
"esm-env": "^1.2.1",
|
||||
"esm-env": "^1.2.2",
|
||||
"import-meta-resolve": "^4.1.0",
|
||||
"kleur": "^4.1.5",
|
||||
"magic-string": "^0.30.5",
|
||||
"mrmime": "^2.0.0",
|
||||
"sade": "^1.8.1",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
"sirv": "^3.0.0",
|
||||
"tiny-glob": "^0.2.9"
|
||||
"sirv": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"svelte-kit": "svelte-kit.js"
|
||||
@ -976,9 +1037,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltestrap/sveltestrap": {
|
||||
"version": "6.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@sveltestrap/sveltestrap/-/sveltestrap-6.2.7.tgz",
|
||||
"integrity": "sha512-WwLLfAFUb42BGuRrf3Vbct30bQMzlEMMipN/MfxhjuLTmLQeW9muVJfPyvjtWS+mY+RjkSCoHvAp/ZobP1NLlQ==",
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sveltestrap/sveltestrap/-/sveltestrap-7.0.3.tgz",
|
||||
"integrity": "sha512-lvZpVlq7pHVxJbjq2d6JAAr/Z1mkSaPOw3pwpZiuQ9FK97/Pr66m5Bf9qZIc1FUkLnbNiDtRAbhVyR8LVdr3FQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8"
|
||||
@ -1218,9 +1279,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz",
|
||||
"integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1259,9 +1320,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1353,19 +1414,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.15.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz",
|
||||
"integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==",
|
||||
"version": "9.19.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz",
|
||||
"integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.19.0",
|
||||
"@eslint/core": "^0.9.0",
|
||||
"@eslint/core": "^0.10.0",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "9.15.0",
|
||||
"@eslint/plugin-kit": "^0.2.3",
|
||||
"@eslint/js": "9.19.0",
|
||||
"@eslint/plugin-kit": "^0.2.5",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.1",
|
||||
@ -1373,7 +1434,7 @@
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"ajv": "^6.12.4",
|
||||
"chalk": "^4.0.0",
|
||||
"cross-spawn": "^7.0.5",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"debug": "^4.3.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"eslint-scope": "^8.2.0",
|
||||
@ -1429,22 +1490,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-prettier": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
|
||||
"integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz",
|
||||
"integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
"eslint-config-prettier": "build/bin/cli.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-svelte": {
|
||||
"version": "2.46.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.46.0.tgz",
|
||||
"integrity": "sha512-1A7iEMkzmCZ9/Iz+EAfOGYL8IoIG6zeKEq1SmpxGeM5SXmoQq+ZNnCpXFVJpsxPWYx8jIVGMerQMzX20cqUl0g==",
|
||||
"version": "2.46.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.46.1.tgz",
|
||||
"integrity": "sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1506,24 +1567,10 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/@humanwhocodes/retry": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz",
|
||||
"integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
},
|
||||
"node_modules/esm-env": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.1.tgz",
|
||||
"integrity": "sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
|
||||
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -1666,9 +1713,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
|
||||
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
|
||||
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@ -1713,20 +1760,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/globalyzer": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
|
||||
"integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/globrex": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
|
||||
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@ -1809,12 +1842,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/is-reference": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
|
||||
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
|
||||
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "*"
|
||||
"@types/estree": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
@ -1939,9 +1972,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.12",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz",
|
||||
"integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==",
|
||||
"version": "0.30.17",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
||||
"integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
@ -1994,9 +2027,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -2114,16 +2147,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
|
||||
"integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -2141,8 +2174,8 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.1.0",
|
||||
"nanoid": "^3.3.8",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -2248,9 +2281,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
|
||||
"integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
|
||||
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@ -2264,9 +2297,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-svelte": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.3.2.tgz",
|
||||
"integrity": "sha512-kRPjH8wSj2iu+dO+XaUv4vD8qr5mdDmlak3IT/7AOgGIMRG86z/EHOLauFcClKEnOUf4A4nOA7sre5KrJD4Raw==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.3.3.tgz",
|
||||
"integrity": "sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
@ -2295,9 +2328,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
|
||||
"integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
|
||||
"version": "4.32.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.0.tgz",
|
||||
"integrity": "sha512-JmrhfQR31Q4AuNBjjAX4s+a/Pu/Q8Q9iwjWBsjRH1q52SPFE2NqRMK6fUZKKnvKO6id+h7JIRf0oYsph53eATg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2311,22 +2344,25 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.24.0",
|
||||
"@rollup/rollup-android-arm64": "4.24.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.24.0",
|
||||
"@rollup/rollup-darwin-x64": "4.24.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.24.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.24.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.24.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.24.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.24.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.24.0",
|
||||
"@rollup/rollup-android-arm-eabi": "4.32.0",
|
||||
"@rollup/rollup-android-arm64": "4.32.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.32.0",
|
||||
"@rollup/rollup-darwin-x64": "4.32.0",
|
||||
"@rollup/rollup-freebsd-arm64": "4.32.0",
|
||||
"@rollup/rollup-freebsd-x64": "4.32.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.32.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.32.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.32.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.32.0",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.32.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.32.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.32.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.32.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.32.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.32.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.32.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.32.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.32.0",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@ -2357,9 +2393,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/set-cookie-parser": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz",
|
||||
"integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
||||
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -2550,17 +2586,6 @@
|
||||
"svelte": "^3.19.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-glob": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
|
||||
"integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"globalyzer": "0.1.0",
|
||||
"globrex": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/totalist": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
|
||||
@ -2602,9 +2627,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.11",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
|
||||
"integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
|
||||
"version": "5.4.14",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz",
|
||||
"integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -14,7 +14,7 @@
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"prettier": "^3.0.0",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
@ -25,6 +25,6 @@
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.2.2",
|
||||
"bootstrap-icons": "^1.9.1",
|
||||
"@sveltestrap/sveltestrap": "^6.2.1"
|
||||
"@sveltestrap/sveltestrap": "^7.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -11,66 +11,81 @@
|
||||
|
||||
let themesP = fetch('api/qa/export.json').then((res) => res.json())
|
||||
|
||||
function state2Color(state) {
|
||||
function state2Color(report) {
|
||||
const subtle = report.closed ? "-subtle" : "";
|
||||
const state = report.state;
|
||||
if (state === "timer") {
|
||||
return "info";
|
||||
return "info" + subtle;
|
||||
} else if (state === "ok") {
|
||||
return "success";
|
||||
return "success" + subtle;
|
||||
} else if (state.startsWith("issue")) {
|
||||
return "danger";
|
||||
return "danger" + subtle;
|
||||
} else {
|
||||
return "warning";
|
||||
return "warning" + subtle;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="d-flex flex-fill"
|
||||
class="d-flex flex-column flex-fill"
|
||||
style="overflow-y: auto"
|
||||
>
|
||||
{#await themesP then themes}
|
||||
{#if Object.keys(themes).length > 1}
|
||||
<Row
|
||||
style={'min-width:'+15*Object.keys(themes).length + 'vw'}
|
||||
>
|
||||
{#if Object.keys(themes).length > 1}
|
||||
{#each Object.keys(themes) as tname}
|
||||
<Col style="border-right: 1px solid lightgray">
|
||||
<h3
|
||||
class="text-center py-3 mb-3"
|
||||
style="border-bottom: 2px solid black"
|
||||
<div
|
||||
class="d-flex gap-2 mb-2 align-items-start py-1"
|
||||
style="border-bottom: 1px solid lightgray; min-height: 4rem"
|
||||
>
|
||||
{tname}
|
||||
</h3>
|
||||
{#if themes[tname].exercices}
|
||||
{#each themes[tname].exercices as exercice}
|
||||
{#if exercice.reports}
|
||||
{#each exercice.reports as report}
|
||||
<Card
|
||||
class="mb-3"
|
||||
color={state2Color(report.report.state)}
|
||||
style="cursor: pointer"
|
||||
on:click={() => goto(`exercices/${exercice.exercice.id}/${report.report.id}`)}
|
||||
<h4
|
||||
class="d-flex align-items-center pe-2 mb-0 h-100 text-truncate"
|
||||
style="border-right: 2px solid black; max-width: 150px"
|
||||
title={tname}
|
||||
on:click={() => goto("/themes/" + themes[tname].theme.id)}
|
||||
>
|
||||
{tname}
|
||||
</h4>
|
||||
{#if themes[tname].exercices}
|
||||
<div
|
||||
class="flex-fill d-flex flex-column gap-1"
|
||||
>
|
||||
{#each themes[tname].exercices as exercice, eid}
|
||||
{#if exercice.reports}
|
||||
<div
|
||||
class="flex-fill d-flex gap-2 align-items-center"
|
||||
>
|
||||
<CardBody class="p-2">
|
||||
{report.report.subject}
|
||||
{#if report.comments}
|
||||
<Badge
|
||||
class="float-end"
|
||||
>
|
||||
{report.comments.length}
|
||||
</Badge>
|
||||
{/if}
|
||||
</CardBody>
|
||||
</Card>
|
||||
{/each}
|
||||
<hr />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</Col>
|
||||
<span
|
||||
title={exercice.exercice.title}
|
||||
on:click={() => goto("/themes/" + themes[tname].theme.id + "/" + exercice.exercice.id)}
|
||||
>
|
||||
{eid+1}.
|
||||
</span>
|
||||
{#each exercice.reports as report}
|
||||
<Card
|
||||
color={state2Color(report.report)}
|
||||
style="cursor: pointer; min-width: 100px; max-width: 250px"
|
||||
on:click={() => goto(`exercices/${exercice.exercice.id}/${report.report.id}`)}
|
||||
>
|
||||
<CardBody class="p-2 text-truncate" title={report.report.subject}>
|
||||
{#if report.comments}
|
||||
<Badge
|
||||
class="float-end"
|
||||
>
|
||||
{report.comments.length}
|
||||
</Badge>
|
||||
{/if}
|
||||
{report.report.subject}
|
||||
</CardBody>
|
||||
</Card>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</Row>
|
||||
{:else}
|
||||
{:else}
|
||||
{#each Object.keys(themes) as tname}
|
||||
{#if themes[tname].exercices}
|
||||
{#each themes[tname].exercices as exercice}
|
||||
@ -112,6 +127,6 @@
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
|
@ -22,10 +22,12 @@
|
||||
goto("teams/" + id)
|
||||
}
|
||||
|
||||
let start = 0;
|
||||
let turns = 3;
|
||||
let team_prefix = "";
|
||||
let team_assistants = "";
|
||||
let assignForm = {
|
||||
start: 0,
|
||||
turns: 3,
|
||||
team_prefix: "",
|
||||
team_assistants: "",
|
||||
}
|
||||
let assignInProgress = false;
|
||||
|
||||
async function assignExercices() {
|
||||
@ -33,12 +35,7 @@
|
||||
const res = await fetch(`api/qa_assign_work`, {
|
||||
method: 'POST',
|
||||
headers: {'Accept': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
start,
|
||||
turns,
|
||||
team_prefix,
|
||||
team_assistants,
|
||||
}),
|
||||
body: JSON.stringify(assignForm),
|
||||
})
|
||||
if (res.status == 200) {
|
||||
teams.refresh();
|
||||
@ -111,23 +108,26 @@
|
||||
<form on:submit|preventDefault={assignExercices}>
|
||||
<FormGroup>
|
||||
<Label for="ae-start">Compteur de départ</Label>
|
||||
<Input type="number" id="ae-start" bind:value={start} />
|
||||
<Input type="number" id="ae-start" bind:value={assignForm.start} />
|
||||
<p class="form-text">
|
||||
Incrémenter de 1 pour chaque nouveau challenge blanc, cela décale l'attribution des exercices.
|
||||
</p>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="ae-turns">Nombre d'itérations</Label>
|
||||
<Input type="number" id="ae-turns" bind:value={turns} />
|
||||
<Input type="number" id="ae-turns" bind:value={assignForm.turns} />
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="ae-prefix">Préfixe des noms d'équipes</Label>
|
||||
<Input id="ae-prefix" bind:value={team_prefix} placeholder="FIC Groupe" />
|
||||
<Input id="ae-prefix" bind:value={assignForm.team_prefix} placeholder="FIC Groupe" />
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="ae-assistants">Nom de l'équipe assistants</Label>
|
||||
<Input id="ae-assistants" bind:value={team_assistants} placeholder="Assistants" />
|
||||
<Input id="ae-assistants" bind:value={assignForm.team_assistants} placeholder="Assistants" />
|
||||
</FormGroup>
|
||||
<Input class="mb-3" type="switch" label="Ne pas répartir les exercices indépendants" bind:checked={assignForm.without_standalone_exercices} />
|
||||
<Input class="mb-3" type="switch" label="Ne pas répartir les scénarios" bind:checked={assignForm.without_themes} />
|
||||
<Input class="mb-3" type="switch" label="Répartir tous les exercices existant sans considération des scénarios dans lesquels ils se trouvent" bind:checked={assignForm.only_exercices} />
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={assignInProgress}
|
||||
|
@ -2,6 +2,12 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
||||
|
||||
/** @type {import('vite').UserConfig} */
|
||||
const config = {
|
||||
server: {
|
||||
hmr: {
|
||||
port: 10001
|
||||
}
|
||||
},
|
||||
|
||||
plugins: [sveltekit()]
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user