Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modified user create, edut and update routes to use right http verbs #92

Merged
merged 8 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
#branches: [ main ]

jobs:

Expand All @@ -23,4 +23,4 @@ jobs:

- name: Test
run: go test -v ./... --cover

10 changes: 5 additions & 5 deletions internal/webserver/controller/user/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

// Update gathers information from the edit user form and updates user data
func (u *Controller) Update(c *fiber.Ctx) error {
user, err := u.repository.FindByUuid(c.FormValue("id"))
user, err := u.repository.FindByUsername(c.Params("username"))
if err != nil {
log.Println(err.Error())
return fiber.ErrInternalServerError
Expand All @@ -27,7 +27,7 @@ func (u *Controller) Update(c *fiber.Ctx) error {
session = val
}

if session.Role != model.RoleAdmin && user.Uuid != session.Uuid {
if session.Role != model.RoleAdmin && user.Username != session.Username {
return fiber.ErrForbidden
}

Expand All @@ -51,13 +51,13 @@ func (u *Controller) updateUserData(c *fiber.Ctx, user *model.User, session mode
}

if len(validationErrs) > 0 {
return c.Render("users/edit", fiber.Map{
return c.Status(fiber.StatusBadRequest).Render("users/edit", fiber.Map{
"Title": "Edit user",
"User": user,
"MinPasswordLength": u.config.MinPasswordLength,
"UsernamePattern": model.UsernamePattern,
"Errors": validationErrs,
}, "layout")
}, "partials/main")
}

if err := u.repository.Update(user); err != nil {
Expand Down Expand Up @@ -89,7 +89,7 @@ func (u *Controller) updateUserData(c *fiber.Ctx, user *model.User, session mode
"UsernamePattern": model.UsernamePattern,
"Errors": validationErrs,
"Message": "Profile updated",
}, "layout")
}, "partials/main")
}

func (u *Controller) validate(c *fiber.Ctx, user *model.User, session model.Session) (map[string]string, error) {
Expand Down
2 changes: 1 addition & 1 deletion internal/webserver/embedded/js/foliate-js/reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ const url = document.getElementById('url').value
if (url) fetch(url)
.then(res => {
if (res.status == 403) {
location.reload()
return location.reload()
}
return res.blob()
})
Expand Down
1 change: 1 addition & 0 deletions internal/webserver/embedded/js/htmx.min.js

Large diffs are not rendered by default.

129 changes: 129 additions & 0 deletions internal/webserver/embedded/js/response-targets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
(function() {
/** @type {import("../htmx").HtmxInternalApi} */
var api

var attrPrefix = 'hx-target-'

// IE11 doesn't support string.startsWith
function startsWith(str, prefix) {
return str.substring(0, prefix.length) === prefix
}

/**
* @param {HTMLElement} elt
* @param {number} respCode
* @returns {HTMLElement | null}
*/
function getRespCodeTarget(elt, respCodeNumber) {
if (!elt || !respCodeNumber) return null

var respCode = respCodeNumber.toString()

// '*' is the original syntax, as the obvious character for a wildcard.
// The 'x' alternative was added for maximum compatibility with HTML
// templating engines, due to ambiguity around which characters are
// supported in HTML attributes.
//
// Start with the most specific possible attribute and generalize from
// there.
var attrPossibilities = [
respCode,

respCode.substr(0, 2) + '*',
respCode.substr(0, 2) + 'x',

respCode.substr(0, 1) + '*',
respCode.substr(0, 1) + 'x',
respCode.substr(0, 1) + '**',
respCode.substr(0, 1) + 'xx',

'*',
'x',
'***',
'xxx'
]
if (startsWith(respCode, '4') || startsWith(respCode, '5')) {
attrPossibilities.push('error')
}

for (var i = 0; i < attrPossibilities.length; i++) {
var attr = attrPrefix + attrPossibilities[i]
var attrValue = api.getClosestAttributeValue(elt, attr)
if (attrValue) {
if (attrValue === 'this') {
return api.findThisElement(elt, attr)
} else {
return api.querySelectorExt(elt, attrValue)
}
}
}

return null
}

/** @param {Event} evt */
function handleErrorFlag(evt) {
if (evt.detail.isError) {
if (htmx.config.responseTargetUnsetsError) {
evt.detail.isError = false
}
} else if (htmx.config.responseTargetSetsError) {
evt.detail.isError = true
}
}

htmx.defineExtension('response-targets', {

/** @param {import("../htmx").HtmxInternalApi} apiRef */
init: function(apiRef) {
api = apiRef

if (htmx.config.responseTargetUnsetsError === undefined) {
htmx.config.responseTargetUnsetsError = true
}
if (htmx.config.responseTargetSetsError === undefined) {
htmx.config.responseTargetSetsError = false
}
if (htmx.config.responseTargetPrefersExisting === undefined) {
htmx.config.responseTargetPrefersExisting = false
}
if (htmx.config.responseTargetPrefersRetargetHeader === undefined) {
htmx.config.responseTargetPrefersRetargetHeader = true
}
},

/**
* @param {string} name
* @param {Event} evt
*/
onEvent: function(name, evt) {
if (name === 'htmx:beforeSwap' &&
evt.detail.xhr &&
evt.detail.xhr.status !== 200) {
if (evt.detail.target) {
if (htmx.config.responseTargetPrefersExisting) {
evt.detail.shouldSwap = true
handleErrorFlag(evt)
return true
}
if (htmx.config.responseTargetPrefersRetargetHeader &&
evt.detail.xhr.getAllResponseHeaders().match(/HX-Retarget:/i)) {
evt.detail.shouldSwap = true
handleErrorFlag(evt)
return true
}
}
if (!evt.detail.requestConfig) {
return true
}
var target = getRespCodeTarget(evt.detail.requestConfig.elt, evt.detail.xhr.status)
if (target) {
handleErrorFlag(evt)
evt.detail.shouldSwap = true
evt.detail.target = target
}
return true
}
}
})
})()
25 changes: 25 additions & 0 deletions internal/webserver/embedded/js/xh-error-check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
document.body.addEventListener('htmx:afterRequest', function (evt) {
const errorTarget = document.getElementById("box-error")
const unexpectedServerError = errorTarget.getAttribute("data-unexpected-server-error")
const unexpectedError = errorTarget.getAttribute("data-unexpected-error")
if (evt.detail.successful) {
// Successful request, clear out alert
errorTarget.setAttribute("hidden", "true")
errorTarget.innerText = "";
} else if (evt.detail.failed && evt.detail.xhr) {
// Server error with response contents, equivalent to htmx:responseError
const xhr = evt.detail.xhr;
if (xhr.status == "403") {
return location.reload()
}

console.warn("Server error", evt.detail)
errorTarget.innerText = unexpectedServerError + `${xhr.status} - ${xhr.statusText}`
errorTarget.removeAttribute("hidden")
} else {
// Unspecified failure, usually caused by network error
console.error("Unexpected htmx error", evt.detail)
errorTarget.innerText = unexpectedError
errorTarget.removeAttribute("hidden")
}
});
4 changes: 3 additions & 1 deletion internal/webserver/embedded/translations/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"Indexing in progress, search results may not be accurate.": "Indexando documentos, los resultados de búsqueda pueden no ser precisos."
"Remaining time: %s minutes": "Tiempo restante: %s minutos"
"There was an error deleting the user, please try again later": "Hubo un error al borrar el usuario, por favor, vuelva a intentarlo más tarde"
"A user with this username already exists": "Ya exista un usuario con ese nombre de usuario"
"A user with this username already exists": "Ya existe un usuario con ese nombre de usuario"
"A user with this email address already exists": "Ya existe un usuario con esa dirección de correo electrónico"
"Username can only have letters, numbers, _, - and .": "El nombre de usuario solo puede contener letras, números. _, - y ."
"Only letters, numbers, _, - and . allowed": "Solo se permiten letras, números, _, - y ."
Expand All @@ -133,3 +133,5 @@
"Go left": "Ir a la izquierda"
"Go right": "Ir a la derecha"
"Session expired, please log in again.": "Sesión expirada, por favor identifícate de nuevo."
"Unexpected error, check your connection and try to refresh the page.": "Error inesperado, comprueba tu conexión y recarga la página."
"Unexpected server error": "Error inesperado en el servidor"
2 changes: 2 additions & 0 deletions internal/webserver/embedded/translations/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,5 @@
"Go left": "Aller à gauche"
"Go right": "Aller à droite"
"Session expired, please log in again.": "Session expirée, veuillez vous reconnecter."
"Unexpected error, check your connection and try to refresh the page.": "Erreur inattendue, vérifiez votre connexion et essayez d'actualiser la page."
"Unexpected server error": "Erreur de serveur inattendue"
55 changes: 12 additions & 43 deletions internal/webserver/embedded/views/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<script src="/js/color-mode-toggler.js"></script>
</head>

<body class="d-flex flex-column h-100">
<body class="d-flex flex-column h-100" hx-ext="response-targets">
<header>
<nav class="navbar navbar-expand-md fixed-top">
<div class="container">
Expand Down Expand Up @@ -62,7 +62,7 @@ <h5 class="offcanvas-title" id="offcanvasNavbarLabel">Coreander</h5>
{{t .Lang "Users"}}
</a>
{{else}}
<a class="dropdown-item" href="/{{.Lang}}/users/{{.Session.Username}}/edit">
<a class="dropdown-item" href="/{{.Lang}}/users/{{.Session.Username}}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
fill="currentColor" class="bi bi-person-fill" viewBox="0 0 16 16">
<path
Expand Down Expand Up @@ -130,49 +130,16 @@ <h5 class="offcanvas-title" id="offcanvasNavbarLabel">Coreander</h5>
</div>
</nav>
</header>

<main class="mt-5">
<div class="container mt-5">
{{if .RemainingIndexingTime}}
<div class="row text-start">
<div class="alert alert-warning" role="alert">
<p>{{t .Lang "Indexing in progress, search results may not be accurate."}}</p>
<div class="progress" role="progressbar" aria-label='{{t .Lang "Indexing progress"}}' aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar" style="width: {{.IndexingProgressPercentage}}%">{{.IndexingProgressPercentage}}%</div>
</div>
<p class="text-end"><small>{{t .Lang "Remaining time: %s minutes" .RemainingIndexingTime}}</small></p>
</div>
</div>
{{end}}

{{if .Error}}
<div class="row text-start">
<div class="alert alert-danger" role="alert">
{{t .Lang .Error}}
</div>
</div>
{{end}}

{{if .Warning}}
<div class="row text-start">
<div class="alert alert-warning" role="alert">
{{t .Lang .Warning}}
</div>
</div>
{{end}}

{{if .Message}}
<div class="row text-start">
<div class="alert alert-success" role="alert">
{{t .Lang .Message}}
</div>
</div>
{{end}}

{{embed}}
</div>
{{template "partials/main"
dict "Lang" .Lang
"RemainingIndexingTime" .RemainingIndexingTime
"IndexingProgressPercentage" .IndexingProgressPercentage
"Error" .Error
"Warning" .Warning
"Message" .Message
"Embed" .Embed}}
</main>

<footer class="footer mt-auto py-5">
<div class="container">
<span class="text-muted"><small>{{t .Lang "Made with words by Sergio Vera"}} - <a
Expand All @@ -181,6 +148,8 @@ <h5 class="offcanvas-title" id="offcanvasNavbarLabel">Coreander</h5>
</div>
</footer>
<script src="/js/bootstrap.bundle.min.js"></script>
<script src="/js/htmx.min.js"></script>
<script src="/js/response-targets.js"></script>
</body>

</html>
44 changes: 44 additions & 0 deletions internal/webserver/embedded/views/partials/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<div class="container mt-5">
{{if .RemainingIndexingTime}}
<div class="row text-start">
<div class="alert alert-warning" role="alert">
<p>{{t .Lang "Indexing in progress, search results may not be accurate."}}</p>
<div class="progress" role="progressbar" aria-label='{{t .Lang "Indexing progress"}}' aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar" style="width: {{.IndexingProgressPercentage}}%">{{.IndexingProgressPercentage}}%</div>
</div>
<p class="text-end"><small>{{t .Lang "Remaining time: %s minutes" .RemainingIndexingTime}}</small></p>
</div>
</div>
{{end}}

<div class="row text-start">
<div class="alert alert-danger" role="alert" hidden id="box-error" data-unexpected-error='{{t .Lang "Unexpected error, check your connection and try to refresh the page."}}' data-unexpected-server-error='{{t .Lang "Unexpected server error"}}'>
</div>
</div>

{{if .Error}}
<div class="row text-start">
<div class="alert alert-danger" role="alert">
{{t .Lang .Error}}
</div>
</div>
{{end}}

{{if .Warning}}
<div class="row text-start">
<div class="alert alert-warning" role="alert">
{{t .Lang .Warning}}
</div>
</div>
{{end}}

{{if .Message}}
<div class="row text-start">
<div class="alert alert-success" role="alert">
{{t .Lang .Message}}
</div>
</div>
{{end}}

{{embed}}
</div>
Loading