Files
orangesite/hn/handlers.go
sneak 3ab9637246 bring repo into policy compliance; vendor assets; Gitea CI
Vendor the front-end assets and drop the third-party CDN dependencies
(BootstrapCDN is being sunset): the bootstrap 4.0.0 css/js, jquery
3.2.1 slim, and popper 1.12.9 now live under static/ and are served
from the app, byte-for-byte identical to the previous SRI-pinned files.

Migrate CI from Drone to a Gitea Actions workflow that runs
docker build . on push, with the checkout action pinned by SHA.

Bring the repo up to standard:

- add REPO_POLICIES.md, .editorconfig, .dockerignore, .golangci.yml,
  and a comprehensive root-anchored .gitignore
- rewrite the Makefile with the required test/lint/fmt/fmt-check/check/
  docker/hooks targets (golangci-lint, 30s test timeout, verbose rerun
  on failure, check modifies nothing)
- rewrite the Dockerfile as a hash-pinned multistage build: a lint
  stage (golangci-lint), a glibc build+test stage (the legacy sqlite
  driver needs cgo+glibc), and a debian-slim runtime carrying the
  binary, templates, and static assets
- add real tests for the hn package
- bring the code into golangci-lint (default: all) compliance: fix the
  malformed gorm struct tags, check previously-ignored errors, dispatch
  the zerolog error event, avoid a uint->Duration overflow, split long
  functions, and add doc comments — all behaviour-preserving
- expand the README with the required sections
2026-07-26 23:55:21 +07:00

121 lines
3.1 KiB
Go

package hn
import (
"net/http"
"time"
u "git.eeqj.de/sneak/goutil"
"github.com/flosch/pongo2"
"github.com/jinzhu/gorm"
"github.com/labstack/echo"
)
// RequestHandlerSet holds the dependencies shared by the HTTP handlers.
type RequestHandlerSet struct {
db *gorm.DB
version string
}
// NewRequestHandlerSet builds a RequestHandlerSet for the given version and
// database handle.
func NewRequestHandlerSet(version string, db *gorm.DB) *RequestHandlerSet {
rhs := new(RequestHandlerSet)
rhs.db = db
rhs.version = version
return rhs
}
// exitRow is a story that has left the front page within the last 24h.
type exitRow struct {
Duration string
DurationSecs uint
URL string
Title string
HighestRank uint
HNID uint
Score uint
TimeGone string
TimeGoneSecs uint
}
// currentRow is a story currently on the front page.
type currentRow struct {
Duration string
DurationSecs uint
URL string
Title string
Score uint
HighestRank uint
HNID uint
Rank uint
}
// exitedRows returns the stories that left the front page in the last 24h,
// most recently departed first.
func (r *RequestHandlerSet) exitedRows() []exitRow {
last24h := time.Now().Add(time.Second * 86400 * -1)
var fpi []HNFrontPage
r.db.Where("disappeared is not ? and disappeared > ?", time.Time{}, last24h).Order("disappeared desc").Find(&fpi)
rows := make([]exitRow, 0, len(fpi))
for _, item := range fpi {
rows = append(rows, exitRow{
Duration: u.TimeDiffHuman(item.Disappeared, item.Appeared),
DurationSecs: u.TimeDiffAbsSeconds(item.Disappeared, item.Appeared),
URL: item.URL,
HNID: item.HNID,
Score: item.Score,
Title: item.Title,
HighestRank: item.HighestRank,
TimeGone: u.TimeDiffHuman(time.Now(), item.Disappeared),
TimeGoneSecs: u.TimeDiffAbsSeconds(time.Now(), item.Disappeared),
})
}
return rows
}
// currentRows returns the stories currently on the front page, best rank first.
func (r *RequestHandlerSet) currentRows() []currentRow {
var cur []HNFrontPage
r.db.Where("disappeared is ?", time.Time{}).Order("rank asc").Find(&cur)
rows := make([]currentRow, 0, len(cur))
for _, item := range cur {
rows = append(rows, currentRow{
Duration: u.TimeDiffHuman(time.Now(), item.Appeared),
DurationSecs: u.TimeDiffAbsSeconds(time.Now(), item.Appeared),
URL: item.URL,
HNID: item.HNID,
Score: item.Score,
Title: item.Title,
HighestRank: item.HighestRank,
Rank: item.Rank,
})
}
return rows
}
func (r *RequestHandlerSet) indexHandler(c echo.Context) error {
tc := pongo2.Context{
"time": time.Now().UTC().Format(time.RFC3339Nano),
"exits": r.exitedRows(),
"current": r.currentRows(),
"gitrev": r.version,
}
return c.Render(http.StatusOK, "index.html", tc)
}
func (r *RequestHandlerSet) aboutHandler(c echo.Context) error {
tc := pongo2.Context{
"time": time.Now().UTC().Format(time.RFC3339Nano),
"gitrev": r.version,
}
return c.Render(http.StatusOK, "about.html", tc)
}