aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--garage.go181
-rw-r--r--main.go1
-rw-r--r--templates/garage_key.html234
-rw-r--r--templates/garage_website_inspect.html34
-rw-r--r--templates/home.html3
-rw-r--r--website.go27
-rw-r--r--webui_website.go175
7 files changed, 223 insertions, 432 deletions
diff --git a/garage.go b/garage.go
index 7cd879b..f9529e9 100644
--- a/garage.go
+++ b/garage.go
@@ -4,10 +4,7 @@ import (
"context"
"fmt"
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
- "github.com/gorilla/mux"
"log"
- "net/http"
- "strings"
)
func gadmin() (*garage.APIClient, context.Context) {
@@ -166,181 +163,3 @@ func grgDeleteBucket(bid string) error {
}
return err
}
-
-// --- Start page rendering functions
-
-func handleWebsiteConfigure(w http.ResponseWriter, r *http.Request) {
- user := RequireUserHtml(w, r)
- if user == nil {
- return
- }
-
- tKey := getTemplate("garage_key.html")
- tKey.Execute(w, user)
-}
-
-func handleWebsiteList(w http.ResponseWriter, r *http.Request) {
- user := RequireUserHtml(w, r)
- if user == nil {
- return
- }
-
- ctrl, err := NewWebsiteController(user)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- if len(ctrl.PrettyList) > 0 {
- http.Redirect(w, r, "/website/inspect/"+ctrl.PrettyList[0], http.StatusFound)
- } else {
- http.Redirect(w, r, "/website/new", http.StatusFound)
- }
-}
-
-type WebsiteNewTpl struct {
- Ctrl *WebsiteController
- Err error
-}
-
-func handleWebsiteNew(w http.ResponseWriter, r *http.Request) {
- user := RequireUserHtml(w, r)
- if user == nil {
- return
- }
-
- ctrl, err := NewWebsiteController(user)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- tpl := &WebsiteNewTpl{ctrl, nil}
-
- tWebsiteNew := getTemplate("garage_website_new.html")
- if r.Method == "POST" {
- r.ParseForm()
-
- bucket := strings.Join(r.Form["bucket"], "")
- if bucket == "" {
- bucket = strings.Join(r.Form["bucket2"], "")
- }
-
- view, err := ctrl.Create(bucket)
- if err != nil {
- tpl.Err = err
- tWebsiteNew.Execute(w, tpl)
- return
- }
-
- http.Redirect(w, r, "/website/inspect/"+view.Name.Pretty, http.StatusFound)
- return
- }
-
- tWebsiteNew.Execute(w, tpl)
-}
-
-type WebsiteInspectTpl struct {
- Describe *WebsiteDescribe
- View *WebsiteView
- Err error
-}
-
-func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) {
- var processErr error
-
- user := RequireUserHtml(w, r)
- if user == nil {
- return
- }
-
- ctrl, err := NewWebsiteController(user)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- bucketName := mux.Vars(r)["bucket"]
-
- if r.Method == "POST" {
- r.ParseForm()
- action := strings.Join(r.Form["action"], "")
- switch action {
- case "increase_quota":
- _, processErr = ctrl.Patch(bucketName, &WebsitePatch{Size: &user.Quota.WebsiteSizeBursted})
- case "delete_bucket":
- processErr = ctrl.Delete(bucketName)
- if processErr == nil {
- http.Redirect(w, r, "/website", http.StatusFound)
- }
- default:
- processErr = fmt.Errorf("Unknown action")
- }
-
- }
-
- view, err := ctrl.Inspect(bucketName)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- describe, err := ctrl.Describe()
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- tpl := &WebsiteInspectTpl{describe, view, processErr}
-
- tWebsiteInspect := getTemplate("garage_website_inspect.html")
- tWebsiteInspect.Execute(w, &tpl)
-}
-
-func handleWebsiteVhost(w http.ResponseWriter, r *http.Request) {
- var processErr error
-
- user := RequireUserHtml(w, r)
- if user == nil {
- return
- }
-
- ctrl, err := NewWebsiteController(user)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- bucketName := mux.Vars(r)["bucket"]
-
- if r.Method == "POST" {
- r.ParseForm()
-
- bucket := strings.Join(r.Form["bucket"], "")
- if bucket == "" {
- bucket = strings.Join(r.Form["bucket2"], "")
- }
-
- view, processErr := ctrl.Patch(bucketName, &WebsitePatch{Vhost: &bucket})
- if processErr == nil {
- http.Redirect(w, r, "/website/inspect/"+view.Name.Pretty, http.StatusFound)
- return
- }
- }
-
- view, err := ctrl.Inspect(bucketName)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- describe, err := ctrl.Describe()
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- tpl := &WebsiteInspectTpl{describe, view, processErr}
- tWebsiteEdit := getTemplate("garage_website_edit.html")
- tWebsiteEdit.Execute(w, &tpl)
-}
diff --git a/main.go b/main.go
index 39c7f08..e1b0eb8 100644
--- a/main.go
+++ b/main.go
@@ -159,7 +159,6 @@ func server(args []string) {
r.HandleFunc("/website", handleWebsiteList)
r.HandleFunc("/website/new", handleWebsiteNew)
- r.HandleFunc("/website/configure", handleWebsiteConfigure)
r.HandleFunc("/website/inspect/{bucket}", handleWebsiteInspect)
r.HandleFunc("/website/vhost/{bucket}", handleWebsiteVhost)
diff --git a/templates/garage_key.html b/templates/garage_key.html
deleted file mode 100644
index cf56822..0000000
--- a/templates/garage_key.html
+++ /dev/null
@@ -1,234 +0,0 @@
-{{define "title"}}Profile |{{end}}
-
-{{define "body"}}
-<div class="d-flex">
- <h4>Mes identifiants</h4>
- <a class="ml-auto btn btn-link" href="/website">Mes sites webs</a>
- <a class="ml-4 btn btn-info" href="/">Menu principal</a>
-</div>
-
-<ul class="nav nav-tabs" id="proto" role="tablist">
- <li class="nav-item">
- <a class="nav-link active" id="s3-tab" data-toggle="tab" href="#s3" role="tab" aria-controls="s3" aria-selected="true">S3</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" id="sftp-tab" data-toggle="tab" href="#sftp" role="tab" aria-controls="sftp" aria-selected="false">SFTP</a>
- </li>
-</ul>
-
-<div class="tab-content" id="protocols">
- <div class="tab-pane fade show active" id="s3" role="tabpanel" aria-labelledby="s3-tab">
- <table class="table mt-4">
- <tbody>
- <tr>
- <th scope="row" class="col-md-2">Identifiant de clé</th>
- <td>{{ .S3KeyInfo.AccessKeyId }}</td>
- </tr>
- <tr>
- <th scope="row">Clé secrète</th>
- <td><a href="#" onclick="document.getElementById('secret_key').style.display='inline'; this.style.display='none'">Cliquer pour afficher la clé secrète</a><span id="secret_key" style="display: none">{{ .S3KeyInfo.SecretAccessKey }}</span></td>
- </tr>
- <tr>
- <th scope="row">Région</th>
- <td>garage</td>
- </tr>
- <tr>
- <th scope="row">Endpoint URL</th>
- <td>https://garage.deuxfleurs.fr</td>
- </tr>
- <tr>
- <th scope="row">Type d'URL</th>
- <td>DNS et chemin (préférer chemin)</td>
- </tr>
- <tr>
- <th scope="row">Signature</th>
- <td>Version 4</td>
- </tr>
- </tbody>
- </table>
-
- <p>Configurer votre logiciel :</p>
-
- <div class="accordion" id="softconfig">
- <div class="card">
- <div class="card-header" id="awscli-title">
- <h2 class="mb-0">
- <button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#awscli" aria-expanded="false" aria-controls="awscli">
- awscli
- </button>
- </h2>
- </div>
- <div id="awscli" class="collapse" aria-labelledby="awscli-title" data-parent="#softconfig">
- <div class="card-body">
- <p>Créez un fichier nommé <code>~/.awsrc</code> :</p>
- <pre>
-export AWS_ACCESS_KEY_ID={{ .S3KeyInfo.AccessKeyId }}
-export AWS_SECRET_ACCESS_KEY={{ .S3KeyInfo.SecretAccessKey }}
-export AWS_DEFAULT_REGION='garage'
-
-function aws { command aws --endpoint-url https://garage.deuxfleurs.fr $@ ; }
-aws --version
- </pre>
- <p>Ensuite vous pouvez utiliser awscli :</p>
- <pre>
-source ~/.awsrc
-aws s3 ls
-aws s3 ls s3://my-bucket
-aws s3 cp /tmp/a.txt s3://my-bucket
-...
- </pre>
- </div>
- </div>
- </div>
-
- <div class="card">
- <div class="card-header" id="minio-title">
- <h2 class="mb-0">
- <button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#minio" aria-expanded="true" aria-controls="minio">
- Minio CLI
- </button>
- </h2>
- </div>
-
- <div id="minio" class="collapse" aria-labelledby="minio-title" data-parent="#softconfig">
- <div class="card-body">
- <p>Vous pouvez configurer Minio CLI avec cette commande :</p>
- <pre>
-mc alias set \
- garage \
- https://garage.deuxfleurs.fr \
- {{ .S3KeyInfo.AccessKeyId }} \
- {{ .S3KeyInfo.SecretAccessKey }} \
- --api S3v4
- </pre>
- <p>Et ensuite pour utiliser Minio CLI avec :</p>
- <pre>
-mc ls garage/
-mc cp /tmp/a.txt garage/my-bucket/a.txt
-...
- </pre>
- </div>
- </div>
- </div>
-
- <div class="card">
- <div class="card-header" id="winscp-title">
- <h2 class="mb-0">
- <button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#winscp" aria-expanded="true" aria-controls="winscp">
- WinSCP
- </button>
- </h2>
- </div>
-
- <div id="winscp" class="collapse" aria-labelledby="winscp-title" data-parent="#softconfig">
- <div class="card-body">
- Reportez vous <a href="">au guide</a>
- </div>
- </div>
- </div>
-
- <div class="card">
- <div class="card-header" id="hugo-title">
- <h2 class="mb-0">
- <button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#hugo" aria-expanded="false" aria-controls="hugo">
- Hugo
- </button>
- </h2>
- </div>
- <div id="hugo" class="collapse" aria-labelledby="hugo-title" data-parent="#softconfig">
- <div class="card-body">
- <p>Dans votre fichier <code>config.toml</code>, rajoutez :</p>
- <pre>
-[[deployment.targets]]
- URL = "s3://bucket?endpoint=garage.deuxfleurs.fr&amp;s3ForcePathStyle=true&amp;region=garage"
- </pre>
- <p>Assurez-vous d'avoir un fichier dans lequel les variables <code>AWS_ACCESS_KEY_ID</code> et <code>AWS_SECRET_ACCESS_KEY</code> sont définies,
- ici on suppose que vous avez suivi les instructions de l'outil awscli (ci-dessus) et que vous avez un fichier <code>~/.awsrc</code> qui défini ces variables.
- Ensuite : </p>
- <pre>
-source ~/.awsrc
-hugo deploy
- </pre>
- </div>
- </div>
- </div>
-
- <div class="card">
- <div class="card-header" id="publii-title">
- <h2 class="mb-0">
- <button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#publii" aria-expanded="false" aria-controls="publii">
- Publii
- </button>
- </h2>
- </div>
- <div id="publii" class="collapse" aria-labelledby="publii-title" data-parent="#softconfig">
- <div class="card-body">
- <em>Bientôt...</em>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <!-- sftp -->
- <div class="tab-pane fade" id="sftp" role="tabpanel" aria-labelledby="sftp-tab">
- <table class="table mt-4">
- <tbody>
- <tr>
- <th scope="row">Nom d'utilisateur-ice</th>
- <td>{{ .Login.Info.Username }}</td>
- </tr>
- <tr>
- <th scope="row">Mot de passe</th>
- <td>(votre mot de passe guichet)</td>
- </tr>
- <tr>
- <th scope="row">Hôte</th>
- <td>sftp://bagage.deuxfleurs.fr</td>
- </tr>
- <tr>
- <th scope="row">Port</th>
- <td>2222</td>
- </tr>
- </tbody>
- </table>
- <p>Configurer votre logiciel :</p>
-
- <div class="accordion" id="softconfig2">
- <div class="card">
- <div class="card-header" id="filezilla-title">
- <h2 class="mb-0">
- <button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#filezilla" aria-expanded="false" aria-controls="filezilla">
- scp
- </button>
- </h2>
- </div>
- <div id="filezilla" class="collapse show" aria-labelledby="filezilla-title" data-parent="#softconfig">
- <div class="card-body">
- <p>Un exemple avec SCP :</p>
- <pre>
-scp -oHostKeyAlgorithms=+ssh-rsa -P2222 -r ./public {{ .Login.Info.Username }}@bagage.deuxfleurs.fr:mon_bucket/
- </pre>
- </div>
- </div>
- </div>
- <div class="card">
- <div class="card-header" id="filezilla-title">
- <h2 class="mb-0">
- <button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#filezilla" aria-expanded="false" aria-controls="filezilla">
- Filezilla
- </button>
- </h2>
- </div>
- <div id="filezilla" class="collapse" aria-labelledby="filezilla-title" data-parent="#softconfig">
- <div class="card-body">
- <em>Bientôt</em>
- </div>
- </div>
- </div>
- </div>
-
- </div>
-</div>
-
-{{end}}
diff --git a/templates/garage_website_inspect.html b/templates/garage_website_inspect.html
index a8f463d..af87955 100644
--- a/templates/garage_website_inspect.html
+++ b/templates/garage_website_inspect.html
@@ -59,10 +59,44 @@
{{ end }}
</p>
+ <h5 class="mt-3">Informations de connexion</h5>
+ <table class="table mt-4">
+ <tbody>
+ <tr>
+ <th scope="row" class="col-md-2">Identifiant de clé</th>
+ <td>{{ .View.AccessKeyId }}</td>
+ </tr>
+ <tr>
+ <th scope="row">Clé secrète</th>
+ <td>
+ <a href="#" onclick="document.getElementById('secret_key').style.display='inline'; this.style.display='none'">Cliquer pour afficher la clé secrète</a>
+ <span id="secret_key" style="display: none">{{ .View.SecretAccessKey }}</span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Région</th>
+ <td>garage</td>
+ </tr>
+ <tr>
+ <th scope="row">Endpoint URL</th>
+ <td>https://garage.deuxfleurs.fr</td>
+ </tr>
+ <tr>
+ <th scope="row">Type d'URL</th>
+ <td>DNS et chemin (préférer chemin)</td>
+ </tr>
+ <tr>
+ <th scope="row">Signature</th>
+ <td>Version 4</td>
+ </tr>
+ </tbody>
+ </table>
+
<h5 class="mt-3">Actions</h5>
<form action="" method="post">
<div class="btn-group" role="group" aria-label="Actions sur le site web">
<button class="btn btn-secondary" name="action" value="increase_quota">Augmenter le quota</button>
+ <button class="btn btn-secondary" name="action" value="rotate_key">Rotation de la clé</button>
<a class="btn btn-secondary" href="/website/vhost/{{ .View.Name.Pretty }}">Changer le nom de domaine</a>
<button class="btn btn-danger" name="action" value="delete_bucket">Supprimer</button>
</div>
diff --git a/templates/home.html b/templates/home.html
index dd88d13..3210a13 100644
--- a/templates/home.html
+++ b/templates/home.html
@@ -24,10 +24,9 @@
<div class="mt-3">
<div class="card">
<div class="card-header">
- Mon espace sur la toile
+ Mes services
</div>
<div class="list-group list-group-flush">
- <a class="list-group-item list-group-item-action" href="/website/configure">Mes identifiants</a>
<a class="list-group-item list-group-item-action" href="/website">Mes sites Web</a>
</div>
</div>
diff --git a/website.go b/website.go
index 74daf89..cdfae89 100644
--- a/website.go
+++ b/website.go
@@ -81,26 +81,17 @@ func NewWebsiteController(user *LoggedUser) (*WebsiteController, error) {
}
type WebsiteDescribe struct {
- AccessKeyId string `json:"access_key_id"`
- SecretAccessKey string `json:"secret_access_key"`
AllowedWebsites *QuotaStat `json:"quota_website_count"`
BurstBucketQuotaSize string `json:"burst_bucket_quota_size"`
Websites []*WebsiteId `json:"vhosts"`
}
func (w *WebsiteController) Describe() (*WebsiteDescribe, error) {
- s3key, err := w.User.S3KeyInfo()
- if err != nil {
- return nil, err
- }
-
r := make([]*WebsiteId, 0, len(w.PrettyList))
for _, k := range w.PrettyList {
r = append(r, w.WebsiteIdx[k])
}
return &WebsiteDescribe{
- *s3key.AccessKeyId,
- *s3key.SecretAccessKey,
&w.WebsiteCount,
w.User.Quota.WebsiteSizeBurstedPretty(),
r}, nil
@@ -231,9 +222,11 @@ func (w *WebsiteController) Delete(pretty string) error {
}
type WebsiteView struct {
- Name *WebsiteId `json:"vhost"`
- Size QuotaStat `json:"quota_size"`
- Files QuotaStat `json:"quota_files"`
+ Name *WebsiteId `json:"vhost"`
+ AccessKeyId string `json:"access_key_id"`
+ SecretAccessKey string `json:"secret_access_key"`
+ Size QuotaStat `json:"quota_size"`
+ Files QuotaStat `json:"quota_files"`
}
func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView {
@@ -242,10 +235,16 @@ func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView {
wid := NewWebsiteIdFromBucketInfo(binfo)
size := NewQuotaStat(*binfo.Bytes, (&q).GetMaxSize(), true)
objects := NewQuotaStat(*binfo.Objects, (&q).GetMaxObjects(), false)
- return &WebsiteView{wid, size, objects}
+ return &WebsiteView{
+ wid,
+ "not yet implemented",
+ "not yet implemented",
+ size,
+ objects,
+ }
}
type WebsitePatch struct {
- Size *int64 `json:"quota_size"`
+ Size *int64 `json:"quota_size"`
Vhost *string `json:"vhost"`
}
diff --git a/webui_website.go b/webui_website.go
new file mode 100644
index 0000000..e8a89c0
--- /dev/null
+++ b/webui_website.go
@@ -0,0 +1,175 @@
+package main
+
+import (
+ "fmt"
+ "github.com/gorilla/mux"
+ "net/http"
+ "strings"
+)
+
+// --- Start page rendering functions
+func handleWebsiteList(w http.ResponseWriter, r *http.Request) {
+ user := RequireUserHtml(w, r)
+ if user == nil {
+ return
+ }
+
+ ctrl, err := NewWebsiteController(user)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ if len(ctrl.PrettyList) > 0 {
+ http.Redirect(w, r, "/website/inspect/"+ctrl.PrettyList[0], http.StatusFound)
+ } else {
+ http.Redirect(w, r, "/website/new", http.StatusFound)
+ }
+}
+
+type WebsiteNewTpl struct {
+ Ctrl *WebsiteController
+ Err error
+}
+
+func handleWebsiteNew(w http.ResponseWriter, r *http.Request) {
+ user := RequireUserHtml(w, r)
+ if user == nil {
+ return
+ }
+
+ ctrl, err := NewWebsiteController(user)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ tpl := &WebsiteNewTpl{ctrl, nil}
+
+ tWebsiteNew := getTemplate("garage_website_new.html")
+ if r.Method == "POST" {
+ r.ParseForm()
+
+ bucket := strings.Join(r.Form["bucket"], "")
+ if bucket == "" {
+ bucket = strings.Join(r.Form["bucket2"], "")
+ }
+
+ view, err := ctrl.Create(bucket)
+ if err != nil {
+ tpl.Err = err
+ tWebsiteNew.Execute(w, tpl)
+ return
+ }
+
+ http.Redirect(w, r, "/website/inspect/"+view.Name.Pretty, http.StatusFound)
+ return
+ }
+
+ tWebsiteNew.Execute(w, tpl)
+}
+
+type WebsiteInspectTpl struct {
+ Describe *WebsiteDescribe
+ View *WebsiteView
+ Err error
+}
+
+func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) {
+ var processErr error
+
+ user := RequireUserHtml(w, r)
+ if user == nil {
+ return
+ }
+
+ ctrl, err := NewWebsiteController(user)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ bucketName := mux.Vars(r)["bucket"]
+
+ if r.Method == "POST" {
+ r.ParseForm()
+ action := strings.Join(r.Form["action"], "")
+ switch action {
+ case "increase_quota":
+ _, processErr = ctrl.Patch(bucketName, &WebsitePatch{Size: &user.Quota.WebsiteSizeBursted})
+ case "delete_bucket":
+ processErr = ctrl.Delete(bucketName)
+ if processErr == nil {
+ http.Redirect(w, r, "/website", http.StatusFound)
+ }
+ default:
+ processErr = fmt.Errorf("Unknown action")
+ }
+
+ }
+
+ view, err := ctrl.Inspect(bucketName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ describe, err := ctrl.Describe()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ tpl := &WebsiteInspectTpl{describe, view, processErr}
+
+ tWebsiteInspect := getTemplate("garage_website_inspect.html")
+ tWebsiteInspect.Execute(w, &tpl)
+}
+
+func handleWebsiteVhost(w http.ResponseWriter, r *http.Request) {
+ var processErr error
+
+ user := RequireUserHtml(w, r)
+ if user == nil {
+ return
+ }
+
+ ctrl, err := NewWebsiteController(user)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ bucketName := mux.Vars(r)["bucket"]
+
+ if r.Method == "POST" {
+ r.ParseForm()
+
+ bucket := strings.Join(r.Form["bucket"], "")
+ if bucket == "" {
+ bucket = strings.Join(r.Form["bucket2"], "")
+ }
+
+ view, processErr := ctrl.Patch(bucketName, &WebsitePatch{Vhost: &bucket})
+ if processErr == nil {
+ http.Redirect(w, r, "/website/inspect/"+view.Name.Pretty, http.StatusFound)
+ return
+ }
+ }
+
+ view, err := ctrl.Inspect(bucketName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ describe, err := ctrl.Describe()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ tpl := &WebsiteInspectTpl{describe, view, processErr}
+ tWebsiteEdit := getTemplate("garage_website_edit.html")
+ tWebsiteEdit.Execute(w, &tpl)
+}