mirror of
https://github.com/derfenix/webarchive.git
synced 2026-03-11 12:41:54 +03:00
web ui: index and basic details page, api refactoring
This commit is contained in:
@@ -47,6 +47,7 @@ func (p *PDF) Process(_ context.Context, url string) ([]entity.File, error) {
|
||||
page.FooterFontSize.Set(10)
|
||||
page.Zoom.Set(p.cfg.Zoom)
|
||||
page.ViewportSize.Set(p.cfg.Viewport)
|
||||
page.NoBackground.Set(true)
|
||||
|
||||
gen.AddPage(page)
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"net/http/cookiejar"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"github.com/derfenix/webarchive/config"
|
||||
"github.com/derfenix/webarchive/entity"
|
||||
)
|
||||
@@ -52,6 +54,7 @@ func NewProcessors(cfg config.Config) (*Processors, error) {
|
||||
}
|
||||
|
||||
procs := Processors{
|
||||
client: httpClient,
|
||||
processors: map[entity.Format]processor{
|
||||
entity.FormatHeaders: NewHeaders(httpClient),
|
||||
entity.FormatPDF: NewPDF(cfg.PDF),
|
||||
@@ -64,6 +67,7 @@ func NewProcessors(cfg config.Config) (*Processors, error) {
|
||||
|
||||
type Processors struct {
|
||||
processors map[entity.Format]processor
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func (p *Processors) Process(ctx context.Context, format entity.Format, url string) entity.Result {
|
||||
@@ -93,3 +97,62 @@ func (p *Processors) OverrideProcessor(format entity.Format, proc processor) err
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processors) GetMeta(ctx context.Context, url string) (entity.Meta, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return entity.Meta{}, fmt.Errorf("new request: %w", err)
|
||||
}
|
||||
|
||||
response, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
return entity.Meta{}, fmt.Errorf("do request: %w", err)
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return entity.Meta{}, fmt.Errorf("want status 200, got %d", response.StatusCode)
|
||||
}
|
||||
|
||||
if response.Body == nil {
|
||||
return entity.Meta{}, fmt.Errorf("empty response body")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = response.Body.Close()
|
||||
}()
|
||||
|
||||
htmlNode, err := html.Parse(response.Body)
|
||||
if err != nil {
|
||||
return entity.Meta{}, fmt.Errorf("parse response body: %w", err)
|
||||
}
|
||||
|
||||
meta := entity.Meta{}
|
||||
getMetaData(htmlNode, &meta)
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func getMetaData(n *html.Node, meta *entity.Meta) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if c.Type == html.ElementNode && c.Data == "title" {
|
||||
meta.Title = c.FirstChild.Data
|
||||
}
|
||||
if c.Type == html.ElementNode && c.Data == "meta" {
|
||||
attrs := make(map[string]string)
|
||||
for _, attr := range c.Attr {
|
||||
attrs[attr.Key] = attr.Val
|
||||
}
|
||||
|
||||
name, ok := attrs["name"]
|
||||
if ok && name == "description" {
|
||||
meta.Description = attrs["content"]
|
||||
}
|
||||
}
|
||||
|
||||
getMetaData(c, meta)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,18 +64,18 @@ func (p *Page) GetFile(_ context.Context, pageID, fileID uuid.UUID) (*entity.Fil
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (p *Page) Save(_ context.Context, site *entity.Page) error {
|
||||
func (p *Page) Save(_ context.Context, page *entity.Page) error {
|
||||
if p.db.IsClosed() {
|
||||
return ErrDBClosed
|
||||
}
|
||||
|
||||
marshaled, err := marshal(site)
|
||||
marshaled, err := marshal(page)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal data: %w", err)
|
||||
}
|
||||
|
||||
if err := p.db.Update(func(txn *badger.Txn) error {
|
||||
if err := txn.Set(p.key(site), marshaled); err != nil {
|
||||
if err := txn.Set(p.key(page), marshaled); err != nil {
|
||||
return fmt.Errorf("put data: %w", err)
|
||||
}
|
||||
|
||||
@@ -151,6 +151,64 @@ func (p *Page) ListAll(ctx context.Context) ([]*entity.Page, error) {
|
||||
Formats: page.Formats,
|
||||
Version: page.Version,
|
||||
Status: page.Status,
|
||||
Meta: page.Meta,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("view: %w", err)
|
||||
}
|
||||
|
||||
sort.Slice(pages, func(i, j int) bool {
|
||||
return pages[i].Created.After(pages[j].Created)
|
||||
})
|
||||
|
||||
return pages, nil
|
||||
}
|
||||
|
||||
func (p *Page) ListUnprocessed(ctx context.Context) ([]*entity.Page, error) {
|
||||
pages := make([]*entity.Page, 0, 100)
|
||||
|
||||
err := p.db.View(func(txn *badger.Txn) error {
|
||||
iterator := txn.NewIterator(badger.DefaultIteratorOptions)
|
||||
|
||||
defer iterator.Close()
|
||||
|
||||
for iterator.Seek(p.prefix); iterator.ValidForPrefix(p.prefix); iterator.Next() {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return fmt.Errorf("context canceled: %w", err)
|
||||
}
|
||||
|
||||
var page entity.Page
|
||||
|
||||
err := iterator.Item().Value(func(val []byte) error {
|
||||
if err := unmarshal(val, &page); err != nil {
|
||||
return fmt.Errorf("unmarshal: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("get item: %w", err)
|
||||
}
|
||||
|
||||
if page.Status != entity.StatusProcessing {
|
||||
continue
|
||||
}
|
||||
|
||||
pages = append(pages, &entity.Page{
|
||||
ID: page.ID,
|
||||
URL: page.URL,
|
||||
Description: page.Description,
|
||||
Created: page.Created,
|
||||
Formats: page.Formats,
|
||||
Version: page.Version,
|
||||
Status: page.Status,
|
||||
Meta: page.Meta,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user