Compare commits
2 commits
d5fc3d8d23
...
22ddacde2e
Author | SHA1 | Date | |
---|---|---|---|
22ddacde2e | |||
da14c059c1 |
12 changed files with 458 additions and 212 deletions
46
.air.toml
Normal file
46
.air.toml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
root = "."
|
||||||
|
testdata_dir = "testdata"
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = []
|
||||||
|
bin = "./tmp/main"
|
||||||
|
cmd = "templ generate && go build -o ./tmp/main ."
|
||||||
|
delay = 1000
|
||||||
|
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = ["_test.go", ".*_templ.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "templ", "html"]
|
||||||
|
include_file = []
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
poll = false
|
||||||
|
poll_interval = 0
|
||||||
|
post_cmd = []
|
||||||
|
pre_cmd = []
|
||||||
|
rerun = false
|
||||||
|
rerun_delay = 500
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = false
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
main_only = false
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
|
keep_scroll = true
|
98
api.go
98
api.go
|
@ -1,75 +1,75 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"encoding/json"
|
||||||
"encoding/json"
|
"net/http"
|
||||||
|
|
||||||
//"context"
|
//"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/ggicci/httpin"
|
||||||
"github.com/ggicci/httpin"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DEFAULT_RESULT_COUNT = 50;
|
const DEFAULT_RESULT_COUNT = 50
|
||||||
|
|
||||||
type GetTransactionPaginationInput struct {
|
type GetTransactionPaginationInput struct {
|
||||||
ResultCount int `in:"query=result_count"`
|
ResultCount int `in:"query=result_count"`
|
||||||
PageNum int `in:"query=page_num"`
|
PageNum int `in:"query=page_num"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiRouter() http.Handler {
|
func apiRouter() http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
//r.Use(ApiLoginRequired)
|
//r.Use(ApiLoginRequired)
|
||||||
r.With(
|
r.With(
|
||||||
httpin.NewInput(GetTransactionPaginationInput{}),
|
httpin.NewInput(GetTransactionPaginationInput{}),
|
||||||
).Get("/get_transactions", getTransactions)
|
).Get("/get_transactions", getTransactions)
|
||||||
r.Post("/new_transaction", newTransaction)
|
r.Post("/new_transaction", newTransaction)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTransactions(w http.ResponseWriter, req *http.Request) {
|
func getTransactions(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
input := req.Context().Value(httpin.Input).(*GetTransactionPaginationInput)
|
input := req.Context().Value(httpin.Input).(*GetTransactionPaginationInput)
|
||||||
|
|
||||||
if input.ResultCount == 0 {
|
if input.ResultCount == 0 {
|
||||||
input.ResultCount = DEFAULT_RESULT_COUNT
|
input.ResultCount = DEFAULT_RESULT_COUNT
|
||||||
}
|
}
|
||||||
|
|
||||||
transactions := []Transaction{}
|
transactions := []Transaction{}
|
||||||
|
|
||||||
err := db_get_transactions(&transactions, input)
|
err := db_get_transactions(&transactions, input)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
log.Fatal().
|
|
||||||
Err(err).
|
|
||||||
Msg("Fatal error in getTransactions from db_get_transactions")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, trns := range transactions {
|
log.Fatal().
|
||||||
//bytes, err := json.Marshal(trns)
|
Err(err).
|
||||||
bytes, err := json.MarshalIndent(trns, "", "\t")
|
Msg("Fatal error in getTransactions from db_get_transactions")
|
||||||
if err != nil {
|
}
|
||||||
log.Fatal().
|
|
||||||
Err(err).
|
for _, trns := range transactions {
|
||||||
Msg("Could not marshal json")
|
//bytes, err := json.Marshal(trns)
|
||||||
}
|
bytes, err := json.MarshalIndent(trns, "", "\t")
|
||||||
fmt.Fprintf(w, string(bytes))
|
if err != nil {
|
||||||
}
|
log.Fatal().
|
||||||
|
Err(err).
|
||||||
|
Msg("Could not marshal json")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, string(bytes))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTransaction(w http.ResponseWriter, req *http.Request) {
|
func newTransaction(w http.ResponseWriter, req *http.Request) {
|
||||||
decoder := json.NewDecoder(req.Body)
|
decoder := json.NewDecoder(req.Body)
|
||||||
var t Transaction
|
var t Transaction
|
||||||
err := decoder.Decode(&t)
|
err := decoder.Decode(&t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().
|
log.Fatal().
|
||||||
Err(err).
|
Err(err).
|
||||||
Msg("Could not decode incoming post data")
|
Msg("Could not decode incoming post data")
|
||||||
}
|
}
|
||||||
//fmt.Fprintf(w, "New transaction created for Account: %d, with an Amount of: %s",
|
//fmt.Fprintf(w, "New transaction created for Account: %d, with an Amount of: %s",
|
||||||
// t.Account, t.Amount)
|
// t.Account, t.Amount)
|
||||||
db_new_transaction(t)
|
db_new_transaction(t)
|
||||||
}
|
}
|
||||||
|
|
86
db.go
86
db.go
|
@ -1,58 +1,58 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
_ "github.com/lib/pq"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/lib/pq"
|
||||||
"github.com/jmoiron/sqlx"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func db_get_transactions(transactions *[]Transaction,r *GetTransactionPaginationInput) (error) {
|
func db_get_transactions(transactions *[]Transaction, r *GetTransactionPaginationInput) error {
|
||||||
db, err := sqlx.Connect(DB_TYPE, DB_CONNECTION_STRING)
|
db, err := sqlx.Connect(DB_TYPE, DB_CONNECTION_STRING)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().
|
log.Fatal().
|
||||||
Err(err).
|
Err(err).
|
||||||
Msg("Fatal error in db_get_transactions\nCannot connect to server")
|
Msg("Fatal error in db_get_transactions\nCannot connect to server")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
err = db.Select(transactions,
|
err = db.Select(transactions,
|
||||||
"SELECT trns_id, trns_amount, trns_description, " +
|
"SELECT trns_id, trns_amount, trns_description, "+
|
||||||
"trns_account, trns_bucket, trns_date " +
|
"trns_account, trns_bucket, trns_date "+
|
||||||
fmt.Sprintf("FROM %stransactions ORDER BY trns_id DESC ", DB_SCHEMA) +
|
fmt.Sprintf("FROM %stransactions ORDER BY trns_id DESC ", DB_SCHEMA)+
|
||||||
fmt.Sprintf("LIMIT %d OFFSET %d",
|
fmt.Sprintf("LIMIT %d OFFSET %d",
|
||||||
r.ResultCount, r.PageNum * r.ResultCount ))
|
r.ResultCount, r.PageNum*r.ResultCount))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func db_new_transaction(transaction Transaction) (error) {
|
func db_new_transaction(transaction Transaction) error {
|
||||||
db, err := sqlx.Connect(DB_TYPE, DB_CONNECTION_STRING)
|
db, err := sqlx.Connect(DB_TYPE, DB_CONNECTION_STRING)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info()
|
log.Info()
|
||||||
log.Fatal().
|
log.Fatal().
|
||||||
Err(err).
|
Err(err).
|
||||||
Msg("Fatal error in db_get_transactions\nCannot connect to server")
|
Msg("Fatal error in db_get_transactions\nCannot connect to server")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
log.Debug().Msgf("%#v", transaction)
|
log.Debug().Msgf("%#v", transaction)
|
||||||
|
|
||||||
_, err = db.NamedExec(
|
_, err = db.NamedExec(
|
||||||
fmt.Sprintf("INSERT INTO %stransactions", DB_SCHEMA) +
|
fmt.Sprintf("INSERT INTO %stransactions", DB_SCHEMA)+
|
||||||
"(trns_amount, trns_description, trns_account, trns_bucket, trns_date)" +
|
"(trns_amount, trns_description, trns_account, trns_bucket, trns_date)"+
|
||||||
"VALUES (:trns_amount, :trns_description, :trns_account, :trns_bucket, :trns_date)",
|
"VALUES (:trns_amount, :trns_description, :trns_account, :trns_bucket, :trns_date)",
|
||||||
transaction)
|
transaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().
|
log.Fatal().
|
||||||
Err(err).
|
Err(err).
|
||||||
Msg("Could not exec insert db query")
|
Msg("Could not exec insert db query")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
97
flake.lock
97
flake.lock
|
@ -1,5 +1,41 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1667395993,
|
||||||
|
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"templ",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694102001,
|
||||||
|
"narHash": "sha256-vky6VPK1n1od6vXbqzOXnekrQpTL4hbPAwUhT5J9c9E=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "9e21c80adf67ebcb077d75bd5e7d724d21eeafd6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1702830618,
|
"lastModified": 1702830618,
|
||||||
|
@ -16,9 +52,68 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694422566,
|
||||||
|
"narHash": "sha256-lHJ+A9esOz9vln/3CJG23FV6Wd2OoOFbDeEs4cMGMqc=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "3a2786eea085f040a66ecde1bc3ddc7099f6dbeb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs",
|
||||||
|
"templ": "templ"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"templ": {
|
||||||
|
"inputs": {
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"xc": "xc"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1704923336,
|
||||||
|
"narHash": "sha256-aS/k5kkf8kShS5hoyHjnGCZrDnJxawstO/hzTcBPziY=",
|
||||||
|
"owner": "a-h",
|
||||||
|
"repo": "templ",
|
||||||
|
"rev": "914bc73f8030166c9f8bd46af62f5f9bb2c3b089",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "a-h",
|
||||||
|
"repo": "templ",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xc": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": [
|
||||||
|
"templ",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1703164129,
|
||||||
|
"narHash": "sha256-kCcCqqwvjN07H8FPG4tXsRVRcMqT8dUNt9pwW1kKAe8=",
|
||||||
|
"owner": "joerdav",
|
||||||
|
"repo": "xc",
|
||||||
|
"rev": "0655cccfcf036556aeaddfb8f45dc7e8dd1b3680",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "joerdav",
|
||||||
|
"repo": "xc",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
29
flake.nix
29
flake.nix
|
@ -2,9 +2,12 @@
|
||||||
description = "Recount's server/backend, written in Go";
|
description = "Recount's server/backend, written in Go";
|
||||||
|
|
||||||
# Nixpkgs / NixOS version to use.
|
# Nixpkgs / NixOS version to use.
|
||||||
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
templ.url = "github:a-h/templ";
|
||||||
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs }:
|
outputs = { self, nixpkgs, templ }:
|
||||||
let
|
let
|
||||||
# Generate a user-friendly version number.
|
# Generate a user-friendly version number.
|
||||||
version = builtins.substring 0 8 self.lastModifiedDate;
|
version = builtins.substring 0 8 self.lastModifiedDate;
|
||||||
|
@ -18,12 +21,13 @@
|
||||||
# Nixpkgs instantiated for supported system types.
|
# Nixpkgs instantiated for supported system types.
|
||||||
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||||
|
|
||||||
|
templ_function = system: templ.packages.${system}.templ;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
||||||
# Provide some binary packages for selected system types.
|
# Provide some binary packages for selected system types.
|
||||||
packages = forAllSystems (system:
|
packages = forAllSystems (system:
|
||||||
let
|
let
|
||||||
|
inherit system;
|
||||||
pkgs = nixpkgsFor.${system};
|
pkgs = nixpkgsFor.${system};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
@ -33,6 +37,9 @@
|
||||||
default = pkgs.buildGoModule {
|
default = pkgs.buildGoModule {
|
||||||
pname = "recount-server";
|
pname = "recount-server";
|
||||||
inherit version;
|
inherit version;
|
||||||
|
preBuild = ''
|
||||||
|
${templ_function system}/bin/templ generate
|
||||||
|
'';
|
||||||
# In 'nix develop', we don't need a copy of the source tree
|
# In 'nix develop', we don't need a copy of the source tree
|
||||||
# in the Nix store.
|
# in the Nix store.
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
@ -61,10 +68,22 @@
|
||||||
|
|
||||||
|
|
||||||
devShells = forAllSystems (system:
|
devShells = forAllSystems (system:
|
||||||
let pkgs = nixpkgsFor.${system};
|
let
|
||||||
|
inherit system;
|
||||||
|
pkgs = nixpkgsFor.${system};
|
||||||
in {
|
in {
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
buildInputs = with pkgs; [ go gopls gotools go-tools ];
|
shellHook = ''
|
||||||
|
alias gotest='templ generate;go run . -d'
|
||||||
|
'';
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
air # live go app reloading
|
||||||
|
go
|
||||||
|
gopls
|
||||||
|
gotools
|
||||||
|
go-tools
|
||||||
|
(templ_function system)
|
||||||
|
];
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -12,8 +12,9 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/a-h/templ v0.2.513 // indirect
|
||||||
github.com/ggicci/owl v0.4.0 // indirect
|
github.com/ggicci/owl v0.4.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.14.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -1,3 +1,5 @@
|
||||||
|
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/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -24,6 +26,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
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.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
@ -38,5 +42,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||||
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
110
main.go
110
main.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"nickiel.net/recount_server/tests"
|
"nickiel.net/recount_server/tests"
|
||||||
|
"nickiel.net/recount_server/web"
|
||||||
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -25,80 +26,79 @@ var DB_CONNECTION_STRING string = "user=rcntuser password=Devel@pmentPa$$w0rd ho
|
||||||
// "json:"json_code_name,omitempty"" (omit empty)
|
// "json:"json_code_name,omitempty"" (omit empty)
|
||||||
// if you use `json:"-"` it doesn't encode it
|
// if you use `json:"-"` it doesn't encode it
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
Id int `db:"trns_id" json:"Id"`
|
Id int `db:"trns_id" json:"Id"`
|
||||||
Amount string `db:"trns_amount" json:"Amount"`
|
Amount string `db:"trns_amount" json:"Amount"`
|
||||||
Description sql.NullString `db:"trns_description" json:"Description"`
|
Description sql.NullString `db:"trns_description" json:"Description"`
|
||||||
Account int `db:"trns_account" json:"Account"`
|
Account int `db:"trns_account" json:"Account"`
|
||||||
Bucket sql.NullInt64 `db:"trns_bucket" json:"Bucket"`
|
Bucket sql.NullInt64 `db:"trns_bucket" json:"Bucket"`
|
||||||
Date time.Time `db:"trns_date" json:"TransactionDate"`
|
Date time.Time `db:"trns_date" json:"TransactionDate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func hello(w http.ResponseWriter, req *http.Request) {
|
func hello(w http.ResponseWriter, req *http.Request) {
|
||||||
fmt.Fprintf(w, "hello\n")
|
fmt.Fprintf(w, "hello\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func headers(w http.ResponseWriter, req *http.Request) {
|
func headers(w http.ResponseWriter, req *http.Request) {
|
||||||
for name, headers := range req.Header {
|
for name, headers := range req.Header {
|
||||||
for _, h := range headers {
|
for _, h := range headers {
|
||||||
fmt.Fprintf(w, "%v: %v\n", name, h)
|
fmt.Fprintf(w, "%v: %v\n", name, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
|
||||||
var debugFlag = flag.Bool("d", false, "whether to enable debug mode")
|
var debugFlag = flag.Bool("d", false, "whether to enable debug mode")
|
||||||
var traceFlag = flag.Bool("t", false, "whether to trace logging")
|
var traceFlag = flag.Bool("t", false, "whether to trace logging")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *traceFlag {
|
if *traceFlag {
|
||||||
zerolog.SetGlobalLevel(zerolog.TraceLevel)
|
zerolog.SetGlobalLevel(zerolog.TraceLevel)
|
||||||
log.Debug().Msg("Enabling trace level debugging")
|
log.Debug().Msg("Enabling trace level debugging")
|
||||||
}
|
}
|
||||||
|
|
||||||
if *debugFlag {
|
if *debugFlag {
|
||||||
if !*traceFlag {
|
if !*traceFlag {
|
||||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||||
}
|
}
|
||||||
log.Debug().Msg("Is debugging")
|
log.Debug().Msg("Is debugging")
|
||||||
DB_TYPE = "sqlite3"
|
DB_TYPE = "sqlite3"
|
||||||
DB_SCHEMA = ""
|
DB_SCHEMA = ""
|
||||||
DB_CONNECTION_STRING = "test.db"
|
DB_CONNECTION_STRING = "test.db"
|
||||||
debug_mode.Init_testdb(DB_TYPE, DB_CONNECTION_STRING)
|
debug_mode.Init_testdb(DB_TYPE, DB_CONNECTION_STRING)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug_mode.SetLogLevel(zerolog.GlobalLevel())
|
||||||
|
|
||||||
debug_mode.SetLogLevel(zerolog.GlobalLevel())
|
log.Info().Msg("starting server")
|
||||||
|
|
||||||
log.Info().Msg("starting server")
|
r := chi.NewRouter()
|
||||||
|
|
||||||
r := chi.NewRouter()
|
// A good base middleware stack
|
||||||
|
r.Use(middleware.RequestID)
|
||||||
|
r.Use(middleware.RealIP)
|
||||||
|
r.Use(middleware.Recoverer)
|
||||||
|
r.Use(middleware.Logger)
|
||||||
|
|
||||||
// A good base middleware stack
|
// Set a timeout value on the request context (ctx), that will signal
|
||||||
r.Use(middleware.RequestID)
|
// through ctx.Done() that the request has timed out and further
|
||||||
r.Use(middleware.RealIP)
|
// processing should be stopped.
|
||||||
r.Use(middleware.Recoverer)
|
//r.Use(middleware.Timeout(60 * time.Second))
|
||||||
r.Use(middleware.Logger)
|
|
||||||
|
|
||||||
// Set a timeout value on the request context (ctx), that will signal
|
r.Get("/headers", headers)
|
||||||
// through ctx.Done() that the request has timed out and further
|
r.Mount("/", web.WebRouter())
|
||||||
// processing should be stopped.
|
r.Mount("/api", apiRouter())
|
||||||
//r.Use(middleware.Timeout(60 * time.Second))
|
|
||||||
|
|
||||||
r.Get("/", hello)
|
err := http.ListenAndServe(":8090", r)
|
||||||
r.Get("/headers", headers)
|
if err != nil {
|
||||||
r.Mount("/api", apiRouter())
|
log.Fatal().
|
||||||
|
Err(err).
|
||||||
|
Msg("Could not open server connection")
|
||||||
|
}
|
||||||
|
|
||||||
err := http.ListenAndServe(":8090", r)
|
//fmt.Println("Hello World")
|
||||||
if err != nil {
|
|
||||||
log.Fatal().
|
|
||||||
Err(err).
|
|
||||||
Msg("Could not open server connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
//fmt.Println("Hello World")
|
|
||||||
}
|
}
|
||||||
|
|
112
tests/testdb.go
112
tests/testdb.go
|
@ -1,9 +1,9 @@
|
||||||
package debug_mode
|
package debug_mode
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"encoding/json"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
@ -13,50 +13,50 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
Id int `db:"trns_id" json:"Id"`
|
Id int `db:"trns_id" json:"Id"`
|
||||||
Amount string `db:"trns_amount" json:"Amount"`
|
Amount string `db:"trns_amount" json:"Amount"`
|
||||||
Description sql.NullString `db:"trns_description" json:"Description"`
|
Description sql.NullString `db:"trns_description" json:"Description"`
|
||||||
Account int `db:"trns_account" json:"Account"`
|
Account int `db:"trns_account" json:"Account"`
|
||||||
Bucket sql.NullInt64 `db:"trns_bucket" json:"Bucket"`
|
Bucket sql.NullInt64 `db:"trns_bucket" json:"Bucket"`
|
||||||
Date time.Time `db:"trns_date" json:"TransactionDate"`
|
Date time.Time `db:"trns_date" json:"TransactionDate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetLogLevel(level zerolog.Level) {
|
func SetLogLevel(level zerolog.Level) {
|
||||||
zerolog.SetGlobalLevel(level)
|
zerolog.SetGlobalLevel(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init_testdb(DB_TYPE string, DB_CONNECTION_STRING string) {
|
func Init_testdb(DB_TYPE string, DB_CONNECTION_STRING string) {
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Could not get current working directory")
|
log.Fatal().Err(err).Msg("Could not get current working directory")
|
||||||
} else {
|
} else {
|
||||||
log.Trace().Msgf("Currect working directory is: %s", cwd)
|
log.Trace().Msgf("Currect working directory is: %s", cwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = os.Stat(cwd + DB_CONNECTION_STRING)
|
_, err = os.Stat(cwd + DB_CONNECTION_STRING)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Msg("Found existing test.db file. Attempting to delete")
|
log.Debug().Msg("Found existing test.db file. Attempting to delete")
|
||||||
err = os.Remove(DB_CONNECTION_STRING)
|
err = os.Remove(DB_CONNECTION_STRING)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to delete testing db")
|
log.Fatal().Err(err).Msg("Failed to delete testing db")
|
||||||
} else {
|
} else {
|
||||||
log.Debug().Msg("Deleted test.db file successfully")
|
log.Debug().Msg("Deleted test.db file successfully")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Debug().Msg("No existing test.db file found")
|
log.Debug().Msg("No existing test.db file found")
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := sqlx.Connect(DB_TYPE, DB_CONNECTION_STRING)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().
|
|
||||||
Err(err).
|
|
||||||
Msg("Couldn't open test db")
|
|
||||||
}
|
|
||||||
|
|
||||||
defer db.Close()
|
db, err := sqlx.Connect(DB_TYPE, DB_CONNECTION_STRING)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().
|
||||||
|
Err(err).
|
||||||
|
Msg("Couldn't open test db")
|
||||||
|
}
|
||||||
|
|
||||||
init_sql := `
|
defer db.Close()
|
||||||
|
|
||||||
|
init_sql := `
|
||||||
CREATE TABLE accounts (
|
CREATE TABLE accounts (
|
||||||
acnt_id Integer PRIMARY KEY,
|
acnt_id Integer PRIMARY KEY,
|
||||||
acnt_dsply_name varchar(50) NOT NULL,
|
acnt_dsply_name varchar(50) NOT NULL,
|
||||||
|
@ -116,18 +116,18 @@ INSERT INTO transactions (trns_amount, trns_description, trns_account, trns_buck
|
||||||
("50.00", "Money", 1, 1, "2023-11-10");
|
("50.00", "Money", 1, 1, "2023-11-10");
|
||||||
`
|
`
|
||||||
|
|
||||||
tx := db.MustBegin()
|
tx := db.MustBegin()
|
||||||
tx.MustExec(init_sql)
|
tx.MustExec(init_sql)
|
||||||
|
|
||||||
err = tx.Commit()
|
err = tx.Commit()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().
|
log.Fatal().
|
||||||
Err(err).
|
Err(err).
|
||||||
Msg("Could not commit transaction")
|
Msg("Could not commit transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonExample := `{
|
jsonExample := `{
|
||||||
"Id": 3,
|
"Id": 3,
|
||||||
"Amount": "100",
|
"Amount": "100",
|
||||||
"Description": {
|
"Description": {
|
||||||
|
@ -142,20 +142,18 @@ INSERT INTO transactions (trns_amount, trns_description, trns_account, trns_buck
|
||||||
"TransactionDate": "2023-11-11T00:00:00Z"
|
"TransactionDate": "2023-11-11T00:00:00Z"
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var trns Transaction = Transaction{}
|
var trns Transaction = Transaction{}
|
||||||
err = json.Unmarshal([]byte(jsonExample), &trns)
|
err = json.Unmarshal([]byte(jsonExample), &trns)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("could not unmarshal")
|
log.Fatal().Err(err).Msg("could not unmarshal")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.NamedExec("INSERT INTO transactions" +
|
_, err = db.NamedExec("INSERT INTO transactions"+
|
||||||
"(trns_amount, trns_description, trns_account, trns_bucket, trns_date)" +
|
"(trns_amount, trns_description, trns_account, trns_bucket, trns_date)"+
|
||||||
"VALUES (:trns_amount, :trns_description, :trns_account, :trns_bucket, :trns_date)",
|
"VALUES (:trns_amount, :trns_description, :trns_account, :trns_bucket, :trns_date)",
|
||||||
trns)
|
trns)
|
||||||
|
|
||||||
|
|
||||||
|
log.Debug().Msg("Test database initialized")
|
||||||
log.Debug().Msg("Test database initialized")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
6
web/index.templ
Normal file
6
web/index.templ
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
templ hello(name string) {
|
||||||
|
<div>Hello { name } </div>
|
||||||
|
<h1>SUPRISE!!!</h1>
|
||||||
|
}
|
48
web/index_templ.go
Normal file
48
web/index_templ.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.529
|
||||||
|
package web
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
func hello(name 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_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div>Hello ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(name)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/index.templ`, Line: 3, Col: 21}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><h1>SUPRISE!!!</h1>")
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
27
web/router.go
Normal file
27
web/router.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetLogLevel(level zerolog.Level) {
|
||||||
|
zerolog.SetGlobalLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WebRouter() http.Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Get("/", getIndex)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIndex(w http.ResponseWriter, req *http.Request) {
|
||||||
|
log.Debug().Msg("Got index")
|
||||||
|
component := hello("Nick")
|
||||||
|
component.Render(context.Background(), w)
|
||||||
|
}
|
Loading…
Reference in a new issue