structure appears to work
This commit is contained in:
35
.drone.sh
Normal file
35
.drone.sh
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
# disable CGO for cross-compiling
|
||||
export CGO_ENABLED=0
|
||||
|
||||
commit=$(git rev-parse HEAD)
|
||||
#tag=$(git describe --tags --abbrev=0)
|
||||
buildtime=$(TZ=Australia/Sydney date +%Y-%m-%dT%T%z)
|
||||
git_version=$(git describe --tags --always --long --dirty)
|
||||
package_name=vctp
|
||||
|
||||
platforms=("linux/amd64" "darwin/amd64")
|
||||
|
||||
echo Building $package_name with git version: $git_version
|
||||
for platform in "${platforms[@]}"
|
||||
do
|
||||
platform_split=(${platform//\// })
|
||||
GOOS=${platform_split[0]}
|
||||
GOARCH=${platform_split[1]}
|
||||
output_name=$package_name'-'$GOOS'-'$GOARCH
|
||||
if [ $GOOS = "windows" ]; then
|
||||
output_name+='.exe'
|
||||
fi
|
||||
|
||||
echo "build commences"
|
||||
env GOOS=$GOOS GOARCH=$GOARCH go build -trimpath -ldflags="-X main.sha1ver=$commit -X main.buildTime=$buildtime" -o build/$output_name $package
|
||||
if [ $? -ne 0 ]; then
|
||||
echo 'An error has occurred! Aborting the script execution...'
|
||||
exit 1
|
||||
fi
|
||||
echo "build complete at $buildtime : $output_name"
|
||||
sha256sum build/$output_name > build/${output_name}_checksum.txt
|
||||
done
|
||||
|
||||
ls -lah build
|
40
.drone.yml
Normal file
40
.drone.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: golang
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
GOMODCACHE: '/drone/src/pkg.mod'
|
||||
GOCACHE: '/drone/src/pkg.build'
|
||||
volumes:
|
||||
- name: shared
|
||||
path: /shared
|
||||
commands:
|
||||
#- cp /shared/index.html ./www/
|
||||
- sh ./.drone.sh
|
||||
|
||||
- name: dell-sftp-deploy
|
||||
image: hypervtechnics/drone-sftp
|
||||
settings:
|
||||
host: deft.dell.com
|
||||
username:
|
||||
from_secret: DELLFTP_USER
|
||||
password:
|
||||
from_secret: DELLFTP_PASS
|
||||
port: 22
|
||||
source: ./build
|
||||
filter: vctp*
|
||||
clean: false
|
||||
target: /
|
||||
overwrite: true
|
||||
verbose: true
|
||||
|
||||
volumes:
|
||||
- name: shared
|
||||
temp: {}
|
||||
#- name: cache
|
||||
# host:
|
||||
# path: /var/lib/cache
|
92
.github/workflows/ci.yml
vendored
Normal file
92
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- run: go mod download
|
||||
- run: go install github.com/a-h/templ/cmd/templ@v0.2.771
|
||||
- run: make generate-templ
|
||||
- uses: sqlc-dev/setup-sqlc@v4
|
||||
with:
|
||||
sqlc-version: '1.27.0'
|
||||
- run: sqlc vet
|
||||
- run: sqlc generate
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.54
|
||||
skip-pkg-cache: true
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- run: go mod download
|
||||
- run: go install github.com/a-h/templ/cmd/templ@v0.2.771
|
||||
- run: make generate-templ
|
||||
- uses: sqlc-dev/setup-sqlc@v4
|
||||
with:
|
||||
sqlc-version: '1.27.0'
|
||||
- run: sqlc generate
|
||||
- name: Test
|
||||
run: go test -race ./...
|
||||
e2e:
|
||||
name: End-to-End
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- run: go mod download
|
||||
- run: go install github.com/a-h/templ/cmd/templ@v0.2.771
|
||||
- run: templ generate -path ./components
|
||||
- uses: sqlc-dev/setup-sqlc@v4
|
||||
with:
|
||||
sqlc-version: '1.27.0'
|
||||
- run: sqlc generate
|
||||
- run: go test ./... -tags=e2e
|
||||
docker-publish:
|
||||
name: Publish Docker
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
needs:
|
||||
- lint
|
||||
- test
|
||||
- e2e
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
images: ghcr.io/piszmog/vctp
|
||||
- uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
77
.github/workflows/release.yml
vendored
Normal file
77
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: The version to release (e.g. v1.0.0)
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- run: go mod download
|
||||
- run: go install github.com/a-h/templ/cmd/templ@v0.2.771
|
||||
- name: Generate Templ Files
|
||||
run: make generate-templ
|
||||
- name: Generate CSS
|
||||
run: |
|
||||
curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64
|
||||
chmod +x tailwindcss-linux-x64
|
||||
mv tailwindcss-linux-x64 tailwindcss
|
||||
./tailwindcss -i ./styles/input.css -o ./dist/assets/css/output@${{ github.event.inputs.version }}.css --minify
|
||||
- uses: sqlc-dev/setup-sqlc@v4
|
||||
with:
|
||||
sqlc-version: '1.27.0'
|
||||
- run: sqlc generate
|
||||
- name: Build Application
|
||||
run: go build -o ./app -ldflags="-s -w -X version.Value=${{ github.event.inputs.version }}"
|
||||
- name: Create Tag
|
||||
uses: piszmog/create-tag@v1
|
||||
with:
|
||||
version: ${{ github.event.inputs.version }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
name: ${{ github.event.inputs.version }}
|
||||
tag_name: ${{ github.event.inputs.version }}
|
||||
generate_release_notes: true
|
||||
files: app
|
||||
publish:
|
||||
name: Publish Docker
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
needs:
|
||||
- release
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
images: ghcr.io/piszmog/my-app
|
||||
tags: |
|
||||
type=raw,value=${{ github.event.inputs.version }}
|
||||
- uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
VERSION=$${{ github.event.inputs.version }}
|
71
.gitignore
vendored
Normal file
71
.gitignore
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
### Go template
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
vctp
|
||||
build/
|
||||
|
||||
# Certificates
|
||||
*.pem
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
### AppEngine template
|
||||
# Google App Engine generated folder
|
||||
appengine-generated/
|
||||
|
||||
/components/*/*.go
|
||||
/components/*/*.txt
|
||||
.idea
|
||||
*.iml
|
||||
dist/assets/css/
|
||||
*.sqlite3
|
||||
tmp/
|
||||
pb_data/
|
||||
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
/db/queries/*.go
|
9
20240912012703_init.sql
Normal file
9
20240912012703_init.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
SELECT 'up SQL query';
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
SELECT 'down SQL query';
|
||||
-- +goose StatementEnd
|
2
Makefile
2
Makefile
@@ -18,4 +18,4 @@ run:
|
||||
@go run main.go
|
||||
build:
|
||||
@echo "Building..."
|
||||
@go build -o ./app -ldflags="-s -w -X version.Value=1.0.0"
|
||||
@go build -o ./build/vctp -ldflags="-s -w -X version.Value=1.0.0"
|
||||
|
54
README.md
54
README.md
@@ -1,17 +1,17 @@
|
||||
# Go + HTMX Template
|
||||
|
||||
This is a template repository that comes with everything you need to build a Web Application using Go (templ) and HTMX.
|
||||
This is built from the template https://github.com/Piszmog/go-htmx-template that comes with everything you need to build a Web Application using Go (templ) and HTMX.
|
||||
|
||||
The template comes with a basic structure of using a SQL DB (`sqlc`), E2E testing (playwright), and styling (tailwindcss).
|
||||
|
||||
## Getting Started
|
||||
|
||||
In the top right, select the dropdown __Use this template__ and select __Create a new repository__.
|
||||
Clone https://github.com/Piszmog/go-htmx-template
|
||||
|
||||
Once cloned, run the `update_module.sh` script to change the module to your module name.
|
||||
|
||||
```shell
|
||||
./update_module vctpule
|
||||
./update_module my-new-module
|
||||
```
|
||||
|
||||
## Technologies
|
||||
@@ -28,8 +28,8 @@ A few different technologies are configured to help getting off the ground easie
|
||||
- The script `upgrade_templ.sh` is available to make upgrading easier
|
||||
- [HTMX](https://htmx.org/) for HTML interaction
|
||||
- The script `upgrade_htmx.sh` is available to make upgrading easier
|
||||
- [air](https://github.com/cosmtrek/air) for live reloading of the application.
|
||||
- [golang migrate](https://github.com/golang-migrate/migrate) for DB migrations.
|
||||
|
||||
- [golang migrate](https://github.com/golang-migrate/migrate) for DB migrations, TODO replace with [goose](https://github.com/pressly/goose)
|
||||
- [playwright-go](https://github.com/playwright-community/playwright-go) for E2E testing.
|
||||
|
||||
Everything else uses the standard library.
|
||||
@@ -97,7 +97,17 @@ This is where `templ` files live. Anything you want to render to the user goes h
|
||||
This is the directory that `sqlc` generates to. Update `queries.sql` to build
|
||||
your database operations.
|
||||
|
||||
This project uses [golang migrate](https://github.com/golang-migrate/migrate) for DB
|
||||
#### DB Migrations
|
||||
This project now uses [goose](https://github.com/pressly/goose) for DB migrations.
|
||||
|
||||
Install via `brew install goose` on a mac, or install via golang with command `go install github.com/pressly/goose/v3/cmd/goose@latest`
|
||||
|
||||
```shell
|
||||
goose -dir db/migrations sqlite3 ./db.sqlite3 create init sql
|
||||
```
|
||||
|
||||
#### Deprecated
|
||||
This project no longer uses [golang migrate](https://github.com/golang-migrate/migrate) for DB
|
||||
migrations. `sqlc` uses the `db/migrations` directory to generating DB tables. Call
|
||||
`db.Migrate(..)` to automatically migrate your database to the latest version. To add migration
|
||||
call the following command,
|
||||
@@ -108,6 +118,7 @@ migrate create -ext sql -dir db/migrations <name of migration>
|
||||
|
||||
This package can be easily update to use `sqlx` as well.
|
||||
|
||||
|
||||
### Dist
|
||||
|
||||
This is where your assets live. Any Javascript, images, or styling needs to go in the
|
||||
@@ -179,35 +190,16 @@ commands.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Install [templ](https://templ.guide/quick-start/installation)
|
||||
- Install [templ](https://templ.guide/quick-start/installation) - `go install github.com/a-h/templ/cmd/templ@latest`
|
||||
- Install [sqlc](https://docs.sqlc.dev/en/stable/overview/install.html)
|
||||
- Install [tailwindcss CLI](https://tailwindcss.com/docs/installation)
|
||||
- Install [air](https://github.com/cosmtrek/air#installation)
|
||||
|
||||
### air
|
||||
|
||||
`air` has been configured with the file `.air.toml` to allow live reloading of the application
|
||||
when a file changes.
|
||||
|
||||
To run, install `air`
|
||||
|
||||
#### tailwindcss
|
||||
```shell
|
||||
go install github.com/cosmtrek/air@latest
|
||||
```
|
||||
|
||||
Then simply run the command
|
||||
|
||||
```shell
|
||||
air
|
||||
```
|
||||
|
||||
#### Address Already In Use Error
|
||||
|
||||
Sometimes, you may run into the issue _address already in use_. If this is the case, you
|
||||
can run this command to find the PID to kill it.
|
||||
|
||||
```shell
|
||||
ps aux | grep tmp/main
|
||||
# Example for macOS arm64
|
||||
curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-macos-arm64
|
||||
chmod +x tailwindcss-macos-arm64
|
||||
sudo mv tailwindcss-macos-arm64 /usr/local/bin/tailwindcss
|
||||
```
|
||||
|
||||
### Makefile
|
||||
|
49
db/db.go
49
db/db.go
@@ -3,13 +3,10 @@ package db
|
||||
import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"vctp/db/queries"
|
||||
"log/slog"
|
||||
"vctp/db/queries"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database/sqlite3"
|
||||
"github.com/golang-migrate/migrate/v4/source/iofs"
|
||||
"github.com/pressly/goose/v3"
|
||||
)
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
@@ -35,21 +32,37 @@ func New(logger *slog.Logger, url string) (Database, error) {
|
||||
|
||||
// Migrate runs the migrations on the database. Assumes the database is SQLite.
|
||||
func Migrate(db Database) error {
|
||||
driver, err := sqlite3.WithInstance(db.DB(), &sqlite3.Config{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create database driver: %w", err)
|
||||
|
||||
goose.SetBaseFS(migrations)
|
||||
|
||||
if err := goose.SetDialect("sqlite3"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
iofsDriver, err := iofs.New(migrations, "migrations")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create iofs: %w", err)
|
||||
}
|
||||
defer iofsDriver.Close()
|
||||
|
||||
m, err := migrate.NewWithInstance("iofs", iofsDriver, "sqlite3", driver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create migration: %w", err)
|
||||
if err := goose.Up(db.DB(), "migrations"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return m.Up()
|
||||
// TODO - replace with goose
|
||||
/*
|
||||
driver, err := sqlite3.WithInstance(db.DB(), &sqlite3.Config{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create database driver: %w", err)
|
||||
}
|
||||
|
||||
iofsDriver, err := iofs.New(migrations, "migrations")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create iofs: %w", err)
|
||||
}
|
||||
defer iofsDriver.Close()
|
||||
|
||||
m, err := migrate.NewWithInstance("iofs", iofsDriver, "sqlite3", driver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create migration: %w", err)
|
||||
}
|
||||
|
||||
return m.Up()
|
||||
*/
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,7 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS authors (
|
||||
id INTEGER PRIMARY KEY,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
bio TEXT
|
||||
);
|
||||
|
38
db/migrations/20240912012927_init.sql
Normal file
38
db/migrations/20240912012927_init.sql
Normal file
@@ -0,0 +1,38 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS "Inventory" (
|
||||
"Iid" INTEGER UNIQUE,
|
||||
"Name" TEXT,
|
||||
"Vcenter" TEXT,
|
||||
"VmId" TEXT,
|
||||
"EventKey" TEXT,
|
||||
"EventId" TEXT,
|
||||
"CreationTime" TEXT,
|
||||
"DeletionTime" TEXT,
|
||||
"ResourcePool" TEXT,
|
||||
"VmType" TEXT,
|
||||
"Datacenter" TEXT,
|
||||
"Cluster" TEXT,
|
||||
"Folder" TEXT,
|
||||
"ProvisionedDisk" REAL,
|
||||
"InitialVcpus" INTEGER,
|
||||
"InitialRam" INTEGER,
|
||||
"SrmPlaceholder" INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Updates" (
|
||||
"Uid" INTEGER UNIQUE,
|
||||
"InventoryId" INTEGER,
|
||||
"UpdateTime" TEXT,
|
||||
"UpdateType" TEXT,
|
||||
"NewVcpus" INTEGER,
|
||||
"NewRam" INTEGER,
|
||||
"NewResourcePool" TEXT
|
||||
)
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE "Inventory";
|
||||
DROP TABLE "Updates";
|
||||
-- +goose StatementEnd
|
@@ -1,25 +1,3 @@
|
||||
-- name: GetAuthor :one
|
||||
SELECT * FROM authors
|
||||
WHERE id = ? LIMIT 1;
|
||||
|
||||
-- name: ListAuthors :many
|
||||
SELECT * FROM authors
|
||||
ORDER BY name;
|
||||
|
||||
-- name: CreateAuthor :one
|
||||
INSERT INTO authors (
|
||||
name, bio
|
||||
) VALUES (
|
||||
?, ?
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateAuthor :exec
|
||||
UPDATE authors
|
||||
SET name = ?,
|
||||
bio = ?
|
||||
WHERE id = ?;
|
||||
|
||||
-- name: DeleteAuthor :exec
|
||||
DELETE FROM authors
|
||||
WHERE id = ?;
|
||||
-- name: ListInventory :many
|
||||
SELECT * FROM "Inventory"
|
||||
ORDER BY "Name";
|
25
db/queries/query.txt
Normal file
25
db/queries/query.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
-- name: GetAuthor :one
|
||||
SELECT * FROM authors
|
||||
WHERE id = ? LIMIT 1;
|
||||
|
||||
-- name: ListAuthors :many
|
||||
SELECT * FROM authors
|
||||
ORDER BY name;
|
||||
|
||||
-- name: CreateAuthor :one
|
||||
INSERT INTO authors (
|
||||
name, bio
|
||||
) VALUES (
|
||||
?, ?
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateAuthor :exec
|
||||
UPDATE authors
|
||||
SET name = ?,
|
||||
bio = ?
|
||||
WHERE id = ?;
|
||||
|
||||
-- name: DeleteAuthor :exec
|
||||
DELETE FROM authors
|
||||
WHERE id = ?;
|
24
go.mod
24
go.mod
@@ -3,12 +3,13 @@ module vctp
|
||||
go 1.23.1
|
||||
|
||||
require (
|
||||
github.com/a-h/templ v0.2.771
|
||||
github.com/golang-migrate/migrate/v4 v4.17.1
|
||||
github.com/a-h/templ v0.2.778
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/playwright-community/playwright-go v0.4201.1
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240812094001-348a4e45b535
|
||||
modernc.org/sqlite v1.32.0
|
||||
github.com/pressly/goose/v3 v3.22.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d
|
||||
modernc.org/sqlite v1.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -20,22 +21,21 @@ require (
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/mfridman/interpolate v0.0.2 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
github.com/sethvargo/go-retry v0.3.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a // indirect
|
||||
modernc.org/libc v1.59.9 // indirect
|
||||
modernc.org/libc v1.60.1 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
|
39
go.sum
39
go.sum
@@ -1,5 +1,7 @@
|
||||
github.com/a-h/templ v0.2.771 h1:4KH5ykNigYGGpCe0fRJ7/hzwz72k3qFqIiiLLJskbSo=
|
||||
github.com/a-h/templ v0.2.771/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
|
||||
github.com/a-h/templ v0.2.778 h1:VzhOuvWECrwOec4790lcLlZpP4Iptt5Q4K9aFxQmtaM=
|
||||
github.com/a-h/templ v0.2.778/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
||||
@@ -16,8 +18,6 @@ github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkc
|
||||
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
|
||||
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
@@ -25,23 +25,18 @@ github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlG
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
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/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
||||
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
@@ -50,26 +45,33 @@ github.com/playwright-community/playwright-go v0.4201.1 h1:fFX/02r3wrL+8NB132Rcd
|
||||
github.com/playwright-community/playwright-go v0.4201.1/go.mod h1:hpEOnUo/Kgb2lv5lEY29jbW5Xgn7HaBeiE+PowRad8k=
|
||||
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/pressly/goose/v3 v3.22.0 h1:wd/7kNiPTuNAztWun7iaB98DrhulbWPrzMAaw2DEZNw=
|
||||
github.com/pressly/goose/v3 v3.22.0/go.mod h1:yJM3qwSj2pp7aAaCvso096sguezamNb2OBgxCnh/EYg=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
|
||||
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240812094001-348a4e45b535 h1:iLjJLq2A5J6L9zrhyNn+fpmxFvtEpYB4XLMr0rX3epI=
|
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240812094001-348a4e45b535/go.mod h1:l8xTsYB90uaVdMHXMCxKKLSgw5wLYBwBKKefNIUnm9s=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d h1:dOMI4+zEbDI37KGb0TI44GUAwxHF9cMsIoDTJ7UmgfU=
|
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d/go.mod h1:l8xTsYB90uaVdMHXMCxKKLSgw5wLYBwBKKefNIUnm9s=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
@@ -78,6 +80,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
@@ -92,6 +96,7 @@ modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
|
||||
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.20.7 h1:skrinQsjxWfvj6nbC3ztZPJy+NuwmB3hV9zX/pthNYQ=
|
||||
modernc.org/ccgo/v4 v4.20.7/go.mod h1:UOkI3JSG2zT4E2ioHlncSOZsXbuDCZLvPi3uMlZT5GY=
|
||||
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
|
||||
@@ -100,6 +105,8 @@ modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a h1:CfbpOLEo2IwNzJdMvE8aiRbP
|
||||
modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.59.9 h1:k+nNDDakwipimgmJ1D9H466LhFeSkaPPycAs1OZiDmY=
|
||||
modernc.org/libc v1.59.9/go.mod h1:EY/egGEU7Ju66eU6SBqCNYaFUDuc4npICkMWnU5EE3A=
|
||||
modernc.org/libc v1.60.1 h1:at373l8IFRTkJIkAU85BIuUoBM4T1b51ds0E1ovPG2s=
|
||||
modernc.org/libc v1.60.1/go.mod h1:xJuobKuNxKH3RUatS7GjR+suWj+5c2K7bi4m/S5arOY=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
@@ -110,6 +117,8 @@ modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s=
|
||||
modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
|
||||
modernc.org/sqlite v1.33.0 h1:WWkA/T2G17okiLGgKAj4/RMIvgyMT19yQ038160IeYk=
|
||||
modernc.org/sqlite v1.33.0/go.mod h1:9uQ9hF/pCZoYZK73D/ud5Z7cIRIILSZI8NdIemVMTX8=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
139
internal/utils/certOperations.go
Normal file
139
internal/utils/certOperations.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateCerts(tlsCert string, tlsKey string) {
|
||||
// @see https://shaneutt.com/blog/golang-ca-and-signed-cert-go/
|
||||
// @see https://golang.org/src/crypto/tls/generate_cert.go
|
||||
validFrom := ""
|
||||
validFor := 365 * 24 * time.Hour
|
||||
isCA := true
|
||||
|
||||
// Get the hostname
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Check that the directory exists
|
||||
relativePath := filepath.Dir(tlsCert)
|
||||
log.Printf("GenerateCerts relative path for file creation is '%s'\n", relativePath)
|
||||
_, err = os.Stat(relativePath)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("Certificate path does not exist, creating %s before generating certificate\n", relativePath)
|
||||
os.MkdirAll(relativePath, os.ModePerm)
|
||||
}
|
||||
|
||||
// Generate a private key
|
||||
priv, err := rsa.GenerateKey(rand.Reader, rsaBits)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate private key: %v", err)
|
||||
}
|
||||
|
||||
var notBefore time.Time
|
||||
if len(validFrom) == 0 {
|
||||
notBefore = time.Now()
|
||||
} else {
|
||||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", validFrom)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse creation date: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
notAfter := notBefore.Add(validFor)
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate serial number: %v", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"DTMS"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
template.DNSNames = append(template.DNSNames, hostname)
|
||||
|
||||
// Add in all the non-local IPs
|
||||
ifaces, err := net.Interfaces()
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error enumerating interfaces: %v\n", err)
|
||||
}
|
||||
|
||||
for _, i := range ifaces {
|
||||
addrs, err := i.Addrs()
|
||||
if err != nil {
|
||||
log.Printf("Oops: %v\n", err)
|
||||
}
|
||||
|
||||
for _, address := range addrs {
|
||||
// check the address type and if it is not a loopback then add it to the list
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ipnet.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isCA {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create certificate: %v", err)
|
||||
}
|
||||
|
||||
certOut, err := os.Create(tlsCert)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open %s for writing: %v", tlsCert, err)
|
||||
}
|
||||
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||
log.Fatalf("Failed to write data to %s: %v", tlsCert, err)
|
||||
}
|
||||
if err := certOut.Close(); err != nil {
|
||||
log.Fatalf("Error closing %s: %v", tlsCert, err)
|
||||
}
|
||||
log.Printf("wrote %s\n", tlsCert)
|
||||
|
||||
keyOut, err := os.OpenFile(tlsKey, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open %s for writing: %v", tlsKey, err)
|
||||
return
|
||||
}
|
||||
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to marshal private key: %v", err)
|
||||
}
|
||||
if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
|
||||
log.Fatalf("Failed to write data to %s: %v", tlsKey, err)
|
||||
}
|
||||
if err := keyOut.Close(); err != nil {
|
||||
log.Fatalf("Error closing %s: %v", tlsKey, err)
|
||||
}
|
||||
log.Printf("wrote %s\n", tlsKey)
|
||||
}
|
55
internal/utils/utils.go
Normal file
55
internal/utils/utils.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"log"
|
||||
"log/slog"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const rsaBits = 4096
|
||||
|
||||
func GetFilePath(path string) string {
|
||||
// Check for empty filename
|
||||
if len(path) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// check if filename exists
|
||||
if _, err := os.Stat(path); os.IsNotExist((err)) {
|
||||
slog.Info("File '%s' not found, searching in same directory as binary", path)
|
||||
// if not, check that it exists in the same directory as the currently executing binary
|
||||
ex, err2 := os.Executable()
|
||||
if err2 != nil {
|
||||
slog.Error("Error determining binary path : '%s'", err)
|
||||
return ""
|
||||
}
|
||||
binaryPath := filepath.Dir(ex)
|
||||
path = filepath.Join(binaryPath, path)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// Get preferred outbound ip of this machine
|
||||
// @see https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go
|
||||
func GetOutboundIP() net.IP {
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
|
||||
return localAddr.IP
|
||||
}
|
||||
|
||||
// Check if a file exists from https://stackoverflow.com/questions/12518876/how-to-check-if-a-file-exists-in-go
|
||||
func FileExists(filename string) bool {
|
||||
info, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return !info.IsDir()
|
||||
}
|
77
main.go
77
main.go
@@ -1,22 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"vctp/db"
|
||||
utils "vctp/internal/utils"
|
||||
"vctp/log"
|
||||
"vctp/server"
|
||||
"vctp/server/router"
|
||||
"os"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
var (
|
||||
bindDisableTls bool
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load data from environment file
|
||||
envFilename := utils.GetFilePath(".env")
|
||||
err := godotenv.Load(envFilename)
|
||||
if err != nil {
|
||||
panic("Error loading .env file")
|
||||
}
|
||||
|
||||
logger := log.New(
|
||||
log.GetLevel(),
|
||||
log.GetOutput(),
|
||||
)
|
||||
|
||||
// Configure database
|
||||
database, err := db.New(logger, "./db.sqlite3")
|
||||
if err != nil {
|
||||
logger.Error("Failed to create database", "error", err)
|
||||
@@ -24,21 +38,68 @@ func main() {
|
||||
}
|
||||
defer database.Close()
|
||||
|
||||
if err = db.Migrate(database); err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
||||
/*
|
||||
if err = db.Migrate(database); err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
||||
logger.Error("failed to migrate database", "error", err)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
if err = db.Migrate(database); err != nil {
|
||||
logger.Error("failed to migrate database", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
// Determine bind IP
|
||||
bindIP := os.Getenv("BIND_IP")
|
||||
if bindIP == "" {
|
||||
bindIP = utils.GetOutboundIP().String()
|
||||
}
|
||||
// Determine bind port
|
||||
bindPort := os.Getenv("BIND_PORT")
|
||||
if bindPort == "" {
|
||||
bindPort = "9443"
|
||||
}
|
||||
bindAddress := fmt.Sprint(bindIP, ":", bindPort)
|
||||
slog.Info("Will listen on address", "ip", bindIP, "port", bindPort)
|
||||
|
||||
// Determine bind disable TLS
|
||||
bindDisableTlsEnv := os.Getenv("BIND_DISABLE_TLS")
|
||||
if bindDisableTlsEnv == "true" {
|
||||
bindDisableTls = true
|
||||
}
|
||||
|
||||
// Get file names for TLS cert/key
|
||||
tlsCertFilename := os.Getenv("TLS_CERT_FILE")
|
||||
if tlsCertFilename != "" {
|
||||
tlsCertFilename = utils.GetFilePath(tlsCertFilename)
|
||||
} else {
|
||||
tlsCertFilename = "./cert.pem"
|
||||
}
|
||||
|
||||
tlsKeyFilename := os.Getenv("TLS_KEY_FILE")
|
||||
if tlsKeyFilename != "" {
|
||||
tlsKeyFilename = utils.GetFilePath(tlsKeyFilename)
|
||||
} else {
|
||||
tlsKeyFilename = "./privkey.pem"
|
||||
}
|
||||
|
||||
// Generate certificate if required
|
||||
if !(utils.FileExists(tlsCertFilename) && utils.FileExists(tlsKeyFilename)) {
|
||||
slog.Warn("Specified TLS certificate or private key do not exist", "certificate", tlsCertFilename, "tls-key", tlsKeyFilename)
|
||||
utils.GenerateCerts(tlsCertFilename, tlsKeyFilename)
|
||||
}
|
||||
|
||||
// Start server
|
||||
svr := server.New(
|
||||
logger,
|
||||
":"+port,
|
||||
bindAddress,
|
||||
server.WithRouter(router.New(logger, database)),
|
||||
)
|
||||
|
||||
svr.DisableTls(bindDisableTls)
|
||||
svr.SetCertificate(tlsCertFilename)
|
||||
svr.SetPrivateKey(tlsKeyFilename)
|
||||
|
||||
svr.StartAndWait()
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"vctp/components/core"
|
||||
"vctp/components/home"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Home handles the home page.
|
||||
|
21
server/handler/vmCreate.go
Normal file
21
server/handler/vmCreate.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// VmCreate receives the CloudEvent for a VM creation
|
||||
func (h *Handler) VmCreate(w http.ResponseWriter, r *http.Request) {
|
||||
reqBody, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Invalid data received")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
h.Logger.Debug("received create request", "body", string(reqBody))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "Create Request (%d): %v\n", len(reqBody), string(reqBody))
|
||||
}
|
21
server/handler/vmUpdate.go
Normal file
21
server/handler/vmUpdate.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// VmUpdate receives the CloudEvent for a VM modification or move
|
||||
func (h *Handler) VmUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
reqBody, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Invalid data received")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
h.Logger.Debug("received update request", "body", string(reqBody))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "Update Request (%d): %v\n", len(reqBody), string(reqBody))
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"vctp/db"
|
||||
"vctp/dist"
|
||||
"vctp/server/handler"
|
||||
"vctp/server/middleware"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func New(logger *slog.Logger, database db.Database) http.Handler {
|
||||
@@ -19,6 +19,8 @@ func New(logger *slog.Logger, database db.Database) http.Handler {
|
||||
|
||||
mux.Handle("/assets/", middleware.CacheMiddleware(http.FileServer(http.FS(dist.AssetsDir))))
|
||||
mux.HandleFunc("/", h.Home)
|
||||
mux.HandleFunc("/api/event/vm/create", h.VmCreate)
|
||||
mux.HandleFunc("/api/event/vm/update", h.VmUpdate)
|
||||
|
||||
return middleware.NewLoggingMiddleware(logger, mux)
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -11,16 +12,36 @@ import (
|
||||
|
||||
// Server represents an HTTP server.
|
||||
type Server struct {
|
||||
srv *http.Server
|
||||
logger *slog.Logger
|
||||
srv *http.Server
|
||||
logger *slog.Logger
|
||||
disableTls bool
|
||||
tlsCertFilename string
|
||||
tlsKeyFilename string
|
||||
}
|
||||
|
||||
// New creates a new server with the given logger, address and options.
|
||||
func New(logger *slog.Logger, addr string, opts ...Option) *Server {
|
||||
|
||||
// Set some options for TLS
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||
PreferServerCipherSuites: true,
|
||||
InsecureSkipVerify: true,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
}
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
WriteTimeout: 15 * time.Second,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
TLSConfig: tlsConfig,
|
||||
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&Server{srv: srv})
|
||||
@@ -56,6 +77,21 @@ func WithRouter(handler http.Handler) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// DisableTls sets the disable tls value
|
||||
func (s *Server) DisableTls(disableTls bool) {
|
||||
s.disableTls = disableTls
|
||||
}
|
||||
|
||||
// SetCertificate sets the path to the certificate used for TLS, in PEM format
|
||||
func (s *Server) SetCertificate(tlsCertFilename string) {
|
||||
s.tlsCertFilename = tlsCertFilename
|
||||
}
|
||||
|
||||
// SetPrivateKey sets the path to the private key used for TLS, in PEM format
|
||||
func (s *Server) SetPrivateKey(tlsKeyFilename string) {
|
||||
s.tlsKeyFilename = tlsKeyFilename
|
||||
}
|
||||
|
||||
// StartAndWait starts the server and waits for a signal to shut down.
|
||||
func (s *Server) StartAndWait() {
|
||||
s.Start()
|
||||
@@ -64,11 +100,20 @@ func (s *Server) StartAndWait() {
|
||||
|
||||
// Start starts the server.
|
||||
func (s *Server) Start() {
|
||||
|
||||
go func() {
|
||||
s.logger.Info("starting server", "port", s.srv.Addr)
|
||||
if err := s.srv.ListenAndServe(); err != nil {
|
||||
s.logger.Warn("failed to start server", "error", err)
|
||||
if s.disableTls {
|
||||
s.logger.Info("starting server", "port", s.srv.Addr)
|
||||
if err := s.srv.ListenAndServe(); err != nil {
|
||||
s.logger.Warn("failed to start server", "error", err)
|
||||
}
|
||||
} else {
|
||||
s.logger.Info("starting TLS server", "port", s.srv.Addr, "cert", s.tlsCertFilename, "key", s.tlsKeyFilename)
|
||||
if err := s.srv.ListenAndServeTLS(s.tlsCertFilename, s.tlsKeyFilename); err != nil {
|
||||
s.logger.Warn("failed to start server", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user