First commit

This commit is contained in:
2024-11-01 22:41:06 +01:00
commit 8ce5189b62
7 changed files with 239 additions and 0 deletions

73
api/api.go Normal file
View File

@@ -0,0 +1,73 @@
package api
import (
"downloadhub/data"
_ "embed"
"fmt"
"html/template"
"log/slog"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
type (
Server struct {
r chi.Router
d data.Service
port uint16
}
)
//go:embed templates/index.html
var index string
//go:embed templates/description.html
var description string
func New(port uint16, d data.Service) *Server {
indexTemplate := template.New("index")
indexTemplate.Parse(index)
descTemplate := template.New("description")
descTemplate.Parse(description)
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
err := indexTemplate.Execute(w, d.Softwares)
if err != nil {
slog.Error(fmt.Sprintf("failed to execute template index: %s", err))
}
})
r.Get("/d/{softID}", func(w http.ResponseWriter, r *http.Request) {
softID := chi.URLParam(r, "softID")
var soft data.Software
var found bool
for _, s := range d.Softwares {
if s.UUID == softID {
soft = s
found = true
}
}
if !found {
w.Header().Add("Location", "/")
w.WriteHeader(http.StatusTemporaryRedirect)
return
}
err := descTemplate.Execute(w, soft)
if err != nil {
slog.Error(fmt.Sprintf("failed to execute template index: %s", err))
}
})
return &Server{
r: r,
d: d,
port: port,
}
}
func (s *Server) Serve() error {
return http.ListenAndServe(fmt.Sprintf(":%d", s.port), s.r)
}

View File

@@ -0,0 +1,41 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DownloadHub - {{.Name}}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-light bg-light">
<span style="margin-left: 1rem;" class="navbar-brand mb-0 h1">DownloadHub</span>
</nav>
<div class="container" style="margin-top: 1rem;">
<h2>{{.Name}} ({{.Version}})</h2>
<img width="100%" src="{{index .ScreenshotURLs 0}}" />
<p>{{.Description}}</p>
<hr />
<div class="card">
<div class="card-header">
Download
</div>
<ul class="list-group list-group-flush">
{{range .DownloadLinks}}
<a href="{{.URL}}">
<li class="list-group-item">
{{.OS}} ({{.Arch}})
</li>
</a>
{{end}}
</ul>
</div>
</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy"
crossorigin="anonymous"></script>
</html>

34
api/templates/index.html Normal file
View File

@@ -0,0 +1,34 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DownloadHub</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-light bg-light">
<span style="margin-left: 1rem;" class="navbar-brand mb-0 h1">DownloadHub</span>
</nav>
<div class="container" style="margin-top: 1rem;">
{{range .}}
<div class="row">
<div class="col-3">
<img width="100%" src="{{index .ScreenshotURLs 0}}" />
</div>
<div class="col">
<h2><a href="/d/{{.UUID}}">{{.Name}}</a></h2>
<p>{{.Description}}</p>
</div>
<hr />
</div>
{{end}}
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy"
crossorigin="anonymous"></script>
</html>

61
data/data.go Normal file
View File

@@ -0,0 +1,61 @@
package data
import (
"encoding/json"
"os"
"github.com/google/uuid"
)
type (
Service struct {
Softwares []Software
}
Software struct {
UUID string
Name string `json:"name"`
Description string `json:"description"`
Version string `json:"version"`
IconURL string `json:"icon_url"`
ScreenshotURLs []string `json:"screenshot_urls"`
DownloadLinks []DownloadLink `json:"download_links"`
}
DownloadLink struct {
OS string `json:"os"`
Arch string `json:"arch"`
URL string `json:"url"`
}
document struct {
Softwares []Software `json:"softwares"`
}
)
func Load(path string) Service {
f, err := os.OpenFile(path, os.O_RDONLY, 0744)
if err != nil {
panic("failed to open data file: " + err.Error())
}
defer f.Close()
var s document
d := json.NewDecoder(f)
err = d.Decode(&s)
if err != nil {
panic("failed to parse data file: " + err.Error())
}
s = generateUUID(s)
return Service{
Softwares: s.Softwares,
}
}
func generateUUID(d document) document {
for i, s := range d.Softwares {
s.UUID = uuid.New().String()
d.Softwares[i] = s
}
return d
}

8
go.mod Normal file
View File

@@ -0,0 +1,8 @@
module downloadhub
go 1.22
require (
github.com/go-chi/chi/v5 v5.1.0
github.com/google/uuid v1.6.0
)

4
go.sum Normal file
View File

@@ -0,0 +1,4 @@
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
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=

18
main.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"downloadhub/api"
"downloadhub/data"
"flag"
)
func main() {
var configFile string
flag.StringVar(&configFile, "c", "config.json", "Configuration file path")
flag.Parse()
d := data.Load(configFile)
s := api.New(3000, d)
s.Serve()
}