First commit
This commit is contained in:
73
api/api.go
Normal file
73
api/api.go
Normal 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)
|
||||||
|
}
|
||||||
41
api/templates/description.html
Normal file
41
api/templates/description.html
Normal 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
34
api/templates/index.html
Normal 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
61
data/data.go
Normal 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
8
go.mod
Normal 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
4
go.sum
Normal 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
18
main.go
Normal 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()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user