basic navigation and basic forms
|
@ -5,5 +5,6 @@
|
|||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="bootstrap" level="application" />
|
||||
</component>
|
||||
</module>
|
12
Lab01/app/.idea/dataSources.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="sqlite" uuid="90df965d-ff21-4af2-b1e2-02d2ad7754f1">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:C:\Users\FlorianHoss\Documents\GitHub\SWB6-ITSec\Lab01\app\sqlite.db</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
6
Lab01/app/.idea/jsLibraryMappings.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="PROJECT" libraries="{bootstrap}" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,26 +0,0 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"app/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (api *Api) defineRoutes() {
|
||||
api.Router.GET("/ping", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{
|
||||
"message": "pong",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (api *Api) initialize() {
|
||||
api.Database = database.Database{Location: "sqlite.db"}
|
||||
api.Database.Initialize()
|
||||
}
|
||||
|
||||
func (api *Api) Run() {
|
||||
api.initialize()
|
||||
api.Router = gin.Default()
|
||||
api.defineRoutes()
|
||||
api.Router.Run()
|
||||
}
|
|
@ -8,6 +8,7 @@ type Database struct {
|
|||
}
|
||||
|
||||
type User struct {
|
||||
ID int
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ module app
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/static v0.0.1
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
gorm.io/driver/sqlite v1.3.1
|
||||
gorm.io/gorm v1.23.3
|
||||
|
|
|
@ -3,6 +3,9 @@ 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/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-contrib/static v0.0.1 h1:JVxuvHPuUfkoul12N7dtQw7KRn/pSMq7Ue1Va9Swm1U=
|
||||
github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
|
@ -11,6 +14,7 @@ github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8c
|
|||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package main
|
||||
|
||||
import "app/api"
|
||||
import "app/webpage"
|
||||
|
||||
func main() {
|
||||
backend := api.Api{}
|
||||
backend := webpage.Webpage{}
|
||||
backend.Run()
|
||||
}
|
||||
|
|
BIN
Lab01/app/static/icons/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
Lab01/app/static/icons/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
Lab01/app/static/icons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
9
Lab01/app/static/icons/browserconfig.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
BIN
Lab01/app/static/icons/favicon-16x16.png
Normal file
After Width: | Height: | Size: 616 B |
BIN
Lab01/app/static/icons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 909 B |
BIN
Lab01/app/static/icons/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
Lab01/app/static/icons/mstile-144x144.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Lab01/app/static/icons/mstile-150x150.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
Lab01/app/static/icons/mstile-310x150.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
Lab01/app/static/icons/mstile-310x310.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
Lab01/app/static/icons/mstile-70x70.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
35
Lab01/app/static/icons/safari-pinned-tab.svg
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M3397 6124 c-1 -1 -42 -5 -91 -8 -49 -4 -91 -8 -95 -10 -3 -2 -28 -6
|
||||
-56 -10 -136 -16 -361 -80 -505 -143 -340 -147 -645 -380 -862 -657 -26 -33
|
||||
-51 -65 -56 -71 -20 -26 -103 -156 -132 -207 -33 -59 -44 -71 -70 -75 -8 -1
|
||||
-35 -5 -60 -8 -25 -4 -72 -13 -105 -22 -33 -8 -70 -17 -82 -19 -38 -7 -195
|
||||
-65 -269 -99 -318 -146 -601 -404 -780 -713 -80 -138 -162 -346 -188 -476 -3
|
||||
-11 -10 -46 -16 -76 -26 -128 -34 -391 -17 -540 57 -491 356 -968 780 -1245
|
||||
193 -126 460 -232 652 -260 23 -3 52 -7 65 -10 14 -3 56 -7 95 -11 93 -8 2464
|
||||
-8 2472 0 4 3 7 97 8 209 2 153 6 218 19 267 46 177 108 291 221 411 l48 51 3
|
||||
92 c4 127 16 223 41 316 128 487 506 846 1005 956 84 19 366 28 443 14 125
|
||||
-21 181 -35 266 -66 421 -151 743 -517 838 -952 10 -48 20 -65 20 -36 0 11 2
|
||||
82 4 159 6 169 -3 249 -43 407 -82 319 -305 633 -584 823 -184 125 -448 225
|
||||
-636 242 -36 3 -70 10 -77 15 -6 5 -17 32 -23 60 -63 295 -225 628 -428 879
|
||||
-333 412 -850 713 -1347 785 -16 2 -39 6 -51 8 -56 11 -398 28 -407 20z"/>
|
||||
<path d="M5595 3205 c-5 -2 -43 -11 -83 -20 -92 -21 -149 -46 -233 -101 -130
|
||||
-86 -237 -227 -283 -374 -34 -105 -38 -151 -38 -425 l-1 -243 -39 -6 c-91 -15
|
||||
-182 -89 -225 -183 l-26 -57 -1 -605 c-1 -423 2 -622 10 -659 17 -80 78 -162
|
||||
147 -199 79 -42 69 -42 881 -40 l781 2 45 24 c66 35 134 110 158 172 20 52 21
|
||||
75 21 666 1 453 -2 624 -11 657 -25 95 -120 188 -219 216 l-62 17 -2 269 c-2
|
||||
148 -6 280 -9 294 -3 14 -9 38 -12 54 -7 41 -56 154 -90 207 -105 168 -263
|
||||
276 -475 325 -26 6 -217 13 -234 9z m220 -310 c28 -8 77 -33 109 -55 67 -47
|
||||
70 -50 111 -105 81 -109 88 -140 89 -441 l2 -251 -438 0 -439 0 1 248 c1 136
|
||||
6 265 11 286 43 160 193 302 349 332 57 10 141 5 205 -14z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
19
Lab01/app/static/icons/site.webmanifest
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
49
Lab01/app/templates/index.tmpl
Normal file
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ .title }}</title>
|
||||
{{template "head" .}}
|
||||
</head>
|
||||
<body>
|
||||
{{template "navbar" .}}
|
||||
|
||||
<div class="position-absolute top-50 start-50 translate-middle">
|
||||
<div>Welcome</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{{define "head"}}
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../static/icons/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="../static/icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="../static/icons/favicon-16x16.png">
|
||||
<link rel="manifest" href="../static/icons/site.webmanifest">
|
||||
<link rel="mask-icon" href="../static/icons/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||
{{end}}
|
||||
|
||||
{{define "navbar"}}
|
||||
<nav class="navbar navbar-expand-md navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">SuperSave</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="ms-auto navbar-nav mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-primary" href="/login">Login</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-secondary" href="/register">Register</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{{end}}
|
29
Lab01/app/templates/login.tmpl
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ .title }}</title>
|
||||
{{template "head" .}}
|
||||
</head>
|
||||
<body>
|
||||
{{template "navbar" .}}
|
||||
|
||||
<div class="position-absolute top-50 start-50 translate-middle">
|
||||
<form action="http://localhost:8080/register" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" aria-describedby="emailHelp">
|
||||
<div id="emailHelp" class="form-text">The username needs to be unique</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" aria-describedby="passwordHelp">
|
||||
<div id="passwordHelp" class="form-text">The password needs to be different from the username</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
45
Lab01/app/templates/register.tmpl
Normal file
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ .title }}</title>
|
||||
{{template "head" .}}
|
||||
</head>
|
||||
<body>
|
||||
{{template "navbar" .}}
|
||||
|
||||
<div class="position-absolute top-50 start-50 translate-middle">
|
||||
<form action="http://localhost:8080/register" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" aria-describedby="emailHelp">
|
||||
<div id="emailHelp" class="form-text">The username needs to be unique</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" aria-describedby="passwordHelp">
|
||||
<div id="passwordHelp" class="form-text">The password needs to be different from the username</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
let formData = new FormData();
|
||||
formData.append("username", "Florian");
|
||||
formData.append("password", "SuperSafe");
|
||||
|
||||
let requestOptions = {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
redirect: 'follow'
|
||||
};
|
||||
|
||||
fetch("http://localhost:8080/register", requestOptions)
|
||||
.then(response => response.text())
|
||||
.then(result => console.log(result))
|
||||
.catch(error => console.log('error', error));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,11 +1,11 @@
|
|||
package api
|
||||
package webpage
|
||||
|
||||
import (
|
||||
"app/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Api struct {
|
||||
type Webpage struct {
|
||||
Database database.Database
|
||||
Router *gin.Engine
|
||||
}
|
69
Lab01/app/webpage/webpage.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package webpage
|
||||
|
||||
import (
|
||||
"app/database"
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (wp *Webpage) defineRoutes() {
|
||||
|
||||
wp.Router.GET("/", func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.tmpl", gin.H{
|
||||
"title": "Register",
|
||||
})
|
||||
})
|
||||
wp.Router.GET("/login", func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "login.tmpl", gin.H{
|
||||
"title": "Login",
|
||||
})
|
||||
})
|
||||
wp.Router.GET("/register", func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "register.tmpl", gin.H{
|
||||
"title": "Register",
|
||||
})
|
||||
})
|
||||
wp.Router.GET("/health", func(c *gin.Context) {
|
||||
currentTime := time.Now().UnixMilli()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"timestamp": currentTime,
|
||||
})
|
||||
})
|
||||
wp.Router.NoRoute(func(c *gin.Context) {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/")
|
||||
})
|
||||
|
||||
wp.Router.POST("/register", func(c *gin.Context) {
|
||||
username, err := c.GetPostForm("username")
|
||||
password, err := c.GetPostForm("password")
|
||||
if err == false {
|
||||
c.JSON(400, gin.H{"message": "bad post form"})
|
||||
return
|
||||
}
|
||||
user := database.User{Username: username, Password: password}
|
||||
result := wp.Database.ORM.Create(&user)
|
||||
if result.Error != nil {
|
||||
c.JSON(200, gin.H{"message": "cannot create user"})
|
||||
}
|
||||
c.JSON(200, gin.H{"message": "user registered"})
|
||||
})
|
||||
}
|
||||
|
||||
func (wp *Webpage) initialize() {
|
||||
wp.Database = database.Database{Location: "sqlite.db"}
|
||||
wp.Database.Initialize()
|
||||
}
|
||||
|
||||
func (wp *Webpage) Run() {
|
||||
wp.initialize()
|
||||
wp.Router = gin.New()
|
||||
wp.Router.Use(gin.Recovery())
|
||||
wp.Router.Use(gin.Logger())
|
||||
wp.Router.SetTrustedProxies(nil)
|
||||
wp.Router.Use(static.Serve("/static", static.LocalFile("./static", false)))
|
||||
wp.Router.LoadHTMLGlob("templates/*")
|
||||
wp.defineRoutes()
|
||||
wp.Router.Run()
|
||||
}
|