containerized the base application and connected it to the database. Example env file and build script included. To run, copy the env file to .env and enter a password.

This commit is contained in:
spinach 2025-02-19 04:22:30 -05:00
parent 27e964549f
commit f0d486fd6c
15 changed files with 252 additions and 20 deletions

1
.gitignore vendored
View File

@ -21,3 +21,4 @@
# Go workspace file
go.work
.env

34
Dockerfile Normal file
View File

@ -0,0 +1,34 @@
FROM golang:1.22-alpine AS build
# Set destination for COPY of gui files
WORKDIR /src
# Download Go modules
COPY go.mod go.sum ./
RUN go mod download
# Copy the source code. Note the slash at the end, as explained in
# https://docs.docker.com/reference/dockerfile/#copy
# COPY *.go ./
# RUN CGO_ENABLED=0 GOOS=linux go build -o /adonis
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o adonis .
FROM alpine
WORKDIR /app
COPY --from=build /src/adonis .
COPY --from=build /src/static ./static
# Build
# Optional:
# To bind to a TCP port, runtime parameters must be supplied to the docker command.
# But we can document in the Dockerfile what ports
# the application is going to listen on by default.
# https://docs.docker.com/reference/dockerfile/#expose
EXPOSE 80
# Run
ENTRYPOINT ["/app/adonis"]

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 Keegan Deppe
Copyright (c) 2025 Keegan Deppe
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

4
build.sh Executable file
View File

@ -0,0 +1,4 @@
#! /usr/bin/env bash
# ensures correct tag is applied
docker build . -t adonis

View File

@ -1,13 +0,0 @@
package main
import (
"fmt"
"git.keegandeppe.com/kdeppe/adonis/internal/crypto"
"os"
)
func main() {
fmt.Println("adonis")
crypto.test()
os.Exit(0)
}

27
docker-compose.yml Normal file
View File

@ -0,0 +1,27 @@
services:
postgres:
image: postgres:latest
environment:
# REQUIRED
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_DB=${POSTGRES_DB}
volumes:
- db_data:/var/lib/postgresql/data
networks:
- db
adonis:
image: adonis:latest
ports:
- "42069:80"
env_file: ".env"
depends_on:
- postgres
networks:
- db
networks:
db:
volumes:
db_data:

6
env Normal file
View File

@ -0,0 +1,6 @@
GO_VERSION="1.22.1"
POSTGRES_USER="adonis"
POSTGRES_PASSWORD=""
POSTGRES_DB="adonis"
POSTGRES_HOSTNAME="postgres"

17
go.mod
View File

@ -1,3 +1,16 @@
module git.keegandeppe.com/kdeppe/adonis-backend
module git.keegandeppe.com/kdeppe/adonis
go 1.22.1
go 1.22
require (
github.com/jackc/pgx/v5 v5.7.2
golang.org/x/crypto v0.31.0
)
require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
)

28
go.sum Normal file
View File

@ -0,0 +1,28 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,11 +1,23 @@
package crypto
import "crypto/bcrypt"
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
func Test() {
hash("test")
hash("test")
hash("test1")
}
func hash(pwd string) {
hash, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
func test() {
hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost)
if err != nil {
panic(err)
}
fmt.Println(string(hash))
fmt.Printf("Password hash: %x\n", hash)
}

44
internal/db/db.go Normal file
View File

@ -0,0 +1,44 @@
// package db serves as a database wrapper for adonis
package db
import (
"context"
"fmt"
"os"
"github.com/jackc/pgx/v5/pgxpool"
)
type Database struct {
conn *pgxpool.Pool
}
func NewDatabase(username, password, hostname, database string) *Database {
// pool ensures thread safety
url := url(username, password, hostname, database)
dbpool, err := pgxpool.New(context.Background(), url)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to create connection pool: %v\n", err)
os.Exit(1)
}
var greeting string
err = dbpool.QueryRow(context.Background(), "select 'Hello, world!'").Scan(&greeting)
if err != nil {
fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
os.Exit(1)
}
fmt.Println(greeting)
return &Database{conn: dbpool}
}
// this function assumes default port because i cant be bothered
func url(username, password, hostname, database string) string {
// generate url based on passed params
// safe to disable SSL as comms occur over docker network
return fmt.Sprintf("postgresql://%s:%s@%s/%s?sslmode=disable",
username, password, hostname, database)
}

56
main.go Normal file
View File

@ -0,0 +1,56 @@
package main
import (
"html/template"
"log"
"net/http"
"os"
db "git.keegandeppe.com/kdeppe/adonis/internal/db"
)
type Todo struct {
Title string
Done bool
}
type TodoPageData struct {
PageTitle string
Todos []Todo
}
func main() {
base := template.Must(template.ParseFiles("static/index.html"))
tmpl := template.Must(template.ParseFiles("static/layout.html"))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
base.Execute(w, nil)
})
http.HandleFunc("/htmx.min.js", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "static/js/htmx.min.js")
})
http.HandleFunc("/gettasks", func(w http.ResponseWriter, r *http.Request) {
data := TodoPageData{
PageTitle: "My TODO list",
Todos: []Todo{
{Title: "test", Done: false},
},
}
tmpl.Execute(w, data)
})
// setup database connection based on env vars
username := os.Getenv("POSTGRES_USER")
password := os.Getenv("POSTGRES_PASSWORD")
database := os.Getenv("POSTGRES_DB")
hostname := os.Getenv("POSTGRES_HOSTNAME")
db.NewDatabase(username, password, hostname, database)
// attach webserver
log.Fatal(http.ListenAndServe(":80", nil))
// crypto.Test()
// os.Exit(0)
}

4
static/index.html Normal file
View File

@ -0,0 +1,4 @@
<script src="htmx.min.js"></script>
<button hx-get="/gettasks" hx-target="#todo">Get Todos</button>
<div id="todo"></div>

1
static/js/htmx.min.js vendored Normal file

File diff suppressed because one or more lines are too long

15
static/layout.html Normal file
View File

@ -0,0 +1,15 @@
<h1>{{.PageTitle}}</h1>
<ul>
{{range .Todos}}
{{if .Done}}
<li class="done">{{.Title}}</li>
{{else}}
<li class="notdone">{{.Title}}</li>
{{end}}
{{end}}
</ul>
<style>
.done {color: green}
.notdone {color: red}
</style>