update: add the package page
This commit is contained in:
parent
a556ed08aa
commit
aa0b373423
@ -1,11 +1,17 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"git.matterlinux.xyz/matter/tracker/log"
|
||||
"github.com/bigkevmcd/go-configparser"
|
||||
)
|
||||
|
||||
@ -16,6 +22,49 @@ type Package struct {
|
||||
Depends []string `json:"depends"`
|
||||
Size string `json:"size"`
|
||||
Desc string `json:"desc"`
|
||||
Archive string `json:"archive"`
|
||||
}
|
||||
|
||||
func (p *Package) Files() []string {
|
||||
var (
|
||||
gzip_reader io.Reader
|
||||
header *tar.Header
|
||||
result []string
|
||||
file *os.File
|
||||
err error
|
||||
)
|
||||
|
||||
if file, err = os.Open(p.Archive); err != nil {
|
||||
log.Error("Failed to open %s", p.Archive)
|
||||
return result
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if gzip_reader, err = gzip.NewReader(bufio.NewReader(file)); err != nil {
|
||||
log.Error("Failed to create reader for %s", p.Archive)
|
||||
return result
|
||||
}
|
||||
|
||||
reader := tar.NewReader(gzip_reader)
|
||||
|
||||
for header, err = reader.Next(); err == nil; header, err = reader.Next() {
|
||||
if header.Typeflag != tar.TypeReg {
|
||||
continue
|
||||
}
|
||||
|
||||
if path.Base(header.Name) != "files.tar.gz" {
|
||||
continue
|
||||
}
|
||||
|
||||
if result, err = GetFiles(reader); err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
log.Error("Failed to get file list for %s: %s", p.Archive, err.Error())
|
||||
return []string{}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *Package) URL() string {
|
||||
|
@ -75,12 +75,14 @@ func (p *Pool) LoadList(list *[]Package) error {
|
||||
}
|
||||
|
||||
var pkg Package
|
||||
pkg.Pool = p
|
||||
|
||||
if err = pkg.Load(reader); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pkg.Archive = path.Join(p.Dir, fmt.Sprintf("%s_%s.mpf", pkg.Name, pkg.Version))
|
||||
pkg.Pool = p
|
||||
|
||||
*list = append(*list, pkg)
|
||||
}
|
||||
|
||||
|
28
lib/util.go
28
lib/util.go
@ -1,13 +1,41 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func GetFiles(r io.Reader) ([]string, error) {
|
||||
var (
|
||||
gzip_reader io.Reader
|
||||
header *tar.Header
|
||||
result []string
|
||||
err error
|
||||
)
|
||||
|
||||
if gzip_reader, err = gzip.NewReader(r); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
reader := tar.NewReader(gzip_reader)
|
||||
|
||||
for header, err = reader.Next(); err == nil; header, err = reader.Next() {
|
||||
if header.Typeflag != tar.TypeReg {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, header.Name)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func ListToStr(l []string) string {
|
||||
res := ""
|
||||
for _, e := range l {
|
||||
|
2
main.go
2
main.go
@ -92,7 +92,7 @@ func main() {
|
||||
})
|
||||
|
||||
app.Get("/", routes.GET_index)
|
||||
app.Get("/p/:name", routes.GET_package)
|
||||
app.Get("/p/:name/:version", routes.GET_package)
|
||||
|
||||
app.Get("*", func(c *fiber.Ctx) error {
|
||||
return lib.RenderError(c, 404)
|
||||
|
164
public/style.css
164
public/style.css
@ -5,43 +5,157 @@ main {
|
||||
padding: 40px 10% 0% 10%;
|
||||
}
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
color: var(--bright-main);
|
||||
background: var(--dark-second);
|
||||
border: solid 1px var(--bright-main);
|
||||
flex-direction: column;
|
||||
padding: 30px;
|
||||
gap: 10px;
|
||||
.package-main {
|
||||
padding: 40px 20% 0% 20%;
|
||||
}
|
||||
|
||||
.status {
|
||||
.package {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.package tr {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.package th {
|
||||
padding: 10px;
|
||||
border: 1px solid var(--bright-third);
|
||||
background: var(--dark-second);
|
||||
color: var(--bright-second);
|
||||
text-align: left;
|
||||
flex-grow: 4;
|
||||
}
|
||||
|
||||
.package th {
|
||||
color: var(--bright-main);
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.package th a {
|
||||
color: var(--bright-second);
|
||||
}
|
||||
|
||||
.package th a:hover {
|
||||
color: var(--bright-main);
|
||||
}
|
||||
|
||||
.package tr th:first-child {
|
||||
font-weight: 900;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.package tr th:last-child {
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.package-name th {
|
||||
background: var(--dark-third);
|
||||
border: 1px solid var(--bright-second);
|
||||
}
|
||||
|
||||
.detail {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
form {
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
.detail h3 {
|
||||
font-size: 30px;
|
||||
color: var(--bright-main);
|
||||
}
|
||||
|
||||
.detail .links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.detail .links a {
|
||||
border: solid 1px var(--bright-third);
|
||||
background: var(--dark-second);
|
||||
color: var(--bright-second);
|
||||
font-size: 16px;
|
||||
padding: 7px 10px;
|
||||
}
|
||||
|
||||
.detail .links a:hover {
|
||||
border: solid 1px var(--bright-second);
|
||||
color: var(--bright-main);
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
form input {
|
||||
flex-grow: 4;
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
color: var(--bright-main);
|
||||
background: var(--dark-second);
|
||||
border: solid 1px var(--bright-second);
|
||||
padding: 30px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
form .search {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
flex: 0 0 80%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1500px) {
|
||||
form .search {
|
||||
flex: 0 0 70%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
form .search {
|
||||
flex: 0 0 60%;
|
||||
}
|
||||
}
|
||||
|
||||
form .search input {
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
background: var(--dark-main);
|
||||
padding: 10px;
|
||||
outline: none;
|
||||
flex-grow: 4;
|
||||
border: solid 1px var(--bright-third);
|
||||
}
|
||||
|
||||
form select {
|
||||
form .search .status{
|
||||
color: var(--bright-main);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 900px) {
|
||||
form .search .status{
|
||||
flex-direction: column;
|
||||
align-items: left;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
form .options {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
form .options select {
|
||||
appearance: none;
|
||||
flex-grow: 1;
|
||||
font-size: 15px;
|
||||
@ -52,7 +166,7 @@ form select {
|
||||
cursor: pointer;
|
||||
display: grid;
|
||||
border: none;
|
||||
width: 10%;
|
||||
width: 100%;
|
||||
border: solid 1px var(--bright-third);
|
||||
|
||||
margin: 0;
|
||||
@ -77,17 +191,17 @@ form select {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
table {
|
||||
.list {
|
||||
margin-top: 20px;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table tr {
|
||||
.list tr {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
table th, table td {
|
||||
.list th, .list td {
|
||||
padding: 10px;
|
||||
border: 1px solid var(--bright-second);
|
||||
background: var(--dark-second);
|
||||
@ -96,15 +210,19 @@ table th, table td {
|
||||
flex-grow: 4;
|
||||
}
|
||||
|
||||
table td a {
|
||||
.list td a {
|
||||
color: var(--bright-second);
|
||||
}
|
||||
|
||||
table th {
|
||||
.list td a:hover {
|
||||
color: var(--bright-main);
|
||||
}
|
||||
|
||||
.list th {
|
||||
font-weight: 900;
|
||||
color: var(--bright-main);
|
||||
}
|
||||
|
||||
table td {
|
||||
.list td {
|
||||
font-style: italic;
|
||||
}
|
||||
|
@ -1,8 +1,44 @@
|
||||
package routes
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"git.matterlinux.xyz/matter/tracker/lib"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func GET_package(c *fiber.Ctx) error {
|
||||
//name := c.Params("name")
|
||||
return c.Render("package", &fiber.Map{})
|
||||
var (
|
||||
name string
|
||||
version string
|
||||
list *[]lib.Package
|
||||
)
|
||||
|
||||
is_json := c.Query("json") == "1"
|
||||
is_download := c.Query("download") == "1"
|
||||
|
||||
list = c.Locals("list").(*[]lib.Package)
|
||||
version = c.Params("version")
|
||||
name = c.Params("name")
|
||||
|
||||
for _, pkg := range *list {
|
||||
if pkg.Name != name || (version != "ANY" && pkg.Version != version) {
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
if is_download {
|
||||
c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", path.Base(pkg.Archive)))
|
||||
return c.SendFile(pkg.Archive)
|
||||
}
|
||||
|
||||
if is_json {
|
||||
return c.JSON(pkg)
|
||||
}
|
||||
|
||||
return c.Render("package", &pkg)
|
||||
}
|
||||
|
||||
return lib.RenderError(c, 404)
|
||||
}
|
||||
|
@ -9,27 +9,36 @@
|
||||
<body>
|
||||
{{template "parts/bar" .}}
|
||||
<main>
|
||||
<div class="search">
|
||||
<form action="/" method="GET">
|
||||
{{if .query}}
|
||||
<input placeholder="Hit enter to search" type="text" name="q" value="{{.query}}">
|
||||
{{else}}
|
||||
<input placeholder="Hit enter to search" type="text" name="q" autofocus>
|
||||
{{end}}
|
||||
<form action="/" method="GET">
|
||||
<div class="options">
|
||||
<select name="pools">
|
||||
<option value="">Select pool</option>
|
||||
{{range .pools}}
|
||||
<option value="{{.Name}}">{{.Display}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</form>
|
||||
<div class="status">
|
||||
<p>Listing {{len .list}} packages</p>
|
||||
<p>Last updated: {{.last}}</p>
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" name="exact" value="1">
|
||||
<label>Only show exact matches</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" name="json" value="1">
|
||||
<label>Show results as JSON</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="pkgs">
|
||||
<div class="search">
|
||||
{{if .query}}
|
||||
<input placeholder="Hit enter to search" type="text" name="q" value="{{.query}}">
|
||||
{{else}}
|
||||
<input placeholder="Hit enter to search" type="text" name="q" autofocus>
|
||||
{{end}}
|
||||
<div class="status">
|
||||
<p>Listing {{len .list}} packages</p>
|
||||
<p>Last updated: {{.last}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<table class="list">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Pool</th>
|
||||
@ -40,7 +49,7 @@
|
||||
</tr>
|
||||
{{range .list}}
|
||||
<tr>
|
||||
<td><a href="{{.URL}}">{{.Name}}</a></td>
|
||||
<td><a href="/p/{{.Name}}/{{.Version}}">{{.Name}}</a></td>
|
||||
{{if .Pool}}
|
||||
<td>{{.Pool.Display}}</td>
|
||||
{{else}}
|
||||
|
60
templates/package.html
Normal file
60
templates/package.html
Normal file
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>MatterLinux | {{.Name}} {{.Version}}</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1200">
|
||||
<link href="/style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
{{template "parts/bar" .}}
|
||||
<main class="package-main">
|
||||
<div class="detail">
|
||||
<h3>Package details</h3>
|
||||
<div class="links">
|
||||
<a href="{{.URL}}">View source</a>
|
||||
<a href="/p/{{.Name}}/{{.Version}}?download=1">Download archive</a>
|
||||
</div>
|
||||
</div>
|
||||
<table class="package">
|
||||
<tr class="package-name">
|
||||
<th>Name</th>
|
||||
<th>{{.Name}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Version</th>
|
||||
<th>{{.Version}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Size</th>
|
||||
<th>{{.Size}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>{{.Desc}}</th>
|
||||
</tr>
|
||||
{{if .Depends}}
|
||||
<tr>
|
||||
<th>Dependencies</th>
|
||||
<th>
|
||||
{{range .Depends}}
|
||||
<a href="/p/{{.}}/ANY">{{.}}</a>
|
||||
{{end}}
|
||||
</th>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{$files := .Files}}
|
||||
{{if $files}}
|
||||
<tr>
|
||||
<th>Files</th>
|
||||
<th>
|
||||
{{range $files}}
|
||||
<p>/{{.}}</p>
|
||||
{{end}}
|
||||
</th>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user