Proper validation, always show menu in management pages

This commit is contained in:
Jan Zípek 2024-04-01 12:41:04 +02:00
parent 74a4019dc5
commit 71b1b3ad0b
Signed by: kamen
GPG Key ID: A17882625B33AC31
16 changed files with 199 additions and 289 deletions

View File

@ -1,18 +1,27 @@
main.layout { main.layout {
height: 100%; height: 100%;
overflow: auto;
display: flex; display: flex;
flex-direction: column;
> .right-content {
overflow: auto;
display: flex;
flex-direction: column;
height: 100%;
flex: 1;
}
} }
.menu { .menu {
position: fixed; &.as-popup {
left: 0; position: fixed;
top: 0; left: 0;
bottom: 0; top: 0;
bottom: 0;
}
color: var(--box-fg-color); color: var(--box-fg-color);
width: 15rem; width: 15rem;
transition: left 0.1s; transition: left 0.1s, margin-left 0.1s;
z-index: 2; z-index: 2;
display: flex; display: flex;
@ -62,6 +71,11 @@ main.layout {
text-decoration: none; text-decoration: none;
padding: 0.4rem 1rem; padding: 0.4rem 1rem;
display: flex; display: flex;
color: var(--box-fg-color);
&.current {
background-color: var(--box-border-color);
}
&:hover { &:hover {
background-color: var(--box-border-color); background-color: var(--box-border-color);
@ -88,7 +102,9 @@ header.header {
> .inner { > .inner {
display: flex; display: flex;
padding: 0.5rem 0.5rem 0.5rem 0; padding: 0.5rem 0.5rem 0.5rem 0;
height: 2.2rem;
background-color: var(--header-bg-color); background-color: var(--header-bg-color);
align-items: center;
> .menu-button { > .menu-button {
display: flex; display: flex;

View File

@ -1,7 +1,6 @@
.alerts-page .content { .alerts-page .content {
width: 50rem; width: 50rem;
padding: 1rem; padding: 1rem 2rem;
margin: 0 auto;
.contact-points { .contact-points {
margin-bottom: 2rem; margin-bottom: 2rem;

View File

@ -1,9 +1,11 @@
.dashboard { /*
.dashboard-page {
height: 100%; height: 100%;
overflow: auto; overflow: auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
*/
.dashboard-head { .dashboard-head {
display: flex; display: flex;

View File

@ -11,8 +11,7 @@
section.content { section.content {
width: 50rem; width: 50rem;
margin: 0 auto; padding: 1rem 2rem;
padding: 1rem;
} }
} }

View File

@ -8,24 +8,39 @@ type Props = {
children: ComponentChild children: ComponentChild
header?: ComponentChild header?: ComponentChild
className?: string className?: string
popupMenu?: boolean
} }
export const UserLayout = ({ children, header, className }: Props) => { export const UserLayout = ({
const [menuShown, setMenuShown] = useState(false) children,
header,
className,
popupMenu = false,
}: Props) => {
const [menuShown, setMenuShown] = useState(popupMenu ? false : true)
return ( return (
<main className={cn('layout', className)}> <main className={cn('layout', className)}>
<header className={'header'}> <UserMenu
<div className="inner"> popup={popupMenu}
<div className="menu-button" onClick={() => setMenuShown(true)}> shown={menuShown}
<MenuIcon /> onHide={() => setMenuShown(false)}
/>
<div className="right-content">
<header className="header">
<div className="inner">
<div
className="menu-button"
onClick={() => setMenuShown(!menuShown)}
>
<MenuIcon />
</div>
{header}
</div> </div>
{header} {/*<div className="shadow"></div>*/}
</div> </header>
<div className="shadow"></div> <section className="content">{children}</section>
</header> </div>
<section className="content">{children}</section>
<UserMenu shown={menuShown} onHide={() => setMenuShown(false)} />
</main> </main>
) )
} }

View File

@ -0,0 +1,19 @@
import { cn } from '@/utils/cn'
import { useHashRouterLocation } from '@/utils/hooks/useHashLocation'
import { ComponentChildren } from 'preact'
import { Link } from 'wouter'
type Props = {
href: string
children: ComponentChildren
}
export const NavLink = ({ href, children }: Props) => {
const [location] = useHashRouterLocation()
return (
<Link href={href} className={cn(location === href && 'current')}>
{children}
</Link>
)
}

View File

@ -1,33 +1,44 @@
import { CancelIcon } from '@/icons' import { CancelIcon } from '@/icons'
import { Link } from 'wouter' import { cn } from '@/utils/cn'
import { NavLink } from './NavLink'
type Props = { type Props = {
popup: boolean
shown: boolean shown: boolean
onHide: () => void onHide: () => void
} }
export const UserMenu = ({ shown, onHide }: Props) => { export const UserMenu = ({ shown, popup, onHide }: Props) => {
return ( return (
<> <>
<div className="menu" style={{ left: !shown ? '-20rem' : '0' }}> <div
className={cn('menu', popup && 'as-popup')}
style={
popup
? { left: !shown ? '-20rem' : '0' }
: { marginLeft: !shown ? '-15rem' : '0' }
}
>
<div className="inner"> <div className="inner">
<div className="menu-header"> <div className="menu-header">
<div className="menu-close" onClick={onHide}> {popup && (
<CancelIcon /> <div className="menu-close" onClick={onHide}>
</div> <CancelIcon />
</div>
)}
<h2>Graphicek</h2> <h2>Graphicek</h2>
</div> </div>
<nav> <nav>
<a href="#">📈 Dashboards</a> <NavLink href="/">📈 Dashboards</NavLink>
<Link href="/sensors"> Sensors</Link> <NavLink href="/sensors"> Sensors</NavLink>
<Link href="/alerts">🚨 Alerts</Link> <NavLink href="/alerts">🚨 Alerts</NavLink>
<a href="#"> Settings</a> {/*<a href="#">⚙️ Settings</a>*/}
</nav> </nav>
</div> </div>
<div className="shadow"></div> {popup && <div className="shadow"></div>}
</div> </div>
{shown && <div className="menu-overlay" onClick={onHide}></div>} {popup && shown && <div className="menu-overlay" onClick={onHide}></div>}
</> </>
) )
} }

View File

@ -6,7 +6,11 @@ import { DashboardContextProvider } from './contexts/DashboardContext'
export const NewDashboardPage = () => { export const NewDashboardPage = () => {
return ( return (
<DashboardContextProvider> <DashboardContextProvider>
<UserLayout className="dashboard" header={<DashboardHeader />}> <UserLayout
className="dashboard-page"
header={<DashboardHeader />}
popupMenu
>
<DashboardGrid /> <DashboardGrid />
</UserLayout> </UserLayout>
</DashboardContextProvider> </DashboardContextProvider>

View File

@ -3,27 +3,17 @@ package routes
import ( import (
"basic-sensor-receiver/app" "basic-sensor-receiver/app"
"net/http" "net/http"
"strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type postAlertsBody struct { type postAndPutAlertsBody struct {
ContactPointId int64 `json:"contactPointId"` ContactPointId int64 `json:"contactPointId" binding:"required"`
Name string `json:"name"` Name string `json:"name" binding:"required"`
Condition string `json:"condition"` Condition string `json:"condition" binding:"required"`
TriggerInterval int64 `json:"triggerInterval"` TriggerInterval int64 `json:"triggerInterval" binding:"required"`
CustomMessage string `json:"customMessage"` CustomMessage string `json:"customMessage" binding:"required"`
CustomResolvedMessage string `json:"customResolvedMessage"` CustomResolvedMessage string `json:"customResolvedMessage" binding:"required"`
}
type putAlertsBody struct {
ContactPointId int64 `json:"contactPointId"`
Name string `json:"name"`
Condition string `json:"condition"`
TriggerInterval int64 `json:"triggerInterval"`
CustomMessage string `json:"customMessage"`
CustomResolvedMessage string `json:"customResolvedMessage"`
} }
func GetAlerts(s *app.Server) gin.HandlerFunc { func GetAlerts(s *app.Server) gin.HandlerFunc {
@ -41,12 +31,8 @@ func GetAlerts(s *app.Server) gin.HandlerFunc {
func PostAlerts(s *app.Server) gin.HandlerFunc { func PostAlerts(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := postAlertsBody{} body := postAndPutAlertsBody{}
bindJSONBodyOrAbort(c, &body)
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
alert, err := s.Services.Alerts.Create(body.ContactPointId, body.Name, body.Condition, body.TriggerInterval, body.CustomMessage, body.CustomResolvedMessage) alert, err := s.Services.Alerts.Create(body.ContactPointId, body.Name, body.Condition, body.TriggerInterval, body.CustomMessage, body.CustomResolvedMessage)
@ -61,19 +47,10 @@ func PostAlerts(s *app.Server) gin.HandlerFunc {
func PutAlert(s *app.Server) gin.HandlerFunc { func PutAlert(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := putAlertsBody{} alertId := getIntParamOrAbort(c, "alertId")
alertId, err := getAlertId(c) body := postAndPutAlertsBody{}
bindJSONBodyOrAbort(c, &body)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
alert, err := s.Services.Alerts.Update(alertId, body.ContactPointId, body.Name, body.Condition, body.TriggerInterval, body.CustomMessage, body.CustomResolvedMessage) alert, err := s.Services.Alerts.Update(alertId, body.ContactPointId, body.Name, body.Condition, body.TriggerInterval, body.CustomMessage, body.CustomResolvedMessage)
@ -88,12 +65,7 @@ func PutAlert(s *app.Server) gin.HandlerFunc {
func GetAlert(s *app.Server) gin.HandlerFunc { func GetAlert(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
alertId, err := getAlertId(c) alertId := getIntParamOrAbort(c, "alertId")
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
alert, err := s.Services.Alerts.GetById(alertId) alert, err := s.Services.Alerts.GetById(alertId)
@ -108,12 +80,7 @@ func GetAlert(s *app.Server) gin.HandlerFunc {
func DeleteAlert(s *app.Server) gin.HandlerFunc { func DeleteAlert(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
alertId, err := getAlertId(c) alertId := getIntParamOrAbort(c, "alertId")
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
if err := s.Services.Alerts.DeleteById(alertId); err != nil { if err := s.Services.Alerts.DeleteById(alertId); err != nil {
c.AbortWithError(http.StatusInternalServerError, err) c.AbortWithError(http.StatusInternalServerError, err)
@ -123,9 +90,3 @@ func DeleteAlert(s *app.Server) gin.HandlerFunc {
c.Status(http.StatusOK) c.Status(http.StatusOK)
} }
} }
func getAlertId(c *gin.Context) (int64, error) {
id := c.Param("alertId")
return strconv.ParseInt(id, 10, 64)
}

View File

@ -8,18 +8,14 @@ import (
) )
type postLoginBody struct { type postLoginBody struct {
Username string `json:"username"` Username string `json:"username" binding:"required"`
Password string `json:"password"` Password string `json:"password" binding:"required"`
} }
func Login(s *app.Server) gin.HandlerFunc { func Login(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := postLoginBody{} body := postLoginBody{}
bindJSONBodyOrAbort(c, &body)
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(400, err)
return
}
if body.Password != s.Config.AuthPassword || body.Username != s.Config.AuthUsername { if body.Password != s.Config.AuthPassword || body.Username != s.Config.AuthUsername {
c.AbortWithStatus(401) c.AbortWithStatus(401)
@ -36,19 +32,7 @@ func Login(s *app.Server) gin.HandlerFunc {
func Logout(s *app.Server) gin.HandlerFunc { func Logout(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := postLoginBody{} if err := s.Services.Auth.Logout(c); err != nil {
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(400, err)
return
}
if body.Password != s.Config.AuthPassword || body.Username != s.Config.AuthUsername {
c.AbortWithStatus(401)
return
}
if err := s.Services.Auth.Login(c); err != nil {
c.AbortWithError(500, err) c.AbortWithError(500, err)
} }

View File

@ -3,26 +3,19 @@ package routes
import ( import (
"basic-sensor-receiver/app" "basic-sensor-receiver/app"
"net/http" "net/http"
"strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type postContactPointsBody struct { type postOrPutContactPointsBody struct {
Name string `json:"name"` Name string `json:"name" binding:"required"`
Type string `json:"type"` Type string `json:"type" binding:"required,oneof=telegram"`
TypeConfig string `json:"typeConfig"` TypeConfig string `json:"typeConfig" binding:"required"`
}
type putContactPointsBody struct {
Name string `json:"name"`
Type string `json:"type"`
TypeConfig string `json:"typeConfig"`
} }
type testContactPointBody struct { type testContactPointBody struct {
Type string `json:"type"` Type string `json:"type" binding:"required,oneof=telegram"`
TypeConfig string `json:"typeConfig"` TypeConfig string `json:"typeConfig" binding:"required"`
} }
func GetContactPoints(s *app.Server) gin.HandlerFunc { func GetContactPoints(s *app.Server) gin.HandlerFunc {
@ -40,12 +33,8 @@ func GetContactPoints(s *app.Server) gin.HandlerFunc {
func PostContactPoints(s *app.Server) gin.HandlerFunc { func PostContactPoints(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := postContactPointsBody{} body := postOrPutContactPointsBody{}
bindJSONBodyOrAbort(c, &body)
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
contactPoint, err := s.Services.ContactPoints.Create(body.Name, body.Type, body.TypeConfig) contactPoint, err := s.Services.ContactPoints.Create(body.Name, body.Type, body.TypeConfig)
@ -60,19 +49,10 @@ func PostContactPoints(s *app.Server) gin.HandlerFunc {
func PutContactPoint(s *app.Server) gin.HandlerFunc { func PutContactPoint(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := putContactPointsBody{} contactPointId := getIntParamOrAbort(c, "contactPointId")
contactPointId, err := getContactPointId(c) body := postOrPutContactPointsBody{}
bindJSONBodyOrAbort(c, &body)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
contactPoint, err := s.Services.ContactPoints.Update(contactPointId, body.Name, body.Type, body.TypeConfig) contactPoint, err := s.Services.ContactPoints.Update(contactPointId, body.Name, body.Type, body.TypeConfig)
@ -87,12 +67,7 @@ func PutContactPoint(s *app.Server) gin.HandlerFunc {
func GetContactPoint(s *app.Server) gin.HandlerFunc { func GetContactPoint(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
contactPointId, err := getContactPointId(c) contactPointId := getIntParamOrAbort(c, "contactPointId")
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
contactPoint, err := s.Services.ContactPoints.GetById(contactPointId) contactPoint, err := s.Services.ContactPoints.GetById(contactPointId)
@ -107,14 +82,9 @@ func GetContactPoint(s *app.Server) gin.HandlerFunc {
func DeleteContactPoint(s *app.Server) gin.HandlerFunc { func DeleteContactPoint(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
contactPointId, err := getContactPointId(c) contactPointId := getIntParamOrAbort(c, "contactPointId")
if err != nil { err := s.Services.ContactPoints.Delete(contactPointId)
c.AbortWithError(http.StatusBadRequest, err)
return
}
err = s.Services.ContactPoints.Delete(contactPointId)
if err != nil { if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) c.AbortWithError(http.StatusInternalServerError, err)
@ -128,11 +98,7 @@ func DeleteContactPoint(s *app.Server) gin.HandlerFunc {
func TestContactPoint(s *app.Server) gin.HandlerFunc { func TestContactPoint(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := testContactPointBody{} body := testContactPointBody{}
bindJSONBodyOrAbort(c, &body)
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
err := s.Services.ContactPoints.Test(body.Type, body.TypeConfig) err := s.Services.ContactPoints.Test(body.Type, body.TypeConfig)
@ -144,9 +110,3 @@ func TestContactPoint(s *app.Server) gin.HandlerFunc {
c.AbortWithStatus(http.StatusOK) c.AbortWithStatus(http.StatusOK)
} }
} }
func getContactPointId(c *gin.Context) (int64, error) {
sensor := c.Param("contactPointId")
return strconv.ParseInt(sensor, 10, 64)
}

View File

@ -4,20 +4,19 @@ import (
"basic-sensor-receiver/app" "basic-sensor-receiver/app"
"database/sql" "database/sql"
"net/http" "net/http"
"strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type postDashboardBody struct { type postDashboardBody struct {
Id int64 `json:"id"` Id int64 `json:"id" binding:"required"`
Name string `json:"name"` Name string `json:"name" binding:"required"`
Contents string `json:"contents"` Contents string `json:"contents" binding:"required"`
} }
type putDashboardBody struct { type putDashboardBody struct {
Name string `json:"name"` Name string `json:"name" binding:"required"`
Contents string `json:"contents"` Contents string `json:"contents" binding:"required"`
} }
func GetDashboards(s *app.Server) gin.HandlerFunc { func GetDashboards(s *app.Server) gin.HandlerFunc {
@ -35,11 +34,7 @@ func GetDashboards(s *app.Server) gin.HandlerFunc {
func GetDashboardById(s *app.Server) gin.HandlerFunc { func GetDashboardById(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
id, err := getIntParam(c, "id") id := getIntParamOrAbort(c, "id")
if err != nil {
c.AbortWithError(400, err)
return
}
item, err := s.Services.Dashboards.GetById(id) item, err := s.Services.Dashboards.GetById(id)
@ -60,11 +55,7 @@ func GetDashboardById(s *app.Server) gin.HandlerFunc {
func PostDashboard(s *app.Server) gin.HandlerFunc { func PostDashboard(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := postDashboardBody{} body := postDashboardBody{}
bindJSONBodyOrAbort(c, &body)
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(400, err)
return
}
item, err := s.Services.Dashboards.Create(body.Id, body.Name, body.Contents) item, err := s.Services.Dashboards.Create(body.Id, body.Name, body.Contents)
@ -79,18 +70,10 @@ func PostDashboard(s *app.Server) gin.HandlerFunc {
func PutDashboard(s *app.Server) gin.HandlerFunc { func PutDashboard(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
id, err := getIntParam(c, "id") id := getIntParamOrAbort(c, "id")
if err != nil {
c.AbortWithError(400, err)
return
}
body := putDashboardBody{} body := putDashboardBody{}
bindJSONBodyOrAbort(c, &body)
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(400, err)
return
}
item, err := s.Services.Dashboards.Update(id, body.Name, body.Contents) item, err := s.Services.Dashboards.Update(id, body.Name, body.Contents)
@ -105,13 +88,9 @@ func PutDashboard(s *app.Server) gin.HandlerFunc {
func DeleteDashboard(s *app.Server) gin.HandlerFunc { func DeleteDashboard(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
id, err := getIntParam(c, "id") id := getIntParamOrAbort(c, "id")
if err != nil {
c.AbortWithError(400, err)
return
}
err = s.Services.Dashboards.Delete(id) err := s.Services.Dashboards.Delete(id)
if err != nil { if err != nil {
c.AbortWithError(500, err) c.AbortWithError(500, err)
@ -121,9 +100,3 @@ func DeleteDashboard(s *app.Server) gin.HandlerFunc {
c.Status(http.StatusOK) c.Status(http.StatusOK)
} }
} }
func getIntParam(c *gin.Context, key string) (int64, error) {
value := c.Param(key)
return strconv.ParseInt(value, 10, 64)
}

View File

@ -8,7 +8,7 @@ import (
) )
type sensorConfigValue struct { type sensorConfigValue struct {
Value string `json:"value"` Value string `json:"value" binding:"required"`
} }
func PutSensorConfig(s *app.Server) gin.HandlerFunc { func PutSensorConfig(s *app.Server) gin.HandlerFunc {
@ -17,10 +17,7 @@ func PutSensorConfig(s *app.Server) gin.HandlerFunc {
sensor := c.Param("sensor") sensor := c.Param("sensor")
key := c.Param("key") key := c.Param("key")
if err := c.BindJSON(&configValue); err != nil { bindJSONBodyOrAbort(c, &configValue)
c.AbortWithError(400, err)
return
}
if err := s.Services.SensorConfig.SetValue(sensor, key, configValue.Value); err != nil { if err := s.Services.SensorConfig.SetValue(sensor, key, configValue.Value); err != nil {
c.AbortWithError(500, err) c.AbortWithError(500, err)

View File

@ -4,39 +4,30 @@ import (
"basic-sensor-receiver/app" "basic-sensor-receiver/app"
"database/sql" "database/sql"
"net/http" "net/http"
"strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type postSensorValueBody struct { type postSensorValueBody struct {
Value float64 `json:"value"` Value float64 `json:"value" binding:"required"`
} }
type getSensorValuesQuery struct { type getSensorValuesQuery struct {
From int64 `form:"from"` From int64 `form:"from" binding:"required"`
To int64 `form:"to"` To int64 `form:"to" binding:"required"`
} }
type getLatestSensorValueQuery struct { type getLatestSensorValueQuery struct {
To int64 `form:"to"` To int64 `form:"to" binding:"required"`
} }
func PostSensorValues(s *app.Server) gin.HandlerFunc { func PostSensorValues(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
var newValue postSensorValueBody var newValue postSensorValueBody
if err := c.BindJSON(&newValue); err != nil { bindJSONBodyOrAbort(c, &newValue)
c.AbortWithError(400, err)
return
}
sensorId, err := getSensorId(c) sensorId := getIntParamOrAbort(c, "sensor")
if err != nil {
c.AbortWithError(400, err)
return
}
if _, err := s.Services.SensorValues.Push(sensorId, newValue.Value); err != nil { if _, err := s.Services.SensorValues.Push(sensorId, newValue.Value); err != nil {
c.AbortWithError(400, err) c.AbortWithError(400, err)
@ -51,15 +42,10 @@ func GetSensorValues(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
var query getSensorValuesQuery var query getSensorValuesQuery
sensorId, err := getSensorId(c) sensorId := getIntParamOrAbort(c, "sensor")
if err != nil { if err := c.ShouldBindQuery(&query); err != nil {
c.AbortWithError(400, err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := c.BindQuery(&query); err != nil {
c.AbortWithError(500, err)
return return
} }
@ -77,15 +63,10 @@ func GetSensorLatestValue(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
var query getLatestSensorValueQuery var query getLatestSensorValueQuery
sensorId, err := getSensorId(c) sensorId := getIntParamOrAbort(c, "sensor")
if err != nil { if err := c.ShouldBindQuery(&query); err != nil {
c.AbortWithError(400, err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := c.BindQuery(&query); err != nil {
c.AbortWithError(500, err)
return return
} }
@ -104,9 +85,3 @@ func GetSensorLatestValue(s *app.Server) gin.HandlerFunc {
c.JSON(http.StatusOK, value) c.JSON(http.StatusOK, value)
} }
} }
func getSensorId(c *gin.Context) (int64, error) {
sensor := c.Param("sensor")
return strconv.ParseInt(sensor, 10, 64)
}

View File

@ -2,17 +2,14 @@ package routes
import ( import (
"basic-sensor-receiver/app" "basic-sensor-receiver/app"
"log"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type postSensorsBody struct { type postOrPutSensorsBody struct {
Name string `json:"name"` Name string `json:"name" binding:"required"`
}
type putSensorsBody struct {
Name string `json:"name"`
} }
func GetSensors(s *app.Server) gin.HandlerFunc { func GetSensors(s *app.Server) gin.HandlerFunc {
@ -20,7 +17,8 @@ func GetSensors(s *app.Server) gin.HandlerFunc {
sensors, err := s.Services.Sensors.GetList() sensors, err := s.Services.Sensors.GetList()
if err != nil { if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) log.Println(err)
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return return
} }
@ -30,12 +28,8 @@ func GetSensors(s *app.Server) gin.HandlerFunc {
func PostSensors(s *app.Server) gin.HandlerFunc { func PostSensors(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := postSensorsBody{} body := postOrPutSensorsBody{}
bindJSONBodyOrAbort(c, &body)
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
sensor, err := s.Services.Sensors.Create(body.Name) sensor, err := s.Services.Sensors.Create(body.Name)
@ -50,19 +44,10 @@ func PostSensors(s *app.Server) gin.HandlerFunc {
func PutSensor(s *app.Server) gin.HandlerFunc { func PutSensor(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := putSensorsBody{} sensorId := getIntParamOrAbort(c, "sensor")
sensorId, err := getSensorId(c) body := postOrPutSensorsBody{}
bindJSONBodyOrAbort(c, &body)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
sensor, err := s.Services.Sensors.Update(sensorId, body.Name) sensor, err := s.Services.Sensors.Update(sensorId, body.Name)
@ -77,19 +62,10 @@ func PutSensor(s *app.Server) gin.HandlerFunc {
func GetSensor(s *app.Server) gin.HandlerFunc { func GetSensor(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
body := putSensorsBody{} sensorId := getIntParamOrAbort(c, "sensor")
sensorId, err := getSensorId(c) body := postOrPutSensorsBody{}
bindJSONBodyOrAbort(c, &body)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
if err := c.BindJSON(&body); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
sensor, err := s.Services.Sensors.GetById(sensorId) sensor, err := s.Services.Sensors.GetById(sensorId)
@ -104,14 +80,9 @@ func GetSensor(s *app.Server) gin.HandlerFunc {
func DeleteSensor(s *app.Server) gin.HandlerFunc { func DeleteSensor(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
sensorId, err := getSensorId(c) sensorId := getIntParamOrAbort(c, "sensor")
if err != nil { err := s.Services.Sensors.DeleteById(sensorId)
c.AbortWithError(http.StatusBadRequest, err)
return
}
err = s.Services.Sensors.DeleteById(sensorId)
if err != nil { if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) c.AbortWithError(http.StatusInternalServerError, err)

24
server/routes/utils.go Normal file
View File

@ -0,0 +1,24 @@
package routes
import (
"strconv"
"github.com/gin-gonic/gin"
)
func getIntParamOrAbort(c *gin.Context, key string) int64 {
value := c.Param(key)
val, err := strconv.ParseInt(value, 10, 64)
if err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": "Invalid " + key})
}
return val
}
func bindJSONBodyOrAbort(c *gin.Context, body interface{}) {
if err := c.ShouldBindJSON(body); err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": err.Error()})
}
}