feat: config

This commit is contained in:
2025-08-24 14:23:51 +02:00
parent 0f3da902dc
commit 958979c62b
9 changed files with 159 additions and 86 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
config.json

60
config.example.json Normal file
View File

@@ -0,0 +1,60 @@
{
"gitea_url": "https://git.schreifuchs.ch",
"gitea_token": "",
"repos": [
{
"owner": "lou-taylor",
"repo": "lou-taylor-web"
},
{
"owner": "lou-taylor",
"repo": "lou-taylor-api"
},
{
"owner": "lou-taylor",
"repo": "accounting"
}
],
"min_duration": "15m",
"hourly": 16,
"from_entity": {
"name": "schreifuchs.ch",
"iban": "",
"address": {
"street": "",
"number": "",
"zip_code": "",
"place": "",
"country": ""
},
"contact": ""
},
"to_entity": {
"name": "",
"address": {
"street": "",
"number": "",
"zip_code": "",
"place": "",
"country": ""
},
"contact": "Loana Groux"
},
"pdf_generator_url": "http://localhost:3030",
"mailer": {
"smtp": {
"host": "mail.your-server.de",
"port": "465",
"user": "",
"password": ""
},
"from": ""
},
"mail": {
"to": "",
"subject": "",
"body": ""
},
"mail_bcc": [""]
}

36
config.go Normal file
View File

@@ -0,0 +1,36 @@
package main
import (
"encoding/json"
"os"
"git.schreifuchs.ch/lou-taylor/accounting/mailer"
"git.schreifuchs.ch/lou-taylor/accounting/model"
)
type Config struct {
GiteaURL string `json:"gitea_url"`
GiteaToken string `json:"gitea_token"`
Repos []Repo `json:"repos"`
MinDuration Duration `json:"min_duration"`
Hourly float64 `json:"hourly"`
FromEntity model.Entity `json:"from_entity"`
ToEntity model.Entity `json:"to_entity"`
PdfGeneratorURL string `json:"pdf_generator_url"`
Mailer mailer.Config `json:"mailer"`
Mail mailer.Mail `json:"mail"`
MailBcc []string `json:"mail_bcc"`
}
func LoadConfig(path string) (Config, error) {
var cfg Config
file, err := os.Open(path)
if err != nil {
return cfg, err
}
defer file.Close()
decoder := json.NewDecoder(file)
err = decoder.Decode(&cfg)
return cfg, err
}

3
go.mod
View File

@@ -6,6 +6,7 @@ require (
code.gitea.io/sdk/gitea v0.21.0
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a
github.com/jedib0t/go-pretty/v6 v6.6.8
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/starwalkn/gotenberg-go-client/v8 v8.11.0
)
@@ -15,11 +16,9 @@ require (
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/go-fed/httpsig v1.1.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
)

4
go.sum
View File

@@ -49,9 +49,5 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
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

@@ -216,7 +216,7 @@ article {
<h2>schreifuchs.ch</h2>
<p>
Kilchbergerweg 1 <br />
3052 Zollikofen <br />
Zollikofen <br />
Niklas Breitenstein
</p>
@@ -224,7 +224,7 @@ article {
</div>
<div class="invoice-info">
<p>
<strong>Rechnung:</strong> 20 25082 41057 10271 08692 82635 <br />
<strong>Rechnung:</strong> 20 25082 41117 00284 67114 43342 <br />
<strong>Datum:</strong> 24.08.2025 <br />
</p>
</div>
@@ -234,7 +234,7 @@ article {
<p>
Lou Taylor <br />
Alpenstrasse 22 <br />
4950 Huttwil <br />
Huttwil <br />
Loana Groux
</p>
</section>
@@ -387,19 +387,19 @@ article {
CH06 0079 0042 5877 0443 7 <br />
schreifuchs.ch <br />
Kilchbergerweg 1 <br />
3052 Zollikofen
Zollikofen
</p>
</div>
<div>
<h4>Referenz</h4>
<p>20 25082 41057 10271 08692 82635</p>
<p>20 25082 41117 00284 67114 43342</p>
</div>
<div>
<h4>Zahlbar durch</h4>
<p>
Lou Taylor <br />
Alpenstrasse 22 <br />
4950 Huttwil
Huttwil
</p>
</div>
</div>
@@ -481,7 +481,7 @@ article {
<div class="qr-code">
<div class="qr-section-img">
<image src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIAAQMAAADOtka5AAAABlBMVEX///8AAABVwtN&#43;AAAGaklEQVR42uydzbGkOgyF1cXCS0IgFEKD0AiFEFiyoNAr6xzJ7vsiaI96NXXLfLNx6fdIFsXvnC&#43;pv0lfUdFdVj1EZr3KXfSRj6qKbHqseopI0XvihwlIAAB7/W6pgKL1aAXItgNwiZRb7HsVke2oh&#43;erAh77DxMwDGBTPZZTZL7LMz0fHlU9ZLlkvqVU6gfX5ljPxf5x82QCEvANmK/5xlUk4FhVzUoVfSrALp6sei71ZAIGBcistwjsTDVJ9Xv/mZ3CSa2eqR5NwGgAeCYH0AltsB3mmSZ9K8Du0Z//KgEJcECEuvRMCHW3Y3UzVY1PtUi6b3v9Iy3S/2PlBPwwwH8z3NDUApT1ZKjLs7LtcbZepT&#43;/BPzbgOqwGM3UozXEoRc7ZHHf5pGuUZdLkDPVS6sJGANg7sacEAMXy50tSzYnZFfm012k6q5qPj2Zu0rASID61/mS8kzubDY3KMUAFqBs9c6JNM&#43;E/ysBCaDxOeBjCo2PiFpV9lyqoUJN9nWqFWXtbDVTTLoS8PsANcdkBqkwjanfix&#43;1WprnQXBXrNibE0tAAgBg/Drjzv3t51hRdnrl48ZrQUOoGq/307o8Cfh1QNwky3ge1mT3rlzi0cxuRdl6tKi1fl6voSTg9wE7Q11VK857k&#43;ZYD0H51e4Mbpt1&#43;xChMO9NQAIAsA6x6lmzo/JM4ZssqEUixZsom&#43;JozbK99ZOAQQBmkJDzqDf78P1aqRfj35dxiywo2RvV7lwCEmCAXXi9pNyTJc9sF0Jzop4726W1ygziXy/CJGAQABOZC4DX3A0KtWdfhY&#43;U55Q5tGp9ATcBvwxgVItqiTIYUd1X7/rGnZFt3471FFbYLA9KQAII0H07xOuvk/r3NT3CWfuhy7Mz1LXv6a4SMAhAva93lVAeWXKzKiNdaSV77/3cVlixkwlIgHyFumzooDOIpAmuDQ2dGgxt0Uq&#43;q&#43;nyYlwCBgDsG6KZmvI8wlDXAKaCrogJjb2NGjZeryjOJ2AMAFu5lvlO7PZ5UIvrZcJ3dn2t6ibl8TuXgAS0&#43;utyQpdmka4FM9bmmREAwyBZ6mzNZHZ5mr9KwM8DosFbww5F7hzu6kIA3EbzFN8XTNK8IglIgAM4beG9YLZueBM1lG2hnF8wtMPrmYAxAF1Uy4aO0iJh8JMZtSnnmfJ07koSMAjADIp5JstnqQ2JqV5IEVstzRVsMVaRgAR0AC&#43;7AdDiHtxEc0KqXw0hkd61JeDXAVacV7qmKYRpvkSCM54SMpLqmRD&#43;WkKtCRgDwMk8sx0SGncDLFZUNQladH3XU8h9Pm83opmABFBkZDUUlN3UK3QoxqH&#43;6uEQpUvdTUzAAAAan5rl2qBV/FVM4s7NEuKhLrLku19Vk4AEYOoGqfN8&#43;dGQUdvQeJMeWSZlurTSGa8EDAFAg/dE2cyvDEZpTkFRFomQz1qw66vPJ4ooCfh9gM9FXG00j9uJfAdadPs4zXnaurPIjhKQAOrSVoS6pjJ6ov5KTWuJygoy6lNgkaamOEnACAAGI9UiyRSTNBivubpVia4piKiYcqIEJEAYqR6ovyJnalMR3tjDKE5soaDIntuREjAKQJtS4HH9ANwV11whqg2xGnQkU1&#43;MS8DPA5D8xlLEyccqmliN9ddYrnnOFC2yQZyABFAo4AsjbMbzw7EK8et5o5fc7y3BUkVx&#43;UACBgCgQIax3q7Lw/GaOXYxU1RAiXzTGCUgARqbjL4WcYZrw&#43;6rTuhqDeZFfaMA19okYADAt2cKsVnLg7BZ4pVuxJPDFpKAgQA17ICZQKiLCQob0cTMt6sWXad6ej7MPZoJSADaxrJ65BLru1sw06mUoFg5fSMWa3EJGAJATQAFI1OnNcOGEZoejuZxKXjRu&#43;/2JSABX1tpeFh9eQDXXHkzOVb7xrxfAkYCcBmJNYj96QDx3VdRQvFuH2dufP93AsYAxOb/iwCN5e7cmOlCVzMoFstokxMlIAF/3iJpl86imVXpmbqcqc14xiNXkoBBAN3riZ7H9H&#43;UWJnJUKZNUHQ6lAQkIF5Lg0nyXXiY4ovX0rSf7dM246kJGAsQ&#43;kZYJI0XeKOz5w8W2Zqr8rQNzwkYDPDn5aolVIuYoNh8NK/N3HQGJQH/OsBl0FdplVa&#43;a2Z1lUKA7lzIOuNZXoirEzAI4Ov1xK&#43;3u7mySJ9Ol0YZSfl&#43;eyABvw74LwAA//9wYD7CB3CrcAAAAABJRU5ErkJggg=="></image>
<image src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIAAQMAAADOtka5AAAABlBMVEX///8AAABVwtN&#43;AAAFNElEQVR42uydMZLySgyERREQcgSO4qPB0TgKRyDcwGW9stRqybsEL/2LdrRrzEeimpFbLY15Xpv5Y/H3ZT3l/z/nzcyWl13yo9fV15Ob2e19cXezBV8UQIAEPMzMzpu5P83iw7v72&#43;a1fxSA/Xqb7aTllR8JIAAAC&#43;LuHk/td5eMzYg7Owfydf2xHfDEHdtjcw9SAQQ4AmLd&#43;onHn5Z/5AIGdgB6bRNAgM&#43;AuOLx2BLXeBwb4IMhuS97AgjwCYC9MbfEJN08wu3uL6s7b3yvnvm7uQrw3YCRre9L2v/&#43;41O6L8D3AmZKfstM/P68Ie2KSMSSdom1LfZG&#43;3gJ8NWAlA3ybW9fpTKTssv&#43;1H1KC/yR4/4pgAABiLgrkcnxbleZVMtOe0rljocj/PLXTAABAjCFqCQFIK/aAONObIl72m7xs72kCSBArlupiD8iErnZlWxwzi0xVzv3/CiTrFUAAQoQkViFFKPsjWJLigSIu1Q083uZiLkLIAAAlaRnIaVkp1abVsQmhKjcP0NIQJYmgABZtL297Vwp&#43;dQGssZSO&#43;HFCchIxI8IIEAA4sptEiKBMyU3S9nJsX9mto4L738CCLAZXuBib3QfskG&#43;/0Wxd0Qrqnb42XhGAAEA2JOszJsq7p6VdlnGHWp0WMlAwjuiAAIQcKXrBN/LnZB/9N5orLHUwwII0PYBL7NAmk0Qm7hjWO18a4tcvgie3QUQgIDXWMCoDfTeePL2ymWStfX6ZwIIcK5sPfWDR9VYUj&#43;Ypbk7FKleyR60YQogQNrhIhJpfytnU6rmEKLcX1ZCwvWYdgkgQJZIxtseFrCyyEE2SGmhyy&#43;oFoMkgAApWtK1xCvyd6ZdtIGzjjeTLAEEMBTrKrjuE/JDQ9wKIaoUzfewqGwmgAAljbNGNwy5NpBDdjqsdrBBCSDAybklHnwEJU3VajcNKd7lFxpZBBAgrlmRqxrvevJulesFDB0q&#43;VHUWAQQoGwkXhU56AclZMbaVj1zXMmwbWIhFECA2c8UVzVW2s17AfOtbJgpNpSigMYCAQQAgAlUtzF1q1y9HHarU3VhdiQKIICj6fZgn2z7m9feyA6nqrHY0iNPBBAgCyn9uMPZdGUX7phCQWflMH27AAKc2bLdb3urDR9K&#43;ihzk6Qild&#43;jVVwAAQydtoiyMiv1OCUqCs4u3vbKVRuKAAJUjaWsAfAoZZTZQVGoOyj2JjKzLQEEmB6B1i&#43;NJLgG2lkQqtWhICOAAA0o2ZJzuZC298hAG9HaGf1JAAEIGDMnMDyAhhROyqme3fEMei4FEKB6vnMu4EigOFepxyqzi7f84FaDKgUQwH1avB/LHBXAQd3rYXgJ7rSfTgABMI2EXt058pbFuss6Ry6N2Se/2lAE&#43;HYAJaUx&#43;7ZkS/caFWD2IVv/NcZCgK8G9Hxuvv&#43;x2FKN3cOQYlOswtEBAgjQEyZYWqHa1BOX/9To0OpUPhQBBGCHZS9XxlMpesBbVYZbbLA&#43;zEQAAWowCRTNvydO0NkEH0GZvnlOhQACdESdObJ0zi717h6omQGYxsSzvFwAAQbArJsGGGVcyUar7mg1uA4figBfDzicQVHTTKuxEjthA9hqUGKDAAKMSV09hDJLK9XP3V7dXtJKR6&#43;MTAABTm6HkwN5vgTPUCqLANe29qFwHooAAkwAzzJlPxOHKDPJon0AVbtDJAogQI0D5OxkP7ztfThvuY/HEUCAbR5mMnp2235ysFiyn8l9VF0EEKAHdSO4euQkLp6lXMO8lzGFYihZAvzjgP8CAAD//4aIC6aSJNHQAAAAAElFTkSuQmCC"></image>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 20.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
@@ -587,19 +587,19 @@ article {
CH06 0079 0042 5877 0443 7 <br />
schreifuchs.ch <br />
Kilchbergerweg 1 <br />
3052 Zollikofen
Zollikofen
</p>
</div>
<div>
<h4>Referenz</h4>
<p>20 25082 41057 10271 08692 82635</p>
<p>20 25082 41117 00284 67114 43342</p>
</div>
<div>
<h4>Zahlbar durch</h4>
<p>
Lou Taylor <br />
Alpenstrasse 22 <br />
4950 Huttwil
Huttwil
</p>
</div>
</div>

View File

@@ -7,10 +7,10 @@ import (
"github.com/jordan-wright/email"
)
func (s Service) Send(m Mail) (err error) {
func (s Service) Send(m Mail, bcc ...string) (err error) {
e := email.NewEmail()
e.To = []string{m.TO}
e.Bcc = []string{"niklas@sunway.ch"}
e.Bcc = bcc
e.From = s.from
e.Subject = m.Subject
e.Text = []byte(m.Body)

96
main.go
View File

@@ -1,40 +1,39 @@
package main
import (
"os"
"time"
"code.gitea.io/sdk/gitea"
"git.schreifuchs.ch/lou-taylor/accounting/issue"
"git.schreifuchs.ch/lou-taylor/accounting/mailer"
"git.schreifuchs.ch/lou-taylor/accounting/model"
"git.schreifuchs.ch/lou-taylor/accounting/pdf"
"git.schreifuchs.ch/lou-taylor/accounting/report"
)
type Repo struct {
owner string
repo string
Owner string `json:"owner"`
Repo string `json:"repo"`
}
func main() {
cfg, err := LoadConfig("config.json")
if err != nil {
panic(err)
}
client, err := gitea.NewClient(
"https://git.schreifuchs.ch",
gitea.SetToken("6a8ea8f9de039b0950c634bfea40c6f97f94b06b"),
cfg.GiteaURL,
gitea.SetToken(cfg.GiteaToken),
)
if err != nil {
panic(err)
}
var is []*gitea.Issue
for _, repo := range []Repo{
{"lou-taylor", "lou-taylor-web"},
{"lou-taylor", "lou-taylor-api"},
{"lou-taylor", "accounting"},
} {
for _, repo := range cfg.Repos {
iss, _, err := client.ListRepoIssues(
repo.owner,
repo.repo,
repo.Owner,
repo.Repo,
gitea.ListIssueOption{
ListOptions: gitea.ListOptions{Page: 0, PageSize: 99999},
Since: time.Now().AddDate(0, -1, 0),
@@ -55,46 +54,16 @@ func main() {
return i.Closed != nil && i.Closed.After(time.Now().AddDate(0, -1, 0))
},
)
issues := issue.FromGiteas(is, time.Minute*15)
issues := issue.FromGiteas(is, time.Duration(cfg.MinDuration))
r := report.New(
issues,
model.Entity{
Name: "schreifuchs.ch",
IBAN: "CH06 0079 0042 5877 0443 7",
Address: model.Address{
Street: "Kilchbergerweg",
Number: "1",
ZIPCode: "3052",
Place: "Zollikofen",
Country: "Schweiz",
},
Contact: "Niklas Breitenstein",
},
model.Entity{
Name: "Lou Taylor",
Address: model.Address{
Street: "Alpenstrasse",
Number: "22",
ZIPCode: "4950",
Place: "Huttwil",
Country: "Schweiz",
},
Contact: "Loana Groux",
},
16,
cfg.FromEntity,
cfg.ToEntity,
cfg.Hourly,
)
html := r.ToHTML()
file, err := os.Create("index.html")
if err != nil {
panic(err)
}
defer file.Close()
file.Write([]byte(html))
// fmt.Print(html)
pdfs, err := pdf.New("http://localhost:3030")
pdfs, err := pdf.New(cfg.PdfGeneratorURL)
if err != nil {
panic(err)
}
@@ -104,30 +73,21 @@ func main() {
panic(err)
}
mlr, err := mailer.New(mailer.Config{
SMTP: mailer.SMTPConfig{
Host: "mail.your-server.de",
Port: "465",
User: "test@schreifuchs.ch",
Password: "xV27D1nj33dNz8B4",
},
From: "test@schreifuchs.ch",
})
mlr, err := mailer.New(cfg.Mailer)
if err != nil {
panic(err)
}
err = mlr.Send(mailer.Mail{
TO: "kontakt@schreifuchs.ch",
Subject: "test",
Body: "Hallo",
Attachments: []mailer.Attachment{
{
Name: "invoice.pdf",
MimeType: "pdf",
Content: document,
},
mail := cfg.Mail
mail.Attachments = []mailer.Attachment{
{
Name: "invoice.pdf",
MimeType: "pdf",
Content: document,
},
})
}
err = mlr.Send(mail)
if err != nil {
panic(err)
}

21
types.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"encoding/json"
"time"
)
type Duration time.Duration
func (d *Duration) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
tmp, err := time.ParseDuration(s)
if err != nil {
return err
}
*d = Duration(tmp)
return nil
}