Refactor submission retrieval
This commit is contained in:
parent
24d39dd14e
commit
eacaedeb03
@ -1,21 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
"github.com/aws/aws-sdk-go/service/s3"
|
|
||||||
"github.com/drone/drone-go/drone"
|
"github.com/drone/drone-go/drone"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
@ -25,35 +18,11 @@ var (
|
|||||||
droneToken = ""
|
droneToken = ""
|
||||||
droneConfig *http.Client
|
droneConfig *http.Client
|
||||||
droneEndpoint string
|
droneEndpoint string
|
||||||
s3_endpoint string
|
|
||||||
s3_region = "us"
|
|
||||||
s3_bucket string
|
|
||||||
s3_access_key string
|
|
||||||
s3_secret_key string
|
|
||||||
s3_path_style bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.StringVar(&droneToken, "drone-token", droneToken, "Token for Drone Oauth")
|
flag.StringVar(&droneToken, "drone-token", droneToken, "Token for Drone Oauth")
|
||||||
flag.StringVar(&droneEndpoint, "drone-endpoint", droneEndpoint, "Drone Endpoint")
|
flag.StringVar(&droneEndpoint, "drone-endpoint", droneEndpoint, "Drone Endpoint")
|
||||||
|
|
||||||
s3_endpoint, _ = os.LookupEnv("S3_ENDPOINT")
|
|
||||||
if region, ok := os.LookupEnv("S3_REGION"); ok {
|
|
||||||
s3_region = region
|
|
||||||
}
|
|
||||||
s3_bucket, _ = os.LookupEnv("S3_BUCKET")
|
|
||||||
s3_access_key, _ = os.LookupEnv("S3_ACCESS_KEY")
|
|
||||||
s3_secret_key, _ = os.LookupEnv("S3_SECRET_KEY")
|
|
||||||
if path_style, ok := os.LookupEnv("S3_PATH_STYLE"); ok {
|
|
||||||
s3_path_style = path_style == "1" || path_style == "ON" || path_style == "on" || path_style == "TRUE" || path_style == "true" || path_style == "yes" || path_style == "YES"
|
|
||||||
}
|
|
||||||
|
|
||||||
flag.StringVar(&s3_endpoint, "s3-endpoint", s3_endpoint, "When using S3 backend, endpoint to use")
|
|
||||||
flag.StringVar(&s3_region, "s3-region", s3_region, "When using S3 backend, region to use")
|
|
||||||
flag.StringVar(&s3_bucket, "s3-bucket", s3_bucket, "When using S3 backend, bucket to use")
|
|
||||||
flag.StringVar(&s3_access_key, "s3-access-key", s3_access_key, "When using S3 backend, Access Key")
|
|
||||||
flag.StringVar(&s3_secret_key, "s3-secret-key", s3_secret_key, "When using S3 backend, Secret Key")
|
|
||||||
flag.BoolVar(&s3_path_style, "s3-path-style", s3_path_style, "When using S3 backend, force path style (when using minio)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initializeDroneOauth() {
|
func initializeDroneOauth() {
|
||||||
@ -68,15 +37,6 @@ func initializeDroneOauth() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func s3NewSession() (*session.Session, error) {
|
|
||||||
return session.NewSession(&aws.Config{
|
|
||||||
Credentials: credentials.NewStaticCredentials(s3_access_key, s3_secret_key, ""),
|
|
||||||
Endpoint: aws.String(s3_endpoint),
|
|
||||||
Region: aws.String(s3_region),
|
|
||||||
S3ForcePathStyle: &s3_path_style,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
|
func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
|
||||||
router.GET("/repositories", func(c *gin.Context) {
|
router.GET("/repositories", func(c *gin.Context) {
|
||||||
var u *User
|
var u *User
|
||||||
@ -196,10 +156,10 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
|
|||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
/*if repo.LastCheck != nil && !repo.LastCheck.Before(now.Add(-5*time.Minute)) {
|
if repo.LastCheck != nil && !repo.LastCheck.Before(now.Add(-5*time.Minute)) {
|
||||||
c.AbortWithStatusJSON(http.StatusPaymentRequired, gin.H{"errmsg": "Please wait between two pulls."})
|
c.AbortWithStatusJSON(http.StatusPaymentRequired, gin.H{"errmsg": "Please wait between two pulls."})
|
||||||
return
|
return
|
||||||
}*/
|
}
|
||||||
|
|
||||||
client := drone.NewClient(droneEndpoint, droneConfig)
|
client := drone.NewClient(droneEndpoint, droneConfig)
|
||||||
result, err := client.BuildCreate("srs", "atsebay.t-worker", "", "master", map[string]string{
|
result, err := client.BuildCreate("srs", "atsebay.t-worker", "", "master", map[string]string{
|
||||||
@ -225,6 +185,11 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
|
|||||||
repo := c.MustGet("repository").(*Repository)
|
repo := c.MustGet("repository").(*Repository)
|
||||||
|
|
||||||
tmp := strings.Split(repo.DroneRef, "/")
|
tmp := strings.Split(repo.DroneRef, "/")
|
||||||
|
if len(tmp) < 3 {
|
||||||
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "No build number. Please try pulling your work."})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
nbuild, err := strconv.Atoi(tmp[2])
|
nbuild, err := strconv.Atoi(tmp[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Bad build number. Please retry pulling your work."})
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Bad build number. Please retry pulling your work."})
|
||||||
@ -239,49 +204,6 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, result)
|
c.JSON(http.StatusOK, result)
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
repositoriesRoutes.GET("/submission", func(c *gin.Context) {
|
|
||||||
var u *User
|
|
||||||
if user, ok := c.Get("user"); ok {
|
|
||||||
u = user.(*User)
|
|
||||||
} else {
|
|
||||||
u = c.MustGet("LoggedUser").(*User)
|
|
||||||
}
|
|
||||||
repo := c.MustGet("repository").(*Repository)
|
|
||||||
work, err := getWork(int(repo.IdWork))
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := s3NewSession()
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong."})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println(path.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.metadata", u.Login)))
|
|
||||||
result, err := s3.New(s).GetObject(&s3.GetObjectInput{
|
|
||||||
Bucket: aws.String(s3_bucket),
|
|
||||||
Key: aws.String(path.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.metadata", u.Login))),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Submission not found."})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp := map[string]interface{}{}
|
|
||||||
err = json.NewDecoder(result.Body).Decode(&tmp)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Unable to decode JSON metadata:", err.Error())
|
|
||||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Submission not found."})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, tmp)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
125
submissions.go
Normal file
125
submissions.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const SharingTime = 15 * time.Minute
|
||||||
|
|
||||||
|
var (
|
||||||
|
s3_endpoint string
|
||||||
|
s3_region = "us"
|
||||||
|
s3_bucket string
|
||||||
|
s3_access_key string
|
||||||
|
s3_secret_key string
|
||||||
|
s3_path_style bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
s3_endpoint, _ = os.LookupEnv("S3_ENDPOINT")
|
||||||
|
if region, ok := os.LookupEnv("S3_REGION"); ok {
|
||||||
|
s3_region = region
|
||||||
|
}
|
||||||
|
s3_bucket, _ = os.LookupEnv("S3_BUCKET")
|
||||||
|
s3_access_key, _ = os.LookupEnv("S3_ACCESS_KEY")
|
||||||
|
s3_secret_key, _ = os.LookupEnv("S3_SECRET_KEY")
|
||||||
|
if path_style, ok := os.LookupEnv("S3_PATH_STYLE"); ok {
|
||||||
|
s3_path_style = path_style == "1" || path_style == "ON" || path_style == "on" || path_style == "TRUE" || path_style == "true" || path_style == "yes" || path_style == "YES"
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.StringVar(&s3_endpoint, "s3-endpoint", s3_endpoint, "When using S3 backend, endpoint to use")
|
||||||
|
flag.StringVar(&s3_region, "s3-region", s3_region, "When using S3 backend, region to use")
|
||||||
|
flag.StringVar(&s3_bucket, "s3-bucket", s3_bucket, "When using S3 backend, bucket to use")
|
||||||
|
flag.StringVar(&s3_access_key, "s3-access-key", s3_access_key, "When using S3 backend, Access Key")
|
||||||
|
flag.StringVar(&s3_secret_key, "s3-secret-key", s3_secret_key, "When using S3 backend, Secret Key")
|
||||||
|
flag.BoolVar(&s3_path_style, "s3-path-style", s3_path_style, "When using S3 backend, force path style (when using minio)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func s3NewSession() (*session.Session, error) {
|
||||||
|
return session.NewSession(&aws.Config{
|
||||||
|
Credentials: credentials.NewStaticCredentials(s3_access_key, s3_secret_key, ""),
|
||||||
|
Endpoint: aws.String(s3_endpoint),
|
||||||
|
Region: aws.String(s3_region),
|
||||||
|
S3ForcePathStyle: &s3_path_style,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func declareAPIWorkSubmissionsRoutes(router *gin.RouterGroup) {
|
||||||
|
router.GET("/submission", func(c *gin.Context) {
|
||||||
|
var u *User
|
||||||
|
if user, ok := c.Get("user"); ok {
|
||||||
|
u = user.(*User)
|
||||||
|
} else {
|
||||||
|
u = c.MustGet("LoggedUser").(*User)
|
||||||
|
}
|
||||||
|
work := c.MustGet("work").(*Work)
|
||||||
|
|
||||||
|
s, err := s3NewSession()
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong."})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s3.New(s).GetObject(&s3.GetObjectInput{
|
||||||
|
Bucket: aws.String(s3_bucket),
|
||||||
|
Key: aws.String(path.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.metadata", u.Login))),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Submission not found."})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := map[string]interface{}{}
|
||||||
|
err = json.NewDecoder(result.Body).Decode(&tmp)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Unable to decode JSON metadata:", err.Error())
|
||||||
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Submission not found."})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, tmp)
|
||||||
|
})
|
||||||
|
|
||||||
|
router.GET("/download", func(c *gin.Context) {
|
||||||
|
var u *User
|
||||||
|
if user, ok := c.Get("user"); ok {
|
||||||
|
u = user.(*User)
|
||||||
|
} else {
|
||||||
|
u = c.MustGet("LoggedUser").(*User)
|
||||||
|
}
|
||||||
|
work := c.MustGet("work").(*Work)
|
||||||
|
|
||||||
|
s, err := s3NewSession()
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong."})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := s3.New(s).GetObjectRequest(&s3.GetObjectInput{
|
||||||
|
Bucket: aws.String(s3_bucket),
|
||||||
|
Key: aws.String(path.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.tar.xz", u.Login))),
|
||||||
|
})
|
||||||
|
|
||||||
|
url, err := req.Presign(SharingTime)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Unable to create presign URL:", err)
|
||||||
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong when creating the presigned URL."})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Redirect(http.StatusFound, url)
|
||||||
|
})
|
||||||
|
}
|
@ -70,14 +70,6 @@
|
|||||||
{:else}
|
{:else}
|
||||||
{state.status}
|
{state.status}
|
||||||
{/if}
|
{/if}
|
||||||
{/await}<br>
|
|
||||||
Rendu :
|
|
||||||
{#await repo.getSubmission()}
|
|
||||||
<div class="spinner-grow spinner-grow-sm mx-1" role="status"></div>
|
|
||||||
{:then submission}
|
|
||||||
<strong>{submission.commit} ({submission.tag})</strong> (taille : {submission.size} o, date : <DateFormat date={new Date(submission.date)} dateStyle="medium" timeStyle="medium" />)
|
|
||||||
{:catch}
|
|
||||||
<strong>-</strong>
|
|
||||||
{/await}
|
{/await}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -88,7 +80,7 @@
|
|||||||
disable={submitInProgress || readonly}
|
disable={submitInProgress || readonly}
|
||||||
on:click={() => repo.retrieveWork().then(() => { refresh_repo_work() }, (error) => ToastsStore.addErrorToast({msg: "Une erreur s'est produite : " + error}))}
|
on:click={() => repo.retrieveWork().then(() => { refresh_repo_work() }, (error) => ToastsStore.addErrorToast({msg: "Une erreur s'est produite : " + error}))}
|
||||||
>
|
>
|
||||||
Mettre à jour
|
Récupérer mon travail
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -36,17 +36,6 @@ export class WorkRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSubmission() {
|
|
||||||
const res = await fetch(`api/repositories/${this.id}/submission`, {
|
|
||||||
headers: {'Accept': 'application/json'}
|
|
||||||
});
|
|
||||||
if (res.status == 200) {
|
|
||||||
return await res.json();
|
|
||||||
} else {
|
|
||||||
throw new Error((await res.json()).errmsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrieveWork() {
|
async retrieveWork() {
|
||||||
const res = await fetch(`api/repositories/${this.id}/trigger`, {
|
const res = await fetch(`api/repositories/${this.id}/trigger`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -91,6 +91,17 @@ export class Work {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSubmission() {
|
||||||
|
const res = await fetch(`api/works/${this.id}/submission`, {
|
||||||
|
headers: {'Accept': 'application/json'}
|
||||||
|
});
|
||||||
|
if (res.status == 200) {
|
||||||
|
return await res.json();
|
||||||
|
} else {
|
||||||
|
throw new Error((await res.json()).errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getGrades() {
|
async getGrades() {
|
||||||
const res = await fetch(`api/works/${this.id}/grades`, {
|
const res = await fetch(`api/works/${this.id}/grades`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -125,11 +125,33 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<WorkRepository class="mb-3" readonly={w.corrected || new Date(w.end_availability) <= new Date()} work={w} />
|
<WorkRepository class="mb-3" readonly={w.corrected || new Date(w.end_availability) <= new Date()} work={w} />
|
||||||
{#if w.submission_url}
|
<div class="card mb-3">
|
||||||
<p>
|
<div class="card-body d-flex justify-content-between">
|
||||||
<strong>État du rendu :</strong> <SubmissionStatus work={w} />
|
<div>
|
||||||
</p>
|
{#if w.submission_url}
|
||||||
{/if}
|
<strong>État du rendu :</strong> <SubmissionStatus work={w} />
|
||||||
|
{:else}
|
||||||
|
Rendu :
|
||||||
|
{#await w.getSubmission()}
|
||||||
|
<div class="spinner-grow spinner-grow-sm mx-1" role="status"></div>
|
||||||
|
{:then submission}
|
||||||
|
<strong>{submission.commit} ({submission.tag})</strong> (taille : {submission.size} o, date : <DateFormat date={new Date(submission.date)} dateStyle="medium" timeStyle="medium" />)
|
||||||
|
{:catch}
|
||||||
|
<strong>-</strong>
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-column justify-content-center">
|
||||||
|
<a
|
||||||
|
href="/api/works/{w.id}/download"
|
||||||
|
class="btn btn-sm btn-dark"
|
||||||
|
title="Voir la tarball de mon rendu"
|
||||||
|
>
|
||||||
|
<i class="bi bi-download"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{#if w.corrected}
|
{#if w.corrected}
|
||||||
{#await getScore(w)}
|
{#await getScore(w)}
|
||||||
<div class="spinner-border spinner-border-sm" role="status"></div>
|
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||||
@ -145,9 +167,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
{:else}
|
{:else}
|
||||||
<p>
|
<div class="alert alert-warning">
|
||||||
Pour être reconnu, vous devez pousser un tag <strong><a href="keys">signé</a></strong> sur votre dépôt. {#if w.tag}Le tag attendu doit commencer par : <code>{w.tag}</code>. Par exemple <code>{w.tag}v1.0</code>, <code>{w.tag}v1.1</code>, ...{/if} Seul le dernier tag <strong>alphabétique</strong> que vous envoyez avant la date du rendu sera pris en compte. Vous pouvez donc faire autant de tag que vous le souhaitez d'ici la date du rendu.
|
Pour être reconnu, vous devez pousser un tag <strong><a href="keys">signé</a></strong> sur votre dépôt. {#if w.tag}Le tag attendu doit commencer par : <code>{w.tag}</code>. Par exemple <code>{w.tag}v1.0</code>, <code>{w.tag}v1.1</code>, …{/if} Seul le dernier tag <strong>alphabétique</strong> que vous envoyez avant la date du rendu sera pris en compte. Vous pouvez donc faire autant de tag que vous le souhaitez d'ici la date du rendu.
|
||||||
</p>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
{/await}
|
||||||
|
1
works.go
1
works.go
@ -220,6 +220,7 @@ func declareAPIAuthWorksRoutes(router *gin.RouterGroup) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
declareAPIAuthRepositoriesRoutes(worksRoutes)
|
declareAPIAuthRepositoriesRoutes(worksRoutes)
|
||||||
|
declareAPIWorkSubmissionsRoutes(worksRoutes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func workHandler(c *gin.Context) {
|
func workHandler(c *gin.Context) {
|
||||||
|
Reference in New Issue
Block a user