update: add the package page
This commit is contained in:
parent
a556ed08aa
commit
aa0b373423
@ -1,11 +1,17 @@
|
|||||||
package lib
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bufio"
|
||||||
|
"compress/gzip"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.matterlinux.xyz/matter/tracker/log"
|
||||||
"github.com/bigkevmcd/go-configparser"
|
"github.com/bigkevmcd/go-configparser"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +22,49 @@ type Package struct {
|
|||||||
Depends []string `json:"depends"`
|
Depends []string `json:"depends"`
|
||||||
Size string `json:"size"`
|
Size string `json:"size"`
|
||||||
Desc string `json:"desc"`
|
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 {
|
func (p *Package) URL() string {
|
||||||
|
@ -75,12 +75,14 @@ func (p *Pool) LoadList(list *[]Package) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pkg Package
|
var pkg Package
|
||||||
pkg.Pool = p
|
|
||||||
|
|
||||||
if err = pkg.Load(reader); err != nil {
|
if err = pkg.Load(reader); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pkg.Archive = path.Join(p.Dir, fmt.Sprintf("%s_%s.mpf", pkg.Name, pkg.Version))
|
||||||
|
pkg.Pool = p
|
||||||
|
|
||||||
*list = append(*list, pkg)
|
*list = append(*list, pkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
lib/util.go
28
lib/util.go
@ -1,13 +1,41 @@
|
|||||||
package lib
|
package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"compress/gzip"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"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 {
|
func ListToStr(l []string) string {
|
||||||
res := ""
|
res := ""
|
||||||
for _, e := range l {
|
for _, e := range l {
|
||||||
|
2
main.go
2
main.go
@ -92,7 +92,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
app.Get("/", routes.GET_index)
|
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 {
|
app.Get("*", func(c *fiber.Ctx) error {
|
||||||
return lib.RenderError(c, 404)
|
return lib.RenderError(c, 404)
|
||||||
|
164
public/style.css
164
public/style.css
@ -5,43 +5,157 @@ main {
|
|||||||
padding: 40px 10% 0% 10%;
|
padding: 40px 10% 0% 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search {
|
.package-main {
|
||||||
display: flex;
|
padding: 40px 20% 0% 20%;
|
||||||
color: var(--bright-main);
|
|
||||||
background: var(--dark-second);
|
|
||||||
border: solid 1px var(--bright-main);
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 30px;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.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);
|
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;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
.detail h3 {
|
||||||
flex-direction: row;
|
font-size: 30px;
|
||||||
justify-content: space-around;
|
color: var(--bright-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail .links {
|
||||||
display: flex;
|
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;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form input {
|
form {
|
||||||
flex-grow: 4;
|
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;
|
font-size: 15px;
|
||||||
color: white;
|
color: white;
|
||||||
background: var(--dark-main);
|
background: var(--dark-main);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
outline: none;
|
outline: none;
|
||||||
flex-grow: 4;
|
|
||||||
border: solid 1px var(--bright-third);
|
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;
|
appearance: none;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
@ -52,7 +166,7 @@ form select {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: grid;
|
display: grid;
|
||||||
border: none;
|
border: none;
|
||||||
width: 10%;
|
width: 100%;
|
||||||
border: solid 1px var(--bright-third);
|
border: solid 1px var(--bright-third);
|
||||||
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -77,17 +191,17 @@ form select {
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
.list {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
table tr {
|
.list tr {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
table th, table td {
|
.list th, .list td {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid var(--bright-second);
|
border: 1px solid var(--bright-second);
|
||||||
background: var(--dark-second);
|
background: var(--dark-second);
|
||||||
@ -96,15 +210,19 @@ table th, table td {
|
|||||||
flex-grow: 4;
|
flex-grow: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
table td a {
|
.list td a {
|
||||||
color: var(--bright-second);
|
color: var(--bright-second);
|
||||||
}
|
}
|
||||||
|
|
||||||
table th {
|
.list td a:hover {
|
||||||
|
color: var(--bright-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list th {
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: var(--bright-main);
|
color: var(--bright-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
table td {
|
.list td {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,44 @@
|
|||||||
package routes
|
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 {
|
func GET_package(c *fiber.Ctx) error {
|
||||||
//name := c.Params("name")
|
var (
|
||||||
return c.Render("package", &fiber.Map{})
|
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>
|
<body>
|
||||||
{{template "parts/bar" .}}
|
{{template "parts/bar" .}}
|
||||||
<main>
|
<main>
|
||||||
<div class="search">
|
<form action="/" method="GET">
|
||||||
<form action="/" method="GET">
|
<div class="options">
|
||||||
{{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}}
|
|
||||||
<select name="pools">
|
<select name="pools">
|
||||||
<option value="">Select pool</option>
|
<option value="">Select pool</option>
|
||||||
{{range .pools}}
|
{{range .pools}}
|
||||||
<option value="{{.Name}}">{{.Display}}</option>
|
<option value="{{.Name}}">{{.Display}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
</form>
|
<div class="checkbox">
|
||||||
<div class="status">
|
<input type="checkbox" name="exact" value="1">
|
||||||
<p>Listing {{len .list}} packages</p>
|
<label>Only show exact matches</label>
|
||||||
<p>Last updated: {{.last}}</p>
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" name="json" value="1">
|
||||||
|
<label>Show results as JSON</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="search">
|
||||||
|
{{if .query}}
|
||||||
<table class="pkgs">
|
<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>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Pool</th>
|
<th>Pool</th>
|
||||||
@ -40,7 +49,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{{range .list}}
|
{{range .list}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{.URL}}">{{.Name}}</a></td>
|
<td><a href="/p/{{.Name}}/{{.Version}}">{{.Name}}</a></td>
|
||||||
{{if .Pool}}
|
{{if .Pool}}
|
||||||
<td>{{.Pool.Display}}</td>
|
<td>{{.Pool.Display}}</td>
|
||||||
{{else}}
|
{{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