Parcourir la source

Delete handlers GET/POST, ugly Confirm form, all routes defined.

Frédéric G. MARAND il y a 2 ans
Parent
commit
5854e2e462

+ 1 - 1
.run/Redriver.run.xml

@@ -2,7 +2,7 @@
   <configuration default="false" name="Redriver" type="GoApplicationRunConfiguration" factoryName="Go Application">
     <module name="sqs_demo" />
     <working_directory value="$PROJECT_DIR$" />
-    <parameters value="-profile=root -addr :81 -wait 10" />
+    <parameters value="-profile=sqs-tutorial -addr :81 -wait 10" />
     <EXTENSION ID="net.ashald.envfile">
       <option name="IS_ENABLED" value="false" />
       <option name="IS_SUBST" value="false" />

+ 3 - 0
back/services/services.go

@@ -15,6 +15,7 @@ const (
 	PProfile = "profile"
 	PQName   = "queue-name"
 	PRegion  = "region"
+	PSecret  = "secret"
 	PURL     = "url"
 	PWait    = "wait"
 
@@ -43,6 +44,7 @@ func FlagsService(dic *izidic.Container) (any, error) {
 	profile := fs.String(PProfile, "test-profile", "The AWS profile")
 	region := fs.String(PRegion, "eu-west-3", "The AWS region to connect to")
 	qName := fs.String(PQName, "dummy-queue", "The queue name")
+	secret := fs.String(PSecret, "secret", "The session store secret")
 	sqsURL := fs.String(PURL, "http://localhost:4566", "The SQS endpoint URL")
 	wait := fs.Int(PWait, 3, "The maximum number of seconds to wait when receiving messages")
 	if err := fs.Parse(dic.MustParam(PArgs).([]string)); err != nil {
@@ -53,6 +55,7 @@ func FlagsService(dic *izidic.Container) (any, error) {
 	dic.Store(PProfile, *profile)
 	dic.Store(PQName, *qName)
 	dic.Store(PRegion, *region)
+	dic.Store(PSecret, []byte(*secret))
 	dic.Store(PURL, *sqsURL)
 	dic.Store(PWait, *wait)
 	return fs, nil

+ 40 - 0
back/web/delete.go

@@ -0,0 +1,40 @@
+package web
+
+import (
+	"net/http"
+
+	"github.com/gin-contrib/sessions"
+	"github.com/gin-gonic/gin"
+
+	"code.osinet.fr/fgm/sqs_demo/back/services/redriver"
+)
+
+func makeDeleteHandler(rd redriver.Redriver) gin.HandlerFunc {
+	return func(c *gin.Context) {
+		qName := c.Param("name")
+
+		sess := sessions.Default(c)
+		flashes := sess.Flashes()
+		sess.Clear()
+		sess.Save()
+
+		c.HTML(http.StatusOK, "confirm", gin.H{
+			"flashes":     flashes,
+			"question":    "Do you confirm this deletion request ?",
+			"description": "The message cannot be recovered after that step",
+			"cancel":      "Cancel",
+			"confirm":     "Delete",
+			"redirect":    "/queue/" + qName,
+		})
+	}
+	return nil
+}
+
+func makeDeleteConfirmHandler(rd redriver.Redriver) gin.HandlerFunc {
+	return func(c *gin.Context) {
+		c.JSON(http.StatusServiceUnavailable, gin.H{
+			"message": "deletion confirmation",
+			"error":   "not implemented",
+		})
+	}
+}

+ 17 - 0
back/web/handlers.go

@@ -0,0 +1,17 @@
+package web
+
+import (
+	"net/url"
+
+	"github.com/gin-gonic/gin"
+)
+
+func makeConfirmHandler(
+	question, description string,
+	confirm, cancel string,
+	redirect *url.URL,
+) gin.HandlerFunc {
+	return func(c *gin.Context) {
+
+	}
+}

+ 15 - 0
back/web/purge.go

@@ -0,0 +1,15 @@
+package web
+
+import (
+	"github.com/gin-gonic/gin"
+
+	"code.osinet.fr/fgm/sqs_demo/back/services/redriver"
+)
+
+func makePurgeHandler(rd redriver.Redriver) gin.HandlerFunc {
+	return nil
+}
+
+func makePurgeConfirmHandler(rd redriver.Redriver) gin.HandlerFunc {
+	return nil
+}

+ 15 - 1
back/web/queue.go

@@ -1,9 +1,12 @@
 package web
 
 import (
+	"fmt"
 	"log"
 	"net/http"
+	"time"
 
+	"github.com/gin-contrib/sessions"
 	"github.com/gin-gonic/gin"
 
 	"code.osinet.fr/fgm/sqs_demo/back/services/redriver"
@@ -13,6 +16,13 @@ func makeQueueHandler(rd redriver.Redriver) gin.HandlerFunc {
 	return func(c *gin.Context) {
 		ctx := c.Request.Context()
 		qName := c.Param("name")
+
+		sess := sessions.Default(c)
+		flashes := sess.Flashes()
+		sess.Clear()
+		sess.AddFlash(fmt.Sprintf("Previous info acquired at: %v", time.Now()))
+		sess.Save()
+
 		qi, err := rd.GetQueueInfo(ctx, qName)
 		if err != nil {
 			log.Printf("failed getting info for queue %q: %v", qName, err)
@@ -25,6 +35,10 @@ func makeQueueHandler(rd redriver.Redriver) gin.HandlerFunc {
 			c.JSON(http.StatusInternalServerError, nil)
 			return
 		}
-		c.HTML(http.StatusOK, "", map[string]any{"info": qi, "items": items})
+		c.HTML(http.StatusOK, "", map[string]any{
+			"flashes": flashes,
+			"info":    qi,
+			"items":   items,
+		})
 	}
 }

+ 11 - 0
back/web/redrive.go

@@ -0,0 +1,11 @@
+package web
+
+import (
+	"github.com/gin-gonic/gin"
+
+	"code.osinet.fr/fgm/sqs_demo/back/services/redriver"
+)
+
+func makeRedriveHandler(rd redriver.Redriver) gin.HandlerFunc {
+	return nil
+}

+ 27 - 5
back/web/routes.go

@@ -8,6 +8,8 @@ import (
 	"path/filepath"
 
 	"github.com/fgm/izidic"
+	"github.com/gin-contrib/sessions"
+	"github.com/gin-contrib/sessions/cookie"
 	"github.com/gin-gonic/gin"
 
 	"code.osinet.fr/fgm/sqs_demo/back/services"
@@ -15,28 +17,48 @@ import (
 	"code.osinet.fr/fgm/sqs_demo/front"
 )
 
-func SetupRoutes(rd redriver.Redriver, renderer *template.Template) *gin.Engine {
+func SetupRoutes(rd redriver.Redriver, renderer *template.Template, secret []byte) *gin.Engine {
 	const assetsPrefix = "/assets/"
 	r := gin.Default()
 	r.SetHTMLTemplate(renderer)
 	r.SetTrustedProxies(nil)
+	store := cookie.NewStore(secret)
+	r.Use(sessions.Sessions("defaultsession", store))
+
 	r.GET("/", makeHomeHandler(rd))
 	r.StaticFS(assetsPrefix, PrefixFileSystem(assetsPrefix, http.FS(front.Assets)))
+	r.GET("/queue", gin.WrapH(http.RedirectHandler("/", http.StatusMovedPermanently)))
 	r.GET("/queue/:name", makeQueueHandler(rd))
+	r.GET("/queue/:name/delete", makeDeleteHandler(rd))
+	r.POST("/queue/:name/delete", makeDeleteConfirmHandler(rd))
+	r.GET("/queue/:name/purge", makePurgeHandler(rd))
+	r.POST("/queue/:name/purge", makePurgeConfirmHandler(rd))
+	r.POST("/queue/:name/redrive", makeRedriveHandler(rd))
 	return r
 }
 
 func HttpService(dic *izidic.Container) (any, error) {
+	secret := dic.MustParam(services.PSecret).([]byte)
 	rd := dic.MustService(services.SvcRedriver).(redriver.Redriver)
 	re := dic.MustService(services.SvcRenderer).(*template.Template)
-	return SetupRoutes(rd, re), nil
+	return SetupRoutes(rd, re, secret), nil
 }
 
 func RendererService(dic *izidic.Container) (any, error) {
+	var err error
 	renderer := template.New("redriver")
-	renderer, err := renderer.New("queue-get").Parse(front.QueueGet)
-	if err != nil {
-		return nil, fmt.Errorf("failed parsing queue-get template")
+	for _, tpl := range []struct {
+		name  string
+		value string
+	}{
+		{"confirm", front.Confirm},
+		{"flashes", front.Flashes},
+		{"queue-get", front.QueueGet},
+	} {
+		renderer, err = renderer.New(tpl.name).Parse(tpl.value)
+		if err != nil {
+			return nil, fmt.Errorf("failed parsing %q template: %w", tpl.name, err)
+		}
 	}
 	return renderer, nil
 }

+ 11 - 0
front/assets/redriver.css

@@ -1,3 +1,14 @@
 @tailwind base;
 @tailwind components;
 @tailwind utilities;
+
+@layer components {
+    .btn-cancel {
+        @apply py-2 px-4 bg-amber-50 text-black font-semibold rounded-lg shadow-md hover:bg-amber-200 focus:outline-none focus:ring-2 focus:ring-amber-400 focus:ring-opacity-75;
+
+    }
+
+    .btn-primary {
+        @apply py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75;
+    }
+}

+ 108 - 0
front/assets/styles.css

@@ -547,15 +547,94 @@ video {
   }
 }
 
+.btn-cancel {
+  border-radius: 0.5rem;
+  --tw-bg-opacity: 1;
+  background-color: rgb(255 251 235 / var(--tw-bg-opacity));
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+  padding-left: 1rem;
+  padding-right: 1rem;
+  font-weight: 600;
+  --tw-text-opacity: 1;
+  color: rgb(0 0 0 / var(--tw-text-opacity));
+  --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
+  --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
+  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
+}
+
+.btn-cancel:hover {
+  --tw-bg-opacity: 1;
+  background-color: rgb(253 230 138 / var(--tw-bg-opacity));
+}
+
+.btn-cancel:focus {
+  outline: 2px solid transparent;
+  outline-offset: 2px;
+  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
+  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
+  --tw-ring-color: rgb(251 191 36 / var(--tw-ring-opacity));
+  --tw-ring-opacity: 0.75;
+}
+
+.btn-primary {
+  border-radius: 0.5rem;
+  --tw-bg-opacity: 1;
+  background-color: rgb(59 130 246 / var(--tw-bg-opacity));
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+  padding-left: 1rem;
+  padding-right: 1rem;
+  font-weight: 600;
+  --tw-text-opacity: 1;
+  color: rgb(255 255 255 / var(--tw-text-opacity));
+  --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
+  --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
+  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
+}
+
+.btn-primary:hover {
+  --tw-bg-opacity: 1;
+  background-color: rgb(29 78 216 / var(--tw-bg-opacity));
+}
+
+.btn-primary:focus {
+  outline: 2px solid transparent;
+  outline-offset: 2px;
+  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
+  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
+  --tw-ring-color: rgb(96 165 250 / var(--tw-ring-opacity));
+  --tw-ring-opacity: 0.75;
+}
+
+.float-left {
+  float: left;
+}
+
+.clear-both {
+  clear: both;
+}
+
 .mx-auto {
   margin-left: auto;
   margin-right: auto;
 }
 
+.mx-0 {
+  margin-left: 0px;
+  margin-right: 0px;
+}
+
 .table {
   display: table;
 }
 
+.w-full {
+  width: 100%;
+}
+
 .table-auto {
   table-layout: auto;
 }
@@ -570,10 +649,25 @@ video {
   border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y);
 }
 
+.space-y-1 > :not([hidden]) ~ :not([hidden]) {
+  --tw-space-y-reverse: 0;
+  margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
+  margin-bottom: calc(0.25rem * var(--tw-space-y-reverse));
+}
+
+.border-2 {
+  border-width: 2px;
+}
+
 .border {
   border-width: 1px;
 }
 
+.border-red-300 {
+  --tw-border-opacity: 1;
+  border-color: rgb(252 165 165 / var(--tw-border-opacity));
+}
+
 .border-slate-300 {
   --tw-border-opacity: 1;
   border-color: rgb(203 213 225 / var(--tw-border-opacity));
@@ -584,6 +678,20 @@ video {
   border-color: rgb(148 163 184 / var(--tw-border-opacity));
 }
 
+.border-b-red-300 {
+  --tw-border-opacity: 1;
+  border-bottom-color: rgb(252 165 165 / var(--tw-border-opacity));
+}
+
+.bg-red-50 {
+  --tw-bg-opacity: 1;
+  background-color: rgb(254 242 242 / var(--tw-bg-opacity));
+}
+
+.p-2 {
+  padding: 0.5rem;
+}
+
 .p-4 {
   padding: 1rem;
 }

BIN
front/assets/warning-cc0-1.0.png


+ 6 - 0
front/handlers.go

@@ -7,5 +7,11 @@ import (
 //go:embed assets
 var Assets embed.FS
 
+//go:embed templates/confirm.gohtml
+var Confirm string
+
+//go:embed templates/flashes.gohtml
+var Flashes string
+
 //go:embed templates/queue.gohtml
 var QueueGet string

+ 27 - 0
front/templates/confirm.gohtml

@@ -0,0 +1,27 @@
+{{define "confirm" -}}
+    <!DOCTYPE html>
+    <html lang="en">
+    <head>
+        <meta charset="UTF-8">
+        <title>Confirmation needed</title>
+        <link rel="stylesheet" href="/assets/styles.css"/>
+    </head>
+    <body>
+    <div class="container mx-auto">
+        <h1 class="text-3xl font-bold underline">Confirmation needed</h1>
+        {{ template "flashes" .flashes }}
+        <div>
+            <div>
+                <img src="/assets/warning-cc0-1.0.png"  style="width:48px" class="float-left" alt="yellow warning sign"/>
+                <p class="float-left">{{ .question }}</p>
+            </div>
+            <div class="clear-both">{{ .description }}</div>
+            <form method="post">
+                <a href="{{ .redirect }}" class="btn-cancel">{{ .cancel }}</a>
+                <button class="btn-primary">{{ .confirm }}</button>
+            </form>
+        </div>
+    </div>
+    </body>
+    </html>
+{{end}}

+ 9 - 0
front/templates/flashes.gohtml

@@ -0,0 +1,9 @@
+{{ define "flashes" -}}
+    {{ if . }}
+        <ul class="w-full mx-0 border-b-red-300 space-y-1">
+            {{ range . }}
+                <li class="bg-red-50 border-red-300 border-2 p-2">{{ . }}</li>
+            {{ end }}
+        </ul>
+    {{ end }}
+{{ end }}

+ 3 - 2
front/templates/queue.gohtml

@@ -10,8 +10,9 @@
     {{ $th := "border border-slate-300 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400" }}
     {{ $td := "border border-slate-300 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400" }}
     <div class="container mx-auto">
-        <h1 class="text-3xl font-bold underline">Vue file pour {{ .info.Name }}</h1>
-        <table class="table-auto border-separate border-spacing-2 border border-slate-400">
+        <h1 class="text-3xl font-bold underline">Queue view for {{ .info.Name }}</h1>
+        {{ template "flashes" .flashes }}
+        <table class="table-auto border-separate border-spacing-2 border border-slate-400 w-full">
             <thead>
             <tr>
                 <th scope="col" class="{{ $th }}">Propriété</th>

+ 5 - 0
go.mod

@@ -8,8 +8,10 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/sqs v1.20.1
 	github.com/davecgh/go-spew v1.1.1
 	github.com/fgm/izidic v0.0.2
+	github.com/gin-contrib/sessions v0.0.5
 	github.com/gin-gonic/gin v1.8.2
 	github.com/google/uuid v1.3.0
+	github.com/gorilla/mux v1.8.0
 	gopkg.in/yaml.v3 v3.0.1
 )
 
@@ -29,6 +31,9 @@ require (
 	github.com/go-playground/universal-translator v0.18.1 // indirect
 	github.com/go-playground/validator/v10 v10.11.2 // indirect
 	github.com/goccy/go-json v0.10.0 // indirect
+	github.com/gorilla/context v1.1.1 // indirect
+	github.com/gorilla/securecookie v1.1.1 // indirect
+	github.com/gorilla/sessions v1.2.1 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kr/text v0.2.0 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect

+ 10 - 0
go.sum

@@ -30,6 +30,8 @@ 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=
 github.com/fgm/izidic v0.0.2 h1:xpIr9sEVE2xVMlUPu7zcQhumxPB0mjW2/j8Cm598sMw=
 github.com/fgm/izidic v0.0.2/go.mod h1:HSUQlWnf88mpvaovrffBQnjqug3WLjTisLZl5g2RaEc=
+github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE=
+github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY=
 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.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY=
@@ -50,6 +52,14 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
+github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
+github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
+github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=