diff options
| author | Samuel W <samuel.wilhelmsson@gmail.com> | 2024-02-25 15:53:36 +0100 |
|---|---|---|
| committer | Samuel W <samuel.wilhelmsson@gmail.com> | 2024-02-25 15:53:36 +0100 |
| commit | 8488643e5efe4c89ee1abe6f05a8857b8ab122d1 (patch) | |
| tree | 802f3e2512fa430fb2a1ef5dc370c57a229d595b | |
| parent | 9a59ce82149865f059157e8a050b7a3e15ca55b2 (diff) | |
| download | tinygram-8488643e5efe4c89ee1abe6f05a8857b8ab122d1.tar.gz tinygram-8488643e5efe4c89ee1abe6f05a8857b8ab122d1.zip | |
working thing
| -rw-r--r-- | assets/firstpost.png | bin | 40747 -> 0 bytes | |||
| -rw-r--r-- | assets/otherstyle.css | 143 | ||||
| -rw-r--r-- | assets/style.css | 31 | ||||
| -rw-r--r-- | go.mod | 7 | ||||
| -rw-r--r-- | go.sum | 16 | ||||
| -rw-r--r-- | index.templ | 66 | ||||
| -rw-r--r-- | index_templ.go | 143 | ||||
| -rw-r--r-- | main.go | 144 |
8 files changed, 382 insertions, 168 deletions
diff --git a/assets/firstpost.png b/assets/firstpost.png Binary files differdeleted file mode 100644 index 802f95d..0000000 --- a/assets/firstpost.png +++ /dev/null diff --git a/assets/otherstyle.css b/assets/otherstyle.css deleted file mode 100644 index 6e87aae..0000000 --- a/assets/otherstyle.css +++ /dev/null @@ -1,143 +0,0 @@ - -.content { - display: flex; - flex-direction: column; - padding-top: 90px; - padding-left: 32px; - padding-right: 32px; - justify-content: center; - gap: 44px; - word-wrap:break-word; - align-items: center; -} - -.navbar { - padding-left: 32px; - padding-right: 32px; - justify-content: center; - word-wrap:break-word; - align-items: center; -} - -.main { - max-width: 700px; - width: 100%; - display: flex; - flex-direction: column; -} - -.series { - font-size: 12px; - font-weight: 500; - text-decoration: underline -} - -.series { - font-size: 12px; - font-weight: 500; - text-decoration: underline -} - -.side { - display: flex; - flex-direction: column; - align-items: center; -} - -.profilepic { - width: 150pt; - border-radius: 50%; -} - -.flexrow { - display: flex; - flex-direction: row; - align-items: center; -} - - -time { - font-size: 12px -} - -.tag { - background-color: #A0A0A0; - padding: 4px; - margin-right: 2px; - font-size: 12px; -} - -@media (max-width: 500px) { - .tagcontainer{ - display: none; - } -} - -.posttitle { - font-size: 19px; - font-weight:500; - text-decoration: underline; -} - -nav { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - width: 100%; - position: fixed; - height: 90px; - font-size: 30px; - background-color: #C0C0C0; - z-index: 1000; -} - -.socials { - font-size: 15px; - text-decoration: none; - font-weight: 600; - text-decoration: underline; - padding-right: 4px; -} - -pre { - background: #f4f4f4; - border: 1px solid #ddd; - color: #666; - page-break-inside: avoid; - font-family: monospace; - font-size: 15px; - line-height: 1.6; - margin-bottom: 1.6em; - max-width: 100%; - overflow: auto; - padding: 1em 1.5em; - display: block; - word-wrap: break-word; -} - -a,a:visited{ - color: black; - text-decoration: none; -} - -p>a{ - text-decoration: underline; - font-weight: 600; -} - -code{ - font-size: 15px; -} - -body { - margin: 0; - font-family: Arial, Helvetica, sans-serif; - background-color: lightgray -} - - -.title { - font-weight: 600; -} - diff --git a/assets/style.css b/assets/style.css index 1abdc8e..2994f18 100644 --- a/assets/style.css +++ b/assets/style.css @@ -8,6 +8,22 @@ body { justify-content:center; } +.upload { + display: flex; + justify-content:center; + flex-direction: column; +} + +#uploadlink { + text-decoration: none; + color: black; +} + +.imgcontainer { + width: 100%; + padding-top: 100%; + position: relative; +} #container { max-width: 600px; @@ -17,8 +33,19 @@ body { align-items:center; } +#posts { + width: 100%; +} + img{ - width:100%; + object-fit: cover; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + height: 100%; + width: 100%; } .description { @@ -44,7 +71,7 @@ img{ padding-left: 10px; padding-right: 10px; justify-content: center; - gap: 20px; + gap: 5px; word-wrap:break-word; align-items: center; } @@ -4,17 +4,24 @@ go 1.21.6 require ( github.com/a-h/templ v0.2.513 + github.com/gorilla/sessions v1.2.2 + github.com/labstack/echo-contrib v0.15.0 github.com/labstack/echo/v4 v4.11.4 gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55 ) require ( + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/gorilla/context v1.1.1 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect + golang.org/x/time v0.5.0 // indirect ) require ( + github.com/google/uuid v1.6.0 github.com/labstack/gommon v0.4.2 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -2,12 +2,26 @@ github.com/a-h/templ v0.2.513 h1:ZmwGAOx4NYllnHy+FTpusc4+c5msoMpPIYX0Oy3dNqw= github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM= 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/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.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/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= +github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU= +github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4= github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= @@ -37,6 +51,8 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0= diff --git a/index.templ b/index.templ index 5da5272..b0faf2d 100644 --- a/index.templ +++ b/index.templ @@ -18,7 +18,7 @@ templ index(ps []Post) { <body> <div id="container"> <div id="header"> - <h2>Tinygram</h2> + <a id="uploadlink" href="/upload"><h2>Tinygram</h2></a> </div> <div id="posts"> @posts(ps) @@ -28,10 +28,72 @@ templ index(ps []Post) { </html> } +templ loginPage(csrfToken string) { + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="X-UA-Compatible" content="IE=edge"/> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/> + <script src="/static/htmx.min.js"></script> + <link rel="preload" href="/static/style.css" as="style"/> + <link rel="stylesheet" href="/static/style.css"/> + </head> + <body> + <form + class="login" + hx-encoding="multipart/form-data" + hx-post="/login" + > + <input type="password" name="password"/> + <input type="hidden" name="_csrf" value={ csrfToken }/> + <button> + Login + </button> + </form> + </body> + </html> +} + +templ uploadPage(csrfToken string) { + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8"/> + <meta http-equiv="X-UA-Compatible" content="IE=edge"/> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/> + <script src="/static/htmx.min.js"></script> + <link rel="preload" href="/static/style.css" as="style"/> + <link rel="stylesheet" href="/static/style.css"/> + </head> + <body> + <form + class="upload" + hx-encoding="multipart/form-data" + hx-post="/upload" + _="on htmx:xhr:progress(loaded, total) set #progress.value to (loaded/total)*100" + > + <input type="file" name="file" accept="image/png, image/jpeg"/> + <input type="hidden" name="_csrf" value={ csrfToken }/> + <div> + <span>Description:</span> + <input type="text" name="description"/> + </div> + <button> + Upload + </button> + <progress id="progress" value="0" max="100"></progress> + </form> + </body> + </html> +} + templ posts(posts []Post) { for _, post := range posts { <div class="post"> - <img src="/static/firstpost.png" alt={ post.ImageID }/> + <div class="imgcontainer"> + <img src={ post.ImageID } alt={ post.ImageID }/> + </div> <div class="summary"> <p class="description">{ post.Description }</p> <p class="date">{ post.CreatedAt.Format("2006-01-02 - 15:04") }</p> diff --git a/index_templ.go b/index_templ.go index d03d535..748e10d 100644 --- a/index_templ.go +++ b/index_templ.go @@ -36,7 +36,7 @@ func index(ps []Post) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</script><link rel=\"preload\" href=\"/static/style.css\" as=\"style\"><link rel=\"stylesheet\" href=\"/static/style.css\"></head><body><div id=\"container\"><div id=\"header\"><h2>") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</script><link rel=\"preload\" href=\"/static/style.css\" as=\"style\"><link rel=\"stylesheet\" href=\"/static/style.css\"></head><body><div id=\"container\"><div id=\"header\"><a id=\"uploadlink\" href=\"/upload\"><h2>") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -45,7 +45,7 @@ func index(ps []Post) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h2></div><div id=\"posts\">") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h2></a></div><div id=\"posts\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -64,7 +64,7 @@ func index(ps []Post) templ.Component { }) } -func posts(posts []Post) templ.Component { +func loginPage(csrfToken string) templ.Component { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) if !templ_7745c5c3_IsBuffer { @@ -77,8 +77,125 @@ func posts(posts []Post) templ.Component { templ_7745c5c3_Var4 = templ.NopComponent } ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html><head><meta charset=\"utf-8\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"><script src=\"/static/htmx.min.js\">") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var5 := `` + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</script><link rel=\"preload\" href=\"/static/style.css\" as=\"style\"><link rel=\"stylesheet\" href=\"/static/style.css\"></head><body><form class=\"login\" hx-encoding=\"multipart/form-data\" hx-post=\"/login\"><input type=\"password\" name=\"password\"> <input type=\"hidden\" name=\"_csrf\" value=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(csrfToken)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> <button>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var6 := `Login` + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button></form></body></html>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if !templ_7745c5c3_IsBuffer { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) + } + return templ_7745c5c3_Err + }) +} + +func uploadPage(csrfToken string) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) + if !templ_7745c5c3_IsBuffer { + templ_7745c5c3_Buffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var7 := templ.GetChildren(ctx) + if templ_7745c5c3_Var7 == nil { + templ_7745c5c3_Var7 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html><head><meta charset=\"utf-8\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"><script src=\"/static/htmx.min.js\">") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var8 := `` + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</script><link rel=\"preload\" href=\"/static/style.css\" as=\"style\"><link rel=\"stylesheet\" href=\"/static/style.css\"></head><body><form class=\"upload\" hx-encoding=\"multipart/form-data\" hx-post=\"/upload\" _=\"on htmx:xhr:progress(loaded, total) set #progress.value to (loaded/total)*100\"><input type=\"file\" name=\"file\" accept=\"image/png, image/jpeg\"> <input type=\"hidden\" name=\"_csrf\" value=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(csrfToken)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><div><span>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var9 := `Description:` + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span> <input type=\"text\" name=\"description\"></div><button>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var10 := `Upload` + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button> <progress id=\"progress\" value=\"0\" max=\"100\"></progress></form></body></html>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if !templ_7745c5c3_IsBuffer { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) + } + return templ_7745c5c3_Err + }) +} + +func posts(posts []Post) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) + if !templ_7745c5c3_IsBuffer { + templ_7745c5c3_Buffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var11 := templ.GetChildren(ctx) + if templ_7745c5c3_Var11 == nil { + templ_7745c5c3_Var11 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) for _, post := range posts { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"post\"><img src=\"/static/firstpost.png\" alt=\"") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"post\"><div class=\"imgcontainer\"><img src=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(post.ImageID)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" alt=\"") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -86,16 +203,16 @@ func posts(posts []Post) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><div class=\"summary\"><p class=\"description\">") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></div><div class=\"summary\"><p class=\"description\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var5 string - templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(post.Description) + var templ_7745c5c3_Var12 string + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(post.Description) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 35, Col: 45} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 97, Col: 45} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -103,12 +220,12 @@ func posts(posts []Post) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var6 string - templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(post.CreatedAt.Format("2006-01-02 - 15:04")) + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(post.CreatedAt.Format("2006-01-02 - 15:04")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 36, Col: 65} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 98, Col: 65} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1,10 +1,19 @@ package main import ( + "bufio" "fmt" + "io" + "net/http" + "os" + "path" "time" + "github.com/google/uuid" + "github.com/gorilla/sessions" + "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" "gorm.io/driver/sqlite" "gorm.io/gorm" ) @@ -16,21 +25,47 @@ type Post struct { } func main() { - e := echo.New() - db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{}) + dbPath := os.Getenv("DB_PATH") + if dbPath == "" { + dbPath = "tinygram.db" + } - post := Post{ - Description: "Test image :>", - ImageID: "/static/firstpost.png", + sessionSecret := os.Getenv("SESSION_SECRET") + if sessionSecret == "" { + fmt.Println("NEED TO PROVIDE A SECRET") + return } - db.AutoMigrate(Post{}) - db.Create(&post) + passwordFilePath := os.Getenv("PASSWORD_FILE_PATH") + if passwordFilePath == "" { + passwordFilePath = "password.txt" + } + + assetsPath := os.Getenv("ASSETS_PATH") + if assetsPath == "" { + assetsPath = "assets" + } + + e := echo.New() + // e.Use(middleware.Logger()) + e.Use(middleware.Recover()) + e.Use(middleware.Secure()) + + e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{ + TokenLookup: "form:_csrf", + })) + + e.Use(session.Middleware(sessions.NewCookieStore([]byte(sessionSecret)))) + + db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) + + db.AutoMigrate(Post{}) if err != nil { - fmt.Errorf("opening db: %w", err) + fmt.Printf("opening db: %v", err) } + e.Static("/static", "assets") e.GET("/", func(c echo.Context) error { @@ -44,6 +79,99 @@ func main() { return nil }) + e.GET("/login", func(c echo.Context) error { + component := loginPage(c.Get(middleware.DefaultCSRFConfig.ContextKey).(string)) + err := component.Render(c.Request().Context(), c.Response().Writer) + if err != nil { + return err + } + return nil + }) + + e.POST("/login", func(c echo.Context) error { + // read password file, check content, add session if correct + file, err := os.Open(passwordFilePath) + if err != nil { + c.Response().Header().Set("HX-Redirect", "/") + return c.NoContent(http.StatusUnauthorized) + } + + // check provided password against file + s := bufio.NewScanner(file) + s.Scan() + expected := s.Text() + formPass := c.FormValue("password") + + if expected != formPass { + c.Response().Header().Set("HX-Redirect", "/") + return c.NoContent(http.StatusUnauthorized) + } + + sess, _ := session.Get("session", c) + sess.Options = &sessions.Options{ + Path: "/", + MaxAge: 86400 * 7, + HttpOnly: true, + } + sess.Values["user"] = "admin" + sess.Save(c.Request(), c.Response()) + + c.Response().Header().Set("HX-Redirect", "/upload") + return c.NoContent(http.StatusOK) + }) + + e.GET("/upload", func(c echo.Context) error { + sess, _ := session.Get("session", c) + if sess.Values["user"] != "admin" { + return c.Redirect(http.StatusSeeOther, "/login") + } + component := uploadPage(c.Get(middleware.DefaultCSRFConfig.ContextKey).(string)) + err := component.Render(c.Request().Context(), c.Response().Writer) + if err != nil { + return err + } + return nil + }) + + e.POST("/upload", func(c echo.Context) error { + file, err := c.FormFile("file") + if err != nil { + return err + } + src, err := file.Open() + if err != nil { + return err + } + defer src.Close() + + filename, err := uuid.NewRandom() + if err != nil { + return err + } + + dst, err := os.Create(path.Join("assets", filename.String())) //RANDOMIZE + if err != nil { + return err + } + defer dst.Close() + + // Copy + if _, err = io.Copy(dst, src); err != nil { + return err + } + + description := c.FormValue("description") + + post := Post{ + Description: description, + ImageID: "/static/" + filename.String(), + } + db.Create(&post) + + c.Response().Header().Set("HX-Redirect", "/") + return c.NoContent(http.StatusOK) + }) + e.GET("/posts", func(c echo.Context) error { after, err := time.Parse(time.RFC3339, c.QueryParam("after")) |
