35 Commits

Author SHA1 Message Date
Elara6331 81926a53f9 Update nomad template to use new api server image
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-16 12:43:38 -07:00
Elara6331 eb59764b75 Switch back to upstream drone-nomad image 2023-05-16 12:43:11 -07:00
Elara6331 8c023ab05c Add the ability to get badges for packages 2023-05-16 12:42:36 -07:00
Elara6331 b5672f2e05 Run npm audit fix
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-09 09:44:29 -07:00
Elara6331 30edd51714 Remove docker.sh
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-09 09:00:55 -07:00
Elara6331 58cf7a72bb Move api url setting to docker build arg
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-09 08:58:36 -07:00
Elara6331 d23da62dfc Use my fork of the drone-nomad image
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-05 15:11:21 +00:00
Elara6331 cb38468ddd Update videos
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-04 18:04:31 -07:00
Elara6331 fffc7a0b40 Update domain 2023-05-04 17:18:35 -07:00
Elara6331 ff8f176120 Provide github secret
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-04 15:14:01 -07:00
Elara6331 41d3d712e4 Set deploy to only happen on push to public
ci/woodpecker/push/woodpecker Pipeline failed
2023-05-04 15:11:44 -07:00
Elara6331 480c3ac20d Add nomad template
ci/woodpecker/push/woodpecker Pipeline failed
2023-05-04 15:10:09 -07:00
Elara6331 b717e3b131 Add woodpecker file
ci/woodpecker/push/woodpecker Pipeline failed
2023-05-04 14:59:56 -07:00
Elara6331 0f7c55a4d8 Update domain 2023-04-20 20:07:23 -07:00
Elara6331 4073538ee6 Merge pull request #4 from nxjosephofficial/master
Replace lure-bin with linux-user-repository-bin
2023-04-08 21:55:17 +00:00
nxjosephofficial d7a47f2963 Replace lure-bin with linux-user-repository-bin 2023-04-08 23:59:08 +03:00
Elara6331 3f3867da00 Provide SVG LURE icons 2022-12-25 22:02:17 -08:00
Elara6331 38d191fa53 Improve wording and add more information to the FAQ 2022-12-25 21:28:17 -08:00
Elara6331 bbe139f779 Fix page flickering 2022-12-25 19:29:30 -08:00
Elara6331 a0df472552 Switch to dark footer 2022-12-25 17:20:41 -08:00
Elara6331 8aafff96dc Add new footer 2022-12-25 17:10:35 -08:00
Elara6331 688cd83481 Convert text in header logo to path 2022-12-22 09:25:06 -08:00
Elara6331 4c4916159d Use new logo 2022-12-22 00:25:46 -08:00
Elara6331 e6d71b9b3f Use dynamic import as Vime does not work in SSR 2022-12-21 14:05:30 -08:00
Elara6331 321113aec5 Improve video quality 2022-12-21 13:39:27 -08:00
Elara6331 aac1fbfd1d Switch to video player instead of GIF 2022-12-21 13:26:39 -08:00
Elara6331 5a90de4ef5 Disable docker build cache 2022-12-21 11:36:39 -08:00
Elara6331 65e297d792 Don't provide SPDX link for custom licenses 2022-12-21 11:22:52 -08:00
Elara6331 42469e9ef8 Add meta description tags 2022-12-21 11:16:15 -08:00
Elara6331 f18b76424e Add title tags 2022-12-21 11:09:29 -08:00
Elara6331 18cf85ee9d Switch to AGPLv3 2022-12-20 22:24:30 -08:00
Elara6331 76d7bd8d6d Fix dockerfile 2022-12-20 14:24:22 -08:00
Elara6331 e3f7a5667c Add docker build script 2022-12-20 14:22:54 -08:00
Elara6331 ddec44c807 Add Dockerfile 2022-12-20 14:03:40 -08:00
Elara6331 cea2117b56 Initial Commit 2022-12-20 11:54:09 -08:00
62 changed files with 8895 additions and 1613 deletions
+13
View File
@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock
+20
View File
@@ -0,0 +1,20 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
plugins: ['svelte3', '@typescript-eslint'],
ignorePatterns: ['*.cjs'],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
settings: {
'svelte3/typescript': () => require('typescript')
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020
},
env: {
browser: true,
es2017: true,
node: true
}
};
+10 -1
View File
@@ -1 +1,10 @@
/lure-web .DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
+1
View File
@@ -0,0 +1 @@
engine-strict=true
+13
View File
@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock
+9
View File
@@ -0,0 +1,9 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}
+25 -14
View File
@@ -1,15 +1,26 @@
platform: linux/amd64 pipeline:
steps: publish:
docker: image: woodpeckerci/plugin-docker-buildx
image: gitea.elara.ws/elara6331/builder secrets: [docker_username, docker_password]
environment: settings:
- REGISTRY=gitea.elara.ws repo: elara6331/lure-web
- REGISTRY_USERNAME=Elara6331 dockerfile: docker/Dockerfile
- KO_DOCKER_REPO=gitea.elara.ws/lure platforms: linux/amd64,linux/arm64
- KO_DEFAULTBASEIMAGE=gitea.elara.ws/elara6331/static build_args: api_url=https://api.lure.elara.ws
secrets: [ registry_password ] tag: latest
commands:
- registry-login
- ko build -B --platform=linux/amd64,linux/arm64,linux/riscv64 -t latest,${CI_COMMIT_TAG} --sbom=none
when: when:
event: tag branch: public
event: push
deploy:
image: loq9/drone-nomad
secrets: [lure_api_github_secret]
settings:
addr: http://192.168.100.62:4646
template: template.nomad
environment:
- PLUGIN_WATCH_DEPLOYMENT=true
- PLUGIN_WATCH_DEPLOYMENT_TIMEOUT=10m
when:
branch: public
event: push
+24 -55
View File
@@ -1,69 +1,38 @@
# lure-web # create-svelte
`lure-web` is a website for LURE and a web interface that lets users search and get information about LURE's packages. Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
LURE Web is powered by [Salix](https://gitea.elara.ws/Elara6331/salix) and [PicoCSS](https://picocss.com/). ## Creating a project
## API If you're seeing this, you've probably already done this step. Congrats!
### Search Packages ```bash
# create a new project in the current directory
npm create svelte@latest
- **Endpoint:** `GET /api/search` # create a new project in my-app
- **Description:** Search for packages based on specified search options. npm create svelte@latest my-app
- **Parameters:** ```
- None
- **Query Parameters:**
- `sort` (optional) - Sort the results by a specific attribute.
- Possible values: "name", "version", "repo"
- `filter` (optional) - Apply a filter to narrow down the search results.
- Possible values: "inrepo", "arch"
- `fv` (optional) - Filter value to be used based on the selected filter.
- `q` (optional) - The search query string.
- **Response:**
- Content-Type: application/json
- Body: List of translated packages based on search criteria.
### Get Package Information ## Developing
- **Endpoint:** `GET /api/pkg/:repo/:pkg` Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
- **Description:** Get detailed information about a specific package.
- **Parameters:**
- `:repo` - Repository name
- `:pkg` - Package name
- **Response:**
- Content-Type: application/json
- Body: Translated information about the specified package.
### Get Package Script ```bash
npm run dev
- **Endpoint:** `GET /api/pkg/:repo/:pkg/script` # or start the server and open the app in a new browser tab
- **Description:** Get the script associated with a specific package. npm run dev -- --open
- **Parameters:** ```
- `:repo` - Repository name
- `:pkg` - Package name
- **Query Parameters:**
- `highlight` (optional) - If set to `true`, the script will be returned with syntax highlighting.
- **Response:**
- If `highlight` is `true`:
- Content-Type: text/html
- Body: Script with syntax highlighting in HTML format.
- If `highlight` is not provided or set to "false":
- Content-Type: text/x-shellscript
- Body: Raw script content.
### Language Translation ## Building
- **Header:** `Accept-Language` To create a production version of your app:
- Use the Accept-Language header to specify the preferred language for response translations.
- **Query Parameter:** `lang`
- You can also include a `lang` query parameter instead of `Accept-Language` to specify the translation language.
### Error Handling ```bash
npm run build
- **Response:** ```
- Content-Type: application/json
- Body: A JSON object with a single `error` string field containing an explanation of the error.
---
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
-73
View File
@@ -1,73 +0,0 @@
package main
import (
"io"
"net/http"
"github.com/rs/cors"
"github.com/uptrace/bunrouter"
"lure.sh/lure/pkg/loggerctx"
"lure.sh/lure/pkg/search"
"go.elara.ws/logger/log"
)
func registerAPI(mux *bunrouter.Router) {
g := mux.NewGroup(
"/api",
bunrouter.WithMiddleware(apiErrorHandler, corsMiddleware()),
)
g.GET("/*path", func(w http.ResponseWriter, req bunrouter.Request) error {
return HTTPError{404, "endpoint not found: " + req.URL.Path}
})
g.GET("/search", func(w http.ResponseWriter, req bunrouter.Request) error {
ctx := loggerctx.With(req.Context(), log.Logger)
pkgs, err := search.Search(ctx, searchOptions(req.Request))
if err != nil {
return err
}
return bunrouter.JSON(w, translatePkgs(getLanguages(req.Request), pkgs))
})
g.GET("/pkg/:repo/:pkg", func(w http.ResponseWriter, req bunrouter.Request) error {
ctx := loggerctx.With(req.Context(), log.Logger)
pkg, err := search.GetPkg(ctx, req.Param("repo"), req.Param("pkg"))
if err != nil {
return err
}
return bunrouter.JSON(w, translatePkg(getLanguages(req.Request), pkg))
})
g.GET("/pkg/:repo/:pkg/script", func(w http.ResponseWriter, req bunrouter.Request) error {
ctx := loggerctx.With(req.Context(), log.Logger)
script, err := search.GetScript(ctx, req.Param("repo"), req.Param("pkg"))
if err != nil {
return err
}
defer script.Close()
if req.URL.Query().Get("highlight") == "true" {
data, err := highlight(script)
if err != nil {
return err
}
w.Header().Set("Content-Type", "text/html")
_, err = io.WriteString(w, string(data))
return err
} else {
w.Header().Set("Content-Type", "text/x-shellscript")
_, err = io.Copy(w, script)
return err
}
})
}
func corsMiddleware() bunrouter.MiddlewareFunc {
ch := cors.AllowAll()
return func(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
return bunrouter.HTTPHandler(ch.Handler(next))
}
}
-89
View File
@@ -1,89 +0,0 @@
package main
import (
"context"
_ "embed"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"strings"
"github.com/uptrace/bunrouter"
"lure.sh/lure/pkg/search"
)
type pkgCtxKey struct{}
//go:embed static/icons/badge-logo.base64
var logoData string
// registerBadge registers the route for generating LURE badges
func registerBadge(mux *bunrouter.Router) {
// rp is a reverse proxy that will route user requests to shields.io
// and return the responses, stripped of any irrelevant headers.
rp := &httputil.ReverseProxy{
Rewrite: func(pr *httputil.ProxyRequest) {
pkg := pr.In.Context().Value(pkgCtxKey{}).(search.Package)
pr.Out.URL = genBadgeURL(pkg, pr.In.URL.Query().Get("style"))
pr.Out.Host = pr.Out.URL.Host
},
ModifyResponse: func(r *http.Response) error {
// Remove all the irrelevant headers from the response.
r.Header.Del("Alt-Svc")
r.Header.Del("Cache-Control")
r.Header.Del("Report-To")
r.Header.Del("Cf-Cache-Status")
r.Header.Del("Cf-Ray")
r.Header.Del("Fly-Request-Id")
r.Header.Del("Nel")
r.Header.Del("Report-To")
r.Header.Del("Server")
r.Header.Del("Via")
r.Header.Del("Set-Cookie")
return nil
},
}
mux.GET("/pkg/:repo/:pkg/badge.svg", func(w http.ResponseWriter, req bunrouter.Request) error {
repo := req.Param("repo")
name := req.Param("pkg")
pkg, err := search.GetPkg(req.Context(), repo, name)
if err != nil {
return err
}
ctx := context.WithValue(req.Context(), pkgCtxKey{}, pkg)
rp.ServeHTTP(w, req.Request.WithContext(ctx))
return nil
})
}
func genBadgeURL(pkg search.Package, style string) *url.URL {
v := url.Values{}
v.Set("label", pkg.Name)
v.Set("message", genBadgeVersion(pkg))
v.Set("logo", logoData)
v.Set("color", "0060A8")
if style != "" {
v.Set("style", style)
}
return &url.URL{Scheme: "https", Host: "img.shields.io", Path: "/static/v1", RawQuery: v.Encode()}
}
func genBadgeVersion(pkg search.Package) string {
sb := strings.Builder{}
if pkg.Epoch != 0 {
sb.WriteString(strconv.Itoa(int(pkg.Epoch)))
sb.WriteByte(':')
}
sb.WriteString(pkg.Version)
if pkg.Release != 0 {
sb.WriteByte('-')
sb.WriteString(strconv.Itoa(pkg.Release))
}
return sb.String()
}
+13
View File
@@ -0,0 +1,13 @@
FROM node:19-alpine
ARG api_url
ENV LURE_WEB_API_URL $api_url
RUN apk add git
RUN git clone https://gitea.elara.ws/Elara6331/lure-web
RUN cd lure-web && \
npm i && \
npm run build
WORKDIR /lure-web
ENTRYPOINT node build
-90
View File
@@ -1,90 +0,0 @@
package main
import (
"database/sql"
"errors"
"net/http"
"github.com/uptrace/bunrouter"
"go.elara.ws/logger/log"
"go.elara.ws/salix"
)
type HTTPError struct {
StatusCode int `json:"status_code"`
Msg string `json:"error"`
}
func (he HTTPError) Error() string {
return he.Msg
}
func (he HTTPError) StatusText() string {
return http.StatusText(he.StatusCode)
}
func httpError(err error) *HTTPError {
switch err := err.(type) {
case nil:
return nil
case HTTPError:
return &err
case *HTTPError:
return err
default:
switch {
case errors.Is(err, sql.ErrNoRows):
return &HTTPError{404, err.Error()}
default:
return &HTTPError{500, err.Error()}
}
}
}
func siteErrorHandler(ns *salix.Namespace) bunrouter.MiddlewareFunc {
return func(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
tmpl, ok := ns.GetTemplate("error.html")
if !ok {
return basicErrorHandler(next)
}
return func(w http.ResponseWriter, req bunrouter.Request) error {
err := next(w, req)
if err != nil {
he := httpError(err)
w.WriteHeader(he.StatusCode)
err := tmpl.
WithVarMap(map[string]any{"error": he}).
Execute(w)
if err != nil {
log.Error("Error while executing error template").Err(err).Send()
}
}
return err
}
}
}
func apiErrorHandler(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
return func(w http.ResponseWriter, req bunrouter.Request) error {
err := next(w, req)
if err != nil {
he := httpError(err)
w.WriteHeader(he.StatusCode)
_ = bunrouter.JSON(w, map[string]string{"error": he.Msg})
}
return err
}
}
func basicErrorHandler(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
return func(w http.ResponseWriter, req bunrouter.Request) error {
err := next(w, req)
if err != nil {
he := httpError(err)
http.Error(w, he.Msg, he.StatusCode)
}
return err
}
}
-68
View File
@@ -1,68 +0,0 @@
module lure.sh/lure-web
go 1.21.2
toolchain go1.21.6
require (
github.com/alecthomas/chroma/v2 v2.9.1
github.com/rs/cors v1.10.1
github.com/uptrace/bunrouter v1.0.21
go.elara.ws/logger v0.0.0-20230421022458-e80700db2090
go.elara.ws/salix v0.0.0-20240119074218-9bf56b50a461
golang.org/x/text v0.13.0
lure.sh/lure v0.1.4-0.20231223033536-5dc31f43aa39
)
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/go-git/go-git/v5 v5.9.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gookit/color v1.5.1 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/skeema/knownhosts v1.2.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/term v0.12.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.24.1 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.6.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/sqlite v1.25.0 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
mvdan.cc/sh/v3 v3.7.0 // indirect
)
-245
View File
@@ -1,245 +0,0 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/chroma/v2 v2.9.1 h1:0O3lTQh9FxazJ4BYE/MOi/vDGuHn7B+6Bu902N2UZvU=
github.com/alecthomas/chroma/v2 v2.9.1/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY=
github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.5.1 h1:Vjg2VEcdHpwq+oY63s/ksHrgJYCTo0bwWvmmYWdE9fQ=
github.com/gookit/color v1.5.1/go.mod h1:wZFzea4X8qN6vHOSP2apMb4/+w/orMznEzYsIHPaqKM=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/uptrace/bunrouter v1.0.21 h1:HXarvX+N834sXyHpl+I/TuE11m19kLW/qG5u3YpHUag=
github.com/uptrace/bunrouter v1.0.21/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.elara.ws/logger v0.0.0-20230421022458-e80700db2090 h1:RVC8XvWo6Yw4HUshqx4TSzuBDScDghafU6QFRJ4xPZg=
go.elara.ws/logger v0.0.0-20230421022458-e80700db2090/go.mod h1:qng49owViqsW5Aey93lwBXONw20oGbJIoLVscB16mPM=
go.elara.ws/salix v0.0.0-20240119074218-9bf56b50a461 h1:vVyRue86HMRBEguyDUoarLEQjVKQ/r8x6kX3qGgvVkM=
go.elara.ws/salix v0.0.0-20240119074218-9bf56b50a461/go.mod h1:niWia13iw7qDrS1C1mlqv5hxO1sunt8CcOQAB5yVlNU=
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 h1:Ep54XceQlKhcCHl9awG+wWP4kz4kIP3c3Lzw/Gc/zwY=
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4/go.mod h1:/7PNW7nFnDR5W7UXZVc04gdVLR/wBNgkm33KgIz0OBk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee h1:kSXIuMid56Q29WEl7EQb5QUtmGqQqAu66EZ2G0OSUfU=
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee/go.mod h1:/v0u0AZ+wbzUWhV02KzciOf1KFNh7/7rbkz5Z0b5gDA=
lure.sh/lure v0.1.4-0.20231223033536-5dc31f43aa39 h1:9Rw9+Pmmkbjxiq0eREq4RJPn+ggL1jYFpgrgOkgQ9YQ=
lure.sh/lure v0.1.4-0.20231223033536-5dc31f43aa39/go.mod h1:h1+OIDD39zDosjThxQ9szoWW9Z1lGcm7M6eeo6v0OJY=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg=
mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8=
-49
View File
@@ -1,49 +0,0 @@
package main
import (
"context"
"embed"
"net/http"
"os"
"github.com/uptrace/bunrouter"
"go.elara.ws/logger"
"go.elara.ws/logger/log"
"lure.sh/lure/pkg/loggerctx"
"lure.sh/lure/pkg/repos"
)
//go:embed static
var static embed.FS
func init() {
log.Logger = logger.NewPretty(os.Stderr)
}
func main() {
mux := bunrouter.New()
fileServer := http.FileServer(http.FS(static))
mux.Use(cache).GET("/static/*path", bunrouter.HTTPHandler(fileServer))
ctx := loggerctx.With(context.Background(), log.Logger)
err := repos.Pull(ctx, nil)
if err != nil {
log.Fatal("Error pulling repos").Err(err).Send()
}
registerBadge(mux)
registerSite(mux)
registerAPI(mux)
registerWebhook(ctx, mux)
http.ListenAndServe(":8080", mux)
}
func cache(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
return bunrouter.HandlerFunc(func(w http.ResponseWriter, req bunrouter.Request) error {
w.Header().Set("Cache-Control", "max-age=86400, stale-while-revalidate=86400")
return next(w, req)
})
}
+5591
View File
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
{
"name": "lure-web",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@iconify/svelte": "^3.0.1",
"@sveltejs/adapter-auto": "^1.0.0",
"@sveltejs/adapter-node": "^1.0.0",
"@sveltejs/kit": "^1.0.0",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"@vime/core": "^5.4.0",
"@vime/svelte": "^5.4.0",
"bulma": "^0.9.4",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte3": "^4.0.0",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.8.1",
"sass": "^1.53.0",
"svelte": "^3.54.0",
"svelte-carousel": "^1.0.20",
"svelte-check": "^2.9.2",
"svelte-highlight": "^6.2.1",
"svelte-loading-spinners": "^0.3.4",
"svelte-preprocess": "^4.10.7",
"tslib": "^2.4.1",
"typescript": "^4.9.3",
"vite": "^4.0.0"
},
"type": "module",
"dependencies": {
"protoscript": "^0.0.14",
"twirpscript": "^0.0.66"
}
}
-111
View File
@@ -1,111 +0,0 @@
package main
import (
"fmt"
"net/http"
"golang.org/x/text/language"
"lure.sh/lure/pkg/search"
)
type Package struct {
Name string `json:"name"`
Repository string `json:"repository"`
Version string `json:"version"`
Release int `json:"release"`
Epoch uint `json:"epoch"`
Description string `json:"description"`
Homepage string `json:"homepage"`
Maintainer string `json:"maintainer"`
Architectures []string `json:"architectures"`
Licenses []string `json:"licenses"`
Provides []string `json:"provides"`
Conflicts []string `json:"conflicts"`
Replaces []string `json:"replaces"`
Depends map[string][]string `json:"depends"`
BuildDepends map[string][]string `json:"build_depends"`
}
func (p Package) FullVersion() string {
ver := fmt.Sprintf("%s-%d", p.Version, p.Release)
if p.Epoch != 0 {
ver = fmt.Sprintf("%d:%s", p.Epoch, ver)
}
return ver
}
func translatePkgs(langs []string, pkgs []search.Package) []Package {
out := make([]Package, len(pkgs))
for i, pkg := range pkgs {
out[i] = translatePkg(langs, pkg)
}
return out
}
func translatePkg(langs []string, pkg search.Package) Package {
return Package{
Name: pkg.Name,
Repository: pkg.Repository,
Version: pkg.Version,
Release: pkg.Release,
Epoch: pkg.Epoch,
Description: performTranslation(langs, pkg.Description),
Homepage: performTranslation(langs, pkg.Homepage),
Maintainer: performTranslation(langs, pkg.Maintainer),
Architectures: pkg.Architectures,
Licenses: pkg.Licenses,
Provides: pkg.Provides,
Conflicts: pkg.Conflicts,
Depends: pkg.Depends,
BuildDepends: pkg.BuildDepends,
}
}
func getLanguages(req *http.Request) []string {
al := req.Header.Get("Accept-Language")
lang := req.URL.Query().Get("lang")
if al == "" && lang == "" {
return nil
}
tags, _, _ := language.ParseAcceptLanguage(al)
if lang != "" {
langTag, err := language.Parse(lang)
if err == nil {
tags = append(tags, langTag)
}
}
return getLangBases(tags)
}
func getLangBases(langs []language.Tag) []string {
out := make([]string, len(langs)+1)
for i, lang := range langs {
base, _ := lang.Base()
out[i] = base.String()
}
return out
}
func performTranslation(langs []string, v map[string]string) string {
if len(langs) == 0 {
val, ok := v[""]
if !ok {
return "<unknown>"
}
return val
}
for _, name := range langs {
val, ok := v[name]
if !ok {
continue
}
return val
}
return "<unknown>"
}
-193
View File
@@ -1,193 +0,0 @@
package main
import (
"embed"
"io"
"io/fs"
"net/http"
"strings"
"time"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
"github.com/uptrace/bunrouter"
"go.elara.ws/logger/log"
"go.elara.ws/salix"
"lure.sh/lure/pkg/loggerctx"
"lure.sh/lure/pkg/search"
)
//go:embed tmpls
var tmpls embed.FS
func registerSite(mux *bunrouter.Router) {
tmplFS, err := fs.Sub(tmpls, "tmpls")
if err != nil {
panic(err)
}
ns := salix.New().
WithEscapeHTML(true).
WithWriteOnSuccess(true).
WithVarMap(map[string]any{
"now": time.Now,
})
err = ns.ParseFSGlob(tmplFS, "*.html")
if err != nil {
panic(err)
}
g := mux.WithMiddleware(siteErrorHandler(ns))
g.GET("/*path", func(w http.ResponseWriter, req bunrouter.Request) error {
if req.Param("path") == "" {
return ns.ExecuteTemplate(w, "home.html", nil)
}
return HTTPError{404, "page not found: " + req.URL.Path}
})
g.GET("/about", func(w http.ResponseWriter, req bunrouter.Request) error {
return ns.ExecuteTemplate(w, "about.html", nil)
})
g.GET("/icons", func(w http.ResponseWriter, req bunrouter.Request) error {
return ns.ExecuteTemplate(w, "icons.html", nil)
})
g.GET("/pkgs", func(w http.ResponseWriter, req bunrouter.Request) error {
ctx := loggerctx.With(req.Context(), log.Logger)
pkgs, err := search.Search(ctx, searchOptions(req.Request))
if err != nil {
return err
}
return ns.ExecuteTemplate(w, "pkgs.html", map[string]any{
"pkgs": translatePkgs(getLanguages(req.Request), pkgs),
"query": req.URL.Query(),
})
})
g.GET("/pkg/:repo/:pkg", func(w http.ResponseWriter, req bunrouter.Request) error {
ctx := loggerctx.With(req.Context(), log.Logger)
pkg, err := search.GetPkg(ctx, req.Param("repo"), req.Param("pkg"))
if err != nil {
return err
}
return ns.ExecuteTemplate(w, "pkg.html", map[string]any{
"url": getURL(req.Request),
"pkg": translatePkg(getLanguages(req.Request), pkg),
})
})
g.GET("/pkg/:repo/:pkg/script", func(w http.ResponseWriter, req bunrouter.Request) error {
repoName := req.Param("repo")
pkgName := req.Param("pkg")
ctx := loggerctx.With(req.Context(), log.Logger)
script, err := search.GetScript(ctx, repoName, pkgName)
if err != nil {
return err
}
defer script.Close()
data, err := highlight(script)
if err != nil {
return err
}
return ns.ExecuteTemplate(w, "script.html", map[string]any{
"repoName": repoName,
"pkgName": pkgName,
"script": data,
})
})
}
// searchOptions gets a search.Options struct from the request's
// query parameters.
func searchOptions(r *http.Request) search.Options {
sort := search.SortBy(search.SortByNone)
switch r.URL.Query().Get("sort") {
case "name":
sort = search.SortByName
case "version":
sort = search.SortByVersion
case "repo":
sort = search.SortByRepo
}
filter := search.FilterNone
switch r.URL.Query().Get("filter") {
case "inrepo":
filter = search.FilterInRepo
case "arch":
filter = search.FilterSupportsArch
}
return search.Options{
Filter: filter,
FilterValue: r.URL.Query().Get("fv"),
SortBy: sort,
Query: r.URL.Query().Get("q"),
}
}
// highlight returns an HTML string with syntax highlighting
// for the contents of the given reader.
func highlight(r io.Reader) (salix.HTML, error) {
data, err := io.ReadAll(r)
if err != nil {
panic(err)
}
lexer := lexers.Get("bash")
if lexer == nil {
lexer = lexers.Fallback
}
lexer = chroma.Coalesce(lexer)
style := styles.Get("modus-vivendi")
formatter := html.New()
iterator, err := lexer.Tokenise(nil, string(data))
if err != nil {
return "", err
}
out := &strings.Builder{}
err = formatter.Format(out, style, iterator)
if err != nil {
return "", err
}
return salix.HTML(out.String()), nil
}
// getURL attempts to get the current URL of the request
func getURL(r *http.Request) string {
u := *r.URL
xfp := r.Header.Get("X-Forwarded-Proto")
if xfp == "" {
if r.TLS == nil {
u.Scheme = "http"
} else {
u.Scheme = "https"
}
} else {
u.Scheme = xfp
}
xfh := r.Header.Get("X-Forwarded-Host")
if xfh == "" {
if r.URL.Host == "" {
u.Host = r.Host
}
} else {
u.Host = xfh
}
return u.String()
}
+9
View File
@@ -0,0 +1,9 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@vime/core@^5/themes/default.css" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div id="page-container">%sveltekit.body%</div>
</body>
</html>
+63
View File
@@ -0,0 +1,63 @@
/* Write your global styles here, in SCSS syntax. Variables and mixins from the src/variables.scss file are available here without importing */ /* Import only what you need from Bulma */
@import 'bulma/sass/utilities/_all';
@import 'bulma/sass/base/_all';
@import 'bulma/sass/elements/_all';
@import 'bulma/sass/form/_all';
@import 'bulma/sass/components/_all';
@import 'bulma/sass/grid/_all';
@import 'bulma/sass/helpers/_all';
@import 'bulma/sass/layout/_all';
@import './darkly/overrides.scss';
.input::placeholder {
color: darken($grey-light, 10) !important;
opacity: 1;
}
.input::-webkit-input-placeholder {
color: darken($grey-light, 10) !important;
opacity: 1;
}
.input::-moz-placeholder {
color: darken($grey-light, 10) !important;
opacity: 1;
}
.input::-ms-input-placeholder {
color: darken($grey-light, 10) !important;
opacity: 1;
}
pre {
background-color: transparent;
}
.terminal-player {
max-width: 735px;
max-height: 450px;
}
.sc-carousel__carousel-container {
--sc-arrow-size: 3px;
}
button.sc-carousel-arrow__circle {
width: 30px !important;
height: 30px !important;
}
body {
margin: 0;
}
#page-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
#page-container > * {
width: 100%
}
+291
View File
@@ -0,0 +1,291 @@
////////////////////////////////////////////////
// DARKLY
////////////////////////////////////////////////
// Overrides
hr {
height: $border-width;
}
h6 {
text-transform: uppercase;
letter-spacing: 0.5px;
}
.hero {
background-color: $grey-dark;
}
a {
transition: all 200ms ease;
}
.button {
transition: all 200ms ease;
border-width: $border-width;
color: $white;
&.is-active,
&.is-focused,
&:active,
&:focus {
box-shadow: 0 0 0 2px rgba($button-focus-border-color, 0.5);
}
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
&.is-hovered,
&:hover {
background-color: lighten($color, 7.5%);
}
&.is-active,
&.is-focused,
&:active,
&:focus {
border-color: $color;
box-shadow: 0 0 0 2px rgba($color, 0.5);
}
}
}
}
.label {
color: $grey-lighter;
}
.input,
.textarea {
transition: all 200ms ease;
box-shadow: none;
border-width: $border-width;
padding-left: 1em;
padding-right: 1em;
background-color: $body-background-color;
color: $text
}
.select {
&:after,
select {
border-width: $border-width;
background-color: $body-background-color;
color: $text
}
}
.control {
&.has-addons {
.button,
.input,
.select {
margin-right: -$border-width;
}
}
}
.notification {
background-color: $grey-dark;
}
.card {
$card-border-color: lighten($grey-darker, 5);
box-shadow: none;
border: $border-width solid $card-border-color;
background-color: $grey-darker;
border-radius: $radius;
.card-image {
img {
border-radius: $radius $radius 0 0;
}
}
.card-header {
box-shadow: none;
background-color: rgba($black-bis, 0.2);
border-radius: $radius $radius 0 0;
}
.card-footer {
background-color: rgba($black-bis, 0.2);
}
.card-footer,
.card-footer-item {
border-width: $border-width;
border-color: $card-border-color;
}
}
.notification {
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
a:not(.button) {
color: $color-invert;
text-decoration: underline;
}
}
}
}
.tag {
border-radius: $radius;
}
.menu-list {
a {
transition: all 300ms ease;
}
}
.modal-card-body {
background-color: $grey-darker;
}
.modal-card-foot,
.modal-card-head {
border-color: $grey-dark;
}
.message-header {
font-weight: $weight-bold;
background-color: $grey-dark;
color: $white;
}
.message-body {
border-width: $border-width;
border-color: $grey-dark;
}
.navbar {
border-radius: $radius;
&.is-transparent {
background: none;
}
&.is-primary {
.navbar-dropdown {
a.navbar-item.is-active {
background-color: $link;
}
}
}
@include until($navbar-breakpoint) {
.navbar-menu {
background-color: $navbar-background-color;
border-radius: 0 0 $radius $radius;
}
}
}
.hero .navbar,
body > .navbar {
border-radius: 0;
}
.pagination-link,
.pagination-next,
.pagination-previous {
border-width: $border-width;
}
.panel-block,
.panel-heading,
.panel-tabs {
border-width: $border-width;
&:first-child {
border-top-width: $border-width;
}
}
.panel-heading {
font-weight: $weight-bold;
}
.panel-tabs {
a {
border-width: $border-width;
margin-bottom: -$border-width;
&.is-active {
border-bottom-color: $link-active;
}
}
}
.panel-block {
&:hover {
color: $link-hover;
.panel-icon {
color: $link-hover;
}
}
&.is-active {
.panel-icon {
color: $link-active;
}
}
}
.tabs {
a {
border-bottom-width: $border-width;
margin-bottom: -$border-width;
}
ul {
border-bottom-width: $border-width;
}
&.is-boxed {
a {
border-width: $border-width;
}
li.is-active a {
background-color: darken($grey-darker, 4);
}
}
&.is-toggle {
li a {
border-width: $border-width;
margin-bottom: 0;
}
li + li {
margin-left: -$border-width;
}
}
}
.hero {
// Colors
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
.navbar {
.navbar-dropdown {
.navbar-item:hover {
background-color: $navbar-dropdown-item-hover-background-color;
}
}
}
}
}
}
+121
View File
@@ -0,0 +1,121 @@
////////////////////////////////////////////////
// DARKLY
////////////////////////////////////////////////
// Variables
$grey-lighter: #dbdee0;
$grey-light: #8c9b9d;
$grey: darken($grey-light, 18);
$grey-dark: darken($grey, 18);
$grey-darker: darken($grey, 23);
$white: #FFF;
$orange: #e67e22;
$yellow: #f1b70e;
$green: #2ecc71;
$turquoise: #1abc9c;
$blue: #3498db;
$purple: #8e44ad;
$red: #e74c3c;
$white-ter: #ecf0f1;
$primary: #375a7f !default;
$yellow-invert: #fff;
$family-sans-serif: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI",
"Helvetica Neue", "Helvetica", "Arial", sans-serif;
$family-monospace: "Inconsolata", "Consolas", "Monaco", monospace;
$radius-small: 3px;
$radius: 0.4em;
$radius-large: 8px;
$size-6: 15px;
$size-7: 0.85em;
$title-weight: 500;
$subtitle-weight: 400;
$subtitle-color: $grey-dark;
$border-width: 2px;
$border: $grey;
$body-background-color: darken($grey-darker, 4);
$body-size: 15px;
$background: $grey-darker;
$footer-background-color: $background;
$button-background-color: $background;
$button-border-color: lighten($button-background-color, 15);
$title-color: #fff;
$subtitle-color: $grey-light;
$subtitle-strong-color: $grey-light;
$text: #fff;
$text-light: lighten($text, 10);
$text-strong: darken($text, 5);
$box-color: $text;
$box-background-color: $grey-dark;
$box-shadow: none;
$link: $turquoise;
$link-hover: lighten($link, 5);
$link-focus: darken($link, 5);
$link-active: darken($link, 5);
$link-focus-border: $grey-light;
$button-color: $primary;
$button-hover-color: darken($text, 5); // text-dark
$button-focus: darken($text, 5); // text-dark
$button-active-color: darken($text, 5); // text-dark
$button-disabled-background-color: $grey-light;
$button-focus-border-color: $link-focus-border;
$control-height: 2.5em;
$input-color: $grey-darker;
$input-icon-color: $grey;
$input-icon-active-color: $input-color;
$input-hover-color: $grey-light;
$input-disabled-background-color: $grey-light;
$input-disabled-border: $grey-lighter;
$table-color: $text;
$table-head: $grey-lighter;
$table-background-color: $grey-dark;
$table-cell-border: 1px solid $grey;
$table-row-hover-background-color: $grey-darker;
$table-striped-row-even-background-color: $grey-darker;
$table-striped-row-even-hover-background-color: lighten($grey-darker, 2);
$pagination-color: $link;
$pagination-border-color: $border;
$navbar-height: 4rem;
$navbar-background-color: $primary;
$navbar-item-color: $text;
$navbar-item-hover-color: $link;
$navbar-item-hover-background-color: transparent;
$navbar-item-active-color: $link;
$navbar-dropdown-arrow: #fff;
$navbar-divider-background-color: rgba(0, 0, 0, 0.2);
$navbar-dropdown-border-top: 1px solid $navbar-divider-background-color;
$navbar-dropdown-background-color: $primary;
$navbar-dropdown-item-hover-color: $grey-lighter;
$navbar-dropdown-item-hover-background-color: transparent;
$navbar-dropdown-item-active-background-color: transparent;
$navbar-dropdown-item-active-color: $link;
$dropdown-content-background-color: $background;
$dropdown-item-color: $text;
$progress-value-background-color: $grey-lighter;
$bulmaswatch-import-font: true !default;
$file-cta-background-color: $grey-darker;
$progress-bar-background-color: $grey-dark;
$panel-heading-background-color: $grey-dark;
+1830
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
<script>
import '../app.scss';
</script>
<slot />
+113
View File
@@ -0,0 +1,113 @@
<script>
import { onMount } from 'svelte';
import Header from './header.svelte';
import Footer from './footer.svelte';
import Highlight from 'svelte-highlight';
import bash from 'svelte-highlight/languages/bash';
import agate from 'svelte-highlight/styles/agate';
let Carousel, Player, Video, DefaultUi;
onMount(async () => {
// Perform dynamic import of svelte-carousel because it breaks
// if imported during SSR
Carousel = (await import('svelte-carousel')).default;
({ Player, Video, DefaultUi } = await import('@vime/svelte'));
});
let images = [
{
path: '/lure-arch.webm',
caption: 'Recorded on Arch Linux with LURE commit f8af758'
},
{
path: '/lure-debian.webm',
caption: 'Recorded on Debian 11 with LURE commit f8af758'
},
{
path: '/lure-fedora.webm',
caption: 'Recorded on Fedora 37 with LURE commit f8af758'
},
{
path: '/lure-alpine.webm',
caption: 'Recorded on Alpine Linux 3.17 with commit f8af758'
}
];
</script>
<svelte:head>
<title>Home | LURE Web</title>
<meta name="description" content="LURE Web home page" />
{@html agate}
</svelte:head>
<Header />
<section class="container">
<p class="title">LURE</p>
<p class="subtitle">The user repo missing from most Linux distros</p>
<hr />
<div class="columns">
<div class="column">
<div class="card">
<div class="card-header">
<p class="card-header-title">Why should I use it?</p>
</div>
<div class="card-content">
LURE allows users to install software that may not be widely distributed through official
repositories, while still maintaining the convenience of installation through repository
sources. This includes features such as updates and simple uninstallation. Additionally,
LURE provides developers with a central location for all their users to use to install
their software.
</div>
</div>
</div>
<div class="column">
<div class="card">
<div class="card-header">
<p class="card-header-title">How does it work?</p>
</div>
<div class="card-content">
LURE operates by abstracting package formats and package managers, enabling the creation
and installation of native packages automatically built from PKGBUILD-like bash scripts,
using the package manager already present on the system. As a result, packages installed
through LURE can be managed like any other package, without the need for additional
intervention from LURE for most operations.
</div>
</div>
</div>
</div>
<p class="title">Installation</p>
<p>LURE can easily be installed by running its install script:</p>
<Highlight language={bash} code="curl https://www.elara.ws/lure.sh | bash" />
<p>
It's also available on the AUR as <a
href="https://aur.archlinux.org/packages/linux-user-repository-bin"
><code>linux-user-repository-bin</code></a
>
and distro packages are provided at the
<a href="https://gitea.elara.ws/Elara6331/lure/releases/latest">latest Gitea release</a>.
</p>
<br />
<p class="title">Examples</p>
<!-- Use dynamically imported Carousel module -->
<svelte:component this={Carousel}>
{#each images as image}
<div class="has-text-centered">
<figure class="terminal-player mx-auto">
<svelte:component this={Player}>
<svelte:component this={Video}>
<source data-src={image.path} type="video/webm" />
</svelte:component>
<svelte:component this={DefaultUi} />
</svelte:component>
<figcaption>{image.caption ?? ''}</figcaption>
</figure>
</div>
{/each}
</svelte:component>
</section>
<Footer />
+81
View File
@@ -0,0 +1,81 @@
<script>
import Header from "../header.svelte";
import Footer from "../footer.svelte";
</script>
<svelte:head>
<title>FAQ | LURE Web</title>
<meta name="description" content="Frequently Asked Questions">
</svelte:head>
<Header/>
<div class="container">
<p class="title">FAQ</p>
<br>
<p class="subtitle" id='distro-support'>Why isn't my distro supported?</p>
<p>
To support a distribution, LURE must be able to communicate with its package manager and create packages for it.
Communicating with the package manager is relatively straightforward, as LURE simply needs to be provided with the necessary
commands. However, the package formats are more complex. LURE uses <a href="https://github.com/goreleaser/nfpm">nFPM</a> to
handle package formats, and nFPM currently supports only deb, rpm, apk, and archlinux package formats. When developing LURE,
support for archlinux packages was not available in nFPM, so I added it through a pull request. Despite the simplicity of the
archlinux package format, implementing support required over 1,000 lines of code. As a result, supporting a distribution with
a different package format, such as xbps for Void Linux, is very complex and time-consuming.
</p>
<hr>
<p class="subtitle" id='flatpak-snap-appimage'>Why use LURE instead of Flatpak, Snap, or AppImage?</p>
<p>
LURE is not intended to address the same issues as Flatpak, Snap, and AppImage. These are containerized package formats that enable
the creation of a single package that can be used on all distributions. This cross-platform package contains the program and everything
else necessary for it to run, and it relies on containers to achieve this compatibility. However, containers can sometimes cause programs
to start slowly, fail to adhere to system settings, or be unable to access certain parts of the system. If you need most programs to
function consistently or you are using an older distribution with outdated packages, containerized formats may be the best choice. In contrast,
LURE does not use containers. It builds the program from source and installs it automatically. It also does not have its own package format.
Instead, it uses the same format as the distribution it is running on, so LURE packages behave like the distribution's native packages.
This means that unlike Snap and Flatpak, LURE is not a package manager; it simply uses the distribution's package manager, allowing you to
manage the packages installed through LURE even when LURE is not installed.
</p>
<br>
<p>
However, LURE also has some drawbacks. Since it builds programs from source, certain packages, particularly git packages that
retrieve the latest code from git, may not work on older distributions or distributions like Debian that have outdated packages.
Please consider your specific needs and whether these downsides are acceptable before using LURE. Additionally, similar to the AUR,
all packages are user-submitted and not vetted, so while it is unlikely, they may contain malicious code. It is the responsibility
of the user to review the build script to ensure this is not the case. If you come across a malicious package, please report it by
opening an issue on the git repository containing it.
</p>
<hr>
<p class="subtitle" id="handling-dependencies">How does LURE handle dependencies across distros?</p>
<p>
LURE manages dependencies across distributions by offering distro overrides, in which package maintainers can specify different
variables and functions for each distribution. The most specific override is given precedence. After the overrides are resolved,
LURE compares the resulting list of dependencies with the packages installed on the system and filters out any that are already
installed. For the remaining dependencies, LURE checks its own repositories to see if each package is available there. If it is,
LURE installs it from its repositories. If the package is not found in any repository, LURE passes the dependency on to the package
manager, which handles dependency resolution and installation.
</p>
<hr>
<p class="subtitle" id="testing">How can one test a LURE package to ensure it works?</p>
<p>
Docker is recommended for testing LURE packages on different distros. It provides a clean image of any distribution, which is very
useful for testing as it can help catch issues that might not manifest themselves on your system. Eventually, an automated
docker-based testing tool is planned, but in the meantime, this will need to be done manually for each distribution you're planning
to support. To find package names for each distribution, you can use <a href="https://repology.org">repology.org</a> and
<a href="https://pkgs.org">pkgs.org</a>. These websites maintain comprehensive databases of package repositories for various distributions.
</p>
<hr>
<p class="subtitle" id="icons">Can I use and modify LURE's icons?</p>
<p>
LURE's icons are available on the <a href="/icons">icons page</a> of this website and are licensed under CC-BY-NC-SA 4.0. This means that
you are free to share, modify, and use the icons for non-commercial purposes as long as you give appropriate credit and indicate any changes
made to the original icons.
</p>
<hr>
<p class="subtitle" id='adding-packages'>How do I add my own package to LURE?</p>
<p>
To add your own package, please refer to the <a href="https://github.com/Elara6331/lure/blob/master/docs/packages">package documentation</a> provided by LURE.
</p>
</div>
<Footer/>
+15
View File
@@ -0,0 +1,15 @@
<script>
import Icon from '@iconify/svelte';
</script>
<br style="margin-top: 20px">
<div class="hero is-dark is-small">
<div class="hero-body has-text-centered">
<p>Copyright &copy; {new Date().getFullYear()} LURE Web Contributors. Licensed under the <a class="has-text-link" href="https://www.gnu.org/licenses/agpl-3.0-standalone.html">AGPLv3</a>.</p>
<div class="is-size-4">
<a href="https://gitea.elara.ws/Elara6331/lure-web"><Icon icon="cib:gitea"/></a>
<a href="https://github.com/Elara6331/lure-web"><Icon icon="mdi:github"/></a>
<a href="https://reddit.com/r/linux_user_repository"><Icon icon="ic:round-reddit"/></a>
</div>
</div>
</div>
+30
View File
@@ -0,0 +1,30 @@
<script>
import { page } from '$app/stores';
import Icon from '@iconify/svelte';
function isActive(path) {
return $page.route.id == path ? 'is-active' : ''
}
let navbarIsActive = false;
</script>
<nav class="navbar mb-5">
<div class="navbar-brand">
<a class="navbar-item" href="/"><img src="/lure-text-white.svg" alt="LURE Logo"></a>
<button class="navbar-burger {navbarIsActive ? 'is-active' : ''}" aria-label="menu" on:click={() => {navbarIsActive = !navbarIsActive}}>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</button>
</div>
<div id="main-navbar" class="navbar-menu {navbarIsActive ? 'is-active' : ''}">
<div class="navbar-end">
<a class="navbar-item {isActive('/')}" href="/"><Icon icon="material-symbols:house" inline=true/> Home</a>
<a class="navbar-item {isActive('/pkgs')}" href="/pkgs"><Icon icon="mdi:package-variant-closed" inline=true/>&nbsp;Packages</a>
<a class="navbar-item {isActive('/faq')}" href="/faq"><Icon icon="mdi:question-mark-circle" inline=true/>&nbsp;FAQ</a>
</div>
</div>
</nav>
+72
View File
@@ -0,0 +1,72 @@
<script>
import Header from "../header.svelte";
import Footer from "../footer.svelte";
</script>
<svelte:head>
<title>FAQ | LURE Web</title>
<meta name="description" content="LURE Icons">
</svelte:head>
<Header/>
<div class="container">
<p class="title">Icons</p>
<br>
<div class="columns">
<div class="column ">
<div class="card">
<div class="card-header">
<p class="card-header-title">
Without Text
</p>
</div>
<div class="card-content has-text-centered">
<figure class="image is-16by9">
<img src="/lure-no-text.svg" alt="LURE icon without text">
</figure>
</div>
<footer class="card-footer">
<a class="card-footer-item" href="/lure-no-text.svg" download>Download</a>
</footer>
</div>
</div>
<div class="column ">
<div class="card">
<div class="card-header">
<p class="card-header-title">
With Text
</p>
</div>
<div class="card-content has-text-centered">
<figure class="image is-16by9">
<img src="/lure-text.svg" alt="LURE icon with text">
</figure>
</div>
<footer class="card-footer">
<a class="card-footer-item" href="/lure-text.svg" download>Download</a>
</footer>
</div>
</div>
<div class="column">
<div class="card">
<div class="card-header">
<p class="card-header-title">
White With Text
</p>
</div>
<div class="card-content has-text-centered">
<figure class="image is-16by9">
<img src="/lure-text-white.svg" alt="White LURE icon with white text">
</figure>
</div>
<footer class="card-footer">
<a class="card-footer-item" href="/lure-text-white.svg" download>Download</a>
</footer>
</div>
</div>
</div>
<p class="has-text-weight-bold">Note: The icons on this page are licensed under CC-BY-NC-SA 4.0 unless otherwise specified</p>
</div>
<Footer/>
+170
View File
@@ -0,0 +1,170 @@
<script>
import { page } from '$app/stores';
import { client } from 'twirpscript';
import { GetPkg } from '$lib/lure.pb';
import { LURE_WEB_API_URL } from '$env/static/public';
import { DoubleBounce } from 'svelte-loading-spinners';
import Header from '../../../header.svelte';
import Footer from '../../../footer.svelte';
import Icon from '@iconify/svelte';
import { onMount } from 'svelte';
client.baseURL = LURE_WEB_API_URL;
client.prefix = '';
let currentURL = ``;
onMount(() => (currentURL = window.location.href));
function fullVer(pkg) {
let ver = `${pkg.version}-${pkg.release}`;
if (pkg.epoch != 0) {
ver = `${pkg.epoch}:${ver}`;
}
return ver;
}
function objToMap(o) {
return new Map(Object.entries(o));
}
let modalActive = false;
function showModal() {
modalActive = true;
}
function hideModal() {
modalActive = false;
}
</script>
<svelte:head>
<title>{$page.params.name} Package | LURE Web</title>
<meta name="description" content="Information about the {$page.params.name} LURE package." />
</svelte:head>
<Header />
<section class="container">
<a href="/pkgs"><Icon icon="material-symbols:arrow-back-rounded" inline="true" /> Back</a>
{#await GetPkg({ name: $page.params.name, repository: $page.params.repo })}
<center><DoubleBounce color="#375a7f" /></center>
{:then pkg}
<div class="modal {modalActive ? 'is-active' : ''}">
<div class="modal-background"/>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Badge</p>
<button class="delete" aria-label="close" on:click={hideModal}/>
</header>
<section class="modal-card-body">
<img
src="{LURE_WEB_API_URL}/badge/{$page.params.repo}/{$page.params.name}"
alt="badge for {$page.params.name} package"
/>
<p>Markdown</p>
<input
class="input"
value="[![LURE badge for {$page.params.name} package]({LURE_WEB_API_URL}/badge/{$page.params.repo}/{$page.params.name})]({currentURL})"
readonly
/>
<p>HTML</p>
<input
class="input"
value='<a href="{currentURL}"><img src="{LURE_WEB_API_URL}/badge/{$page.params.repo}/{$page.params.name}" alt="LURE badge for {$page.params.name} package"></a>'
readonly
/>
</section>
<footer class="modal-card-foot">
<button class="button is-primary" on:click={hideModal}>Close</button>
</footer>
</div>
</div>
<p class="title">{pkg.name}</p>
<p class="subtitle mb-3">{fullVer(pkg)}</p>
<a href="/pkg/{pkg.repository}/{pkg.name}/lure.sh" class="button is-primary mb-5">View lure.sh</a>
<button href="/pkg/{pkg.repository}/{pkg.name}/lure.sh" class="button is-success mb-5 ml-1" on:click={showModal}>View badge</button>
<div class="box">
<table class="table is-hoverable is-fullwidth">
<tbody>
{#if pkg.description != ''}
<tr>
<th>Description:</th>
<td>{pkg.description}</td>
</tr>
{/if}
{#if pkg.homepage != ''}
<tr>
<th>Homepage:</th>
<td><a href={pkg.homepage}>{pkg.homepage}</a></td>
</tr>
{/if}
{#if pkg.maintainer != ''}
<tr>
<th>Maintainer:</th>
<td>{pkg.maintainer}</td>
</tr>
{/if}
{#if pkg.licenses.length != 0}
<tr>
<th>Licenses:</th>
<td>
{#each pkg.licenses as license, index}
{#if license.startsWith('custom')}
{license}{#if index + 1 < pkg.licenses.length},&nbsp{/if}
{:else}
<a href="https://spdx.org/licenses/{license}.html">{license}</a
>{#if index + 1 < pkg.licenses.length},&nbsp;{/if}
{/if}
{/each}
</td>
</tr>
{/if}
{#if pkg.architectures.length != 0}
<tr>
<th>Architectures:</th>
<td>{pkg.architectures.join(', ')}</td>
</tr>
{/if}
{#if pkg.conflicts.length != 0}
<tr>
<th>Conflicts:</th>
<td>{pkg.conflicts.join(', ')}</td>
</tr>
{/if}
{#if pkg.provides.length != 0}
<tr>
<th>Provides:</th>
<td>{pkg.provides.join(', ')}</td>
</tr>
{/if}
{#each [...objToMap(pkg.depends)] as [override, pkgList]}
<tr>
<th>Depends ({override == '' ? 'default' : override}):</th>
<td>{pkgList.entries.join(', ')}</td>
</tr>
{/each}
{#each [...objToMap(pkg.buildDepends)] as [override, pkgList]}
<tr>
<th>Build Depends ({override != '' ? override : 'default'}):</th>
<td>{pkgList.entries.join(', ')}</td>
</tr>
{/each}
<tr>
<th>Repository:</th>
<td>{pkg.repository}</td>
</tr>
</tbody>
</table>
</div>
{:catch err}
<div class="notification is-danger my-3">
Error: {err.msg}
</div>
{/await}
</section>
<Footer />
@@ -0,0 +1,46 @@
<script>
import { page } from '$app/stores';
import { client } from 'twirpscript';
import { GetBuildScript } from '$lib/lure.pb';
import { LURE_WEB_API_URL } from '$env/static/public';
import { DoubleBounce } from 'svelte-loading-spinners';
import Highlight from 'svelte-highlight';
import bash from 'svelte-highlight/languages/bash';
import agate from 'svelte-highlight/styles/agate';
import Header from "../../../../header.svelte";
import Footer from "../../../../footer.svelte";
import Icon from '@iconify/svelte';
client.baseURL = LURE_WEB_API_URL
client.prefix = ""
</script>
<svelte:head>
<title>{$page.params.name} Build Script | LURE Web</title>
<meta name="description" content="The build script for the {$page.params.name} LURE package.">
{@html agate}
</svelte:head>
<Header/>
<section class="container">
<a href="."><Icon icon="material-symbols:arrow-back-rounded" inline=true/> Back</a>
{#await GetBuildScript({name: $page.params.name, repository: $page.params.repo})}
<center><DoubleBounce color="#375a7f" /></center>
{:then resp}
<p class="title">Build Script</p>
<p class="subtitle">{$page.params.repo} / {$page.params.name}</p>
<div class="box">
<Highlight language={bash} code={resp.script}/>
</div>
{:catch err}
<div class="notification is-danger my-3">
Error: {err.msg}
</div>
{/await}
</section>
<Footer/>
+129
View File
@@ -0,0 +1,129 @@
<script>
import { client } from 'twirpscript';
import { Search } from '$lib/lure.pb';
import { searchReq } from '../../stores.js';
import { LURE_WEB_API_URL } from '$env/static/public';
import Header from "../header.svelte";
import Footer from "../footer.svelte";
import { DoubleBounce } from 'svelte-loading-spinners';
import Icon from '@iconify/svelte';
client.baseURL = LURE_WEB_API_URL
client.prefix = ""
let searchPromise = Search(
$searchReq ?? {
query: "",
limit: BigInt(0),
sortBy: "UNSORTED",
filterType: "NO_FILTER",
}
);
function onSubmit(e) {
let formData = new FormData(e.target);
let filterValue = formData.get('filter-value');
let req = {
query: formData.get('query'),
limit: BigInt(0),
sortBy: formData.get('sort-by'),
filterType: formData.get('filter'),
filterValue: filterValue == '' ? null : filterValue,
}
searchReq.set(req)
searchPromise = Search(req);
}
function onFilterChange(e) {
let filterVal = document.getElementById('filter-val');
if (e.target.value != "NO_FILTER") {
filterVal.classList.remove('is-hidden');
} else {
filterVal.classList.add('is-hidden');
}
}
</script>
<svelte:head>
<title>Package Search | LURE Web</title>
<meta name="description" content="Search for LURE packages">
</svelte:head>
<Header/>
<section class="container">
<p class="title">Package Search</p>
<form on:submit|preventDefault={onSubmit}>
<div class="field">
<div class="control">
<input class="input" type="text" name="query" value="" placeholder="Query">
</div>
</div>
<div class="columns">
<div class="column">
<div class="field">
<div class="control select is-fullwidth">
<select name="sort-by">
<option value="UNSORTED" selected>Unsorted</option>
<option value="NAME">Sort by Name</option>
<option value="VERSION">Sort by Version</option>
<option value="REPOSITORY">Sort by Repository</option>
</select>
</div>
</div>
</div>
<div class="column">
<div class="field">
<div class="control select is-fullwidth">
<select name="filter" on:change={onFilterChange}>
<option value="NO_FILTER" selected>No Filter</option>
<option value="IN_REPOSITORY">In Repo</option>
<option value="SUPPORTS_ARCH">Supports Architecture</option>
</select>
</div>
</div>
</div>
</div>
<div class="field">
<div class="control">
<input id="filter-val" class="input is-hidden" type="text" value="" name="filter-value" placeholder="Filter Value">
</div>
</div>
<input type="submit" class="button is-fullwidth" value="Search">
</form>
<hr>
{#await searchPromise}
<center><DoubleBounce color="#375a7f" /></center>
{:then resp}
{#each resp.packages as pkg}
<div class="card my-5">
<header class="card-header">
<p class="card-header-title">{pkg.repository} / {pkg.name}</p>
<div class="card-header-icon">{pkg.version}</div>
</header>
<div class="card-content">
{pkg.description}
</div>
<footer class="card-footer">
<a class="card-footer-item" href="/pkg/{pkg.repository}/{pkg.name}">More info <Icon icon="material-symbols:arrow-forward-rounded" inline=true/></a>
</footer>
</div>
{/each}
{#if resp.packages.length === 0}
<p class="subtitle has-text-centered has-text-danger my-5 is-fullwidth">No Results</p>
{/if}
{:catch err}
<div class="notification is-danger my-3">
Error: {err.msg}
</div>
{/await}
</section>
<Footer/>
+3
View File
@@ -0,0 +1,3 @@
import { writable } from "svelte/store";
export const searchReq = writable()
+6
View File
@@ -0,0 +1,6 @@
/* Variables and mixins declared here will be available in all other SCSS files */ /* https://github.com/jgthms/bulma/issues/1293 */
$body-overflow-y: auto;
@import './darkly/variables.scss';
$family-sans-serif: "Ubuntu", sans-serif;
-108
View File
@@ -1,108 +0,0 @@
:root {
--primary: #0060A8;
--primary-hover: #026EBF;
--secondary: #0EAAAA;
--secondary-hover: #13b4b4;
--font-size: 16px;
}
.centered {
text-align: center;
}
.left {
text-align: left;
}
.right {
text-align: right;
}
.justify-space-between {
display: flex;
justify-content: space-between;
}
.hidden {
display: none;
}
a.close {
cursor: pointer;
}
nav:not([aria-label="breadcrumb"]) {
/* Workaround for horizontal overflow due to justify-content */
overflow-x: hidden;
}
/* Add some spacing between the navbar items and the edge of the page */
nav:not([aria-label="breadcrumb"]) ul {
padding-left: 2rem;
padding-right: 2rem;
}
nav:not([aria-label="breadcrumb"]) a {
color: #dddedf;
}
nav:not([aria-label="breadcrumb"]) a.active {
background-color: var(--primary);
}
nav:not([aria-label="breadcrumb"]) a:hover:not(.active) {
color: var(--primary-hover);
}
/* Keep the footer at the bottom */
body > main {
min-height: 100vh;
}
/* Make the footer smaller */
body > footer {
--block-spacing-vertical: 2rem;
}
/* Make card header and footer smaller */
article > header, article > footer {
padding-top: 1rem;
padding-bottom: 1rem;
}
/* Make the bottom margin on card headers smaller */
article > header {
margin-bottom: calc(var(--block-spacing-vertical) / 2);
}
/* Make the top margin on card footers smaller */
article > footer {
margin-top: calc(var(--block-spacing-vertical) / 2);
}
/* Fix the icon height to 100px to make cards uniform */
article > img.icon {
height: 100px;
}
th {
width: 30%;
}
td {
word-break: break-word;
cursor: text;
width: 70%;
}
pre code {
cursor: text;
}
.marginless {
margin: 0;
}
[role="link"], a {
--color: var(--secondary);
}
-5
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

-1
View File
@@ -1 +0,0 @@
data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACNklEQVQ4jX3T32vXdRTH8cf5fL+zITLsJrroh3Rjd0VpfDPaXLMpDIbNgoTIiG6kLnPUP1AMU7qqC1G86Yfg1Km52Tf7zso5tnDRRVBBjrEugm7Km32/2+f99mIlrm297s7hvJ6cwzmHuzXb2ObX8bqfrm6+kxub6HTh+qjhyU3WULEiChtRU8ZlN77d/E/uIVmvFKM+mVoFiVXIn6/WNGNUM36zEIctxjm3Ki9rxnHNmLAYPV7f3lq7A9jaNSnplj2Az5RxVlv+XHYQU0qvrj/CXGOnm+M83vmDFM/IMSvbo/SFtnxS0hL6wPszVe/N3AX44+tBoSF5zcw37Sp5QNaDX4QJi/GKMAiO3tigMKx0ZBnw11eDijwknFPGGRUjGFLJhyRPKuOmcAwJp1TyKZH7hd8Lrfo7CkPCackB8rDQi3nhCDpFXjYXDijjJWGvwiH3FEereFA4rWK/7GNhF+YVupWxQ+E8xhT5uKXifmFA4V1vP/EBVJX5TUUU7u1J5hr3LZtzl6V4UeQhEZdU7dNfW3BiuhB53lvbzq99B3ONEc14Tiued6vyp1bULcQWzRizGC8YqC38d+sr15gdwwYpRrXlDkkXfpTtUsaIT6fa/x/wcPdFKfZjk+Syttyhd8djUvTJOpVGnJhuXx8AW7vOSHbLqpIrrlx71N7al5J9sp1KF3z0/R3f6l/4V5PfPaUVdc34Wyue1ff0rJPT/ZY84o3tH67rW6HxazX1ibpL1zvWK7kNwWfQQO79fcgAAAAASUVORK5CYII=
Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 B

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

+26
View File
@@ -0,0 +1,26 @@
import preprocess from 'svelte-preprocess';
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [
vitePreprocess(),
preprocess({
scss: {
prependData: '@use "src/variables.scss" as *;'
}
})
],
kit: {
adapter: adapter(),
env: {
publicPrefix: "LURE_WEB_"
},
}
};
export default config;
+71
View File
@@ -0,0 +1,71 @@
job "lure-web" {
region = "global"
datacenters = ["dc1"]
type = "service"
group "lure-web" {
network {
port "api" {
to = 8080
}
port "http" {
to = 3000
}
}
task "lure-api-server" {
driver = "docker"
env {
LURE_API_GITHUB_SECRET = "${LURE_API_GITHUB_SECRET}"
// Hack to force Nomad to re-deploy the service
// instead of ignoring it
COMMIT_SHA = "${DRONE_COMMIT_SHA}"
}
config {
image = "elara6331/lure-api-server:latest"
ports = ["api"]
}
service {
name = "lure-api-server"
port = "api"
tags = [
"traefik.enable=true",
"traefik.http.routers.lure-api-server.rule=Host(`api.lure.elara.ws`)",
"traefik.http.routers.lure-api-server.tls.certresolver=letsencrypt"
]
check {
type = "tcp"
port = "api"
interval = "30s"
timeout = "2s"
}
}
}
task "lure-web" {
driver = "docker"
config {
image = "elara6331/lure-web:latest"
ports = ["http"]
}
service {
name = "lure-web"
port = "http"
tags = [
"traefik.enable=true",
"traefik.http.routers.lure-web.rule=Host(`lure.elara.ws`)",
"traefik.http.routers.lure-web.tls.certresolver=letsencrypt",
]
}
}
}
}
-77
View File
@@ -1,77 +0,0 @@
#macro("content"):
<nav aria-label="breadcrumb">
<ul>
<li><a href="/">Home</a></li>
<li>About</li>
</ul>
</nav>
<h1>About LURE</h1>
<section class="container">
<hgroup><h2>Why does LURE exist?</h2></hgroup>
<p>
LURE was created because packaging software for multiple Linux distros can be difficult and
error-prone, and installing those packages can be a nightmare for users unless they're available
in their distro's official repositories.
</p>
<p>
Take Discord for example. It only provides a deb file and a tar.gz file, and it's not available
in most official repositories. That means users of RPM distros have to manually install discord
using the tarball or rely on community-maintained repositories that don't always have
up-to-date versions of Discord. That's also made worse by the fact that Discord refuses to run
if there's a newer version available.
</p>
<p>
LURE fixes that by always providing the most up to date version of Discord, which means all that
RPM users have to do is run <code>lure in discord</code> and LURE will get the tarball and
automatically build an RPM package out of it for them. When there's a newer version of Discord
available, users can just run <code>lure up</code> and LURE will automatically download the updated
version of Discord and install it. That also helps users of deb distros because it means they don't
have to manually download packages for software like Discord.
</p>
</section>
<section class="container">
<hgroup><h2>How does LURE work?</h2></hgroup>
<p>
Similar to Arch Linux's AUR, LURE has a repository of shell scripts that tell it how to build a
package. When you run a LURE command, it updates its repository and interprets the shell script
for the package you want to install using its built-in bash implementation. Then, it uses the
code inside the script to build a file structure to be included in the final package. It detects
which distro you're running, resolves dependencies, detects which package manager you have and
which package format it uses, builds metadata for the package from the information in the shell
script, builds the final package, and then runs the install command for your package manager to
install it.
</p>
</section>
<section class="container">
<hgroup><h2>How does LURE keep its packages up to date?</h2></hgroup>
<p>
LURE can automatically update its packages using a bot called
<a href="https://gitea.elara.ws/lure/lure-updater">lure-updater</a>. It accepts plugins that
detect when software is updated upstream and updates the LURE package accordingly. The plugins
that are currently running in my instance of the bot can be found in the
<a href="https://gitea.elara.ws/lure/updater-plugins">updater-plugins</a> repo. The
<code>discord-bin</code> package, for example, checks Discord's API every hour to see if they've
released an updated version, and if they have, it pushes an update to LURE's repo.
</p>
</section>
<section class="container">
<hgroup><h2>How do I add my own package to LURE?</h2></hgroup>
<p>
LURE provides <a href="https://github.com/lure-sh/lure/blob/master/docs/packages">comprehensive documentation</a>
for packagers. If you need help with anything, feel free to ask on LURE's <a href="https://reddit.com/r/linux_user_repository">subreddit</a>.
If you find a bug or would like a feature to be added, please open an issue on LURE's git repo.
</p>
</section>
<section class="container">
<hgroup><h2>Can I use and modify LURE's icons?</h2></hgroup>
<p>
LURE's icons are available on the <a href="/icons">icons page</a> of this site. They're licensed
under CC-BY-NC-SA 4.0, which means you're free to share, modify, and use the icons for non-commercial
purposes as long as you give appropriate credit and indicate any changes made to the original icons.
</p>
</section>
#!macro
#include("base.html", title = "About", desc = "About the Linux User Repository")
-35
View File
@@ -1,35 +0,0 @@
<!DOCTYPE html>
<html data-theme="dark">
<head>
<title>#(title) | LURE Web</title>
<meta name="description" content='#(desc | title + " on LURE Web")' />
<meta name="viewport" content="width=device-width, initial-scale=0.8" />
<link rel="stylesheet" href="/static/css/pico.min.css">
<link rel="stylesheet" href="/static/css/lure.css">
<link rel="icon" href="/static/icons/favicon.ico">
</head>
<body>
<nav>
<ul>
<li><a href="/"><img src="/static/icons/lure-text-white.svg" alt="LURE Logo" width="85"></a></li>
</ul>
<ul>
<li><a href="/" class='#(title == "Home" ? "active" : "")'>Home</a></li>
<li><a href="/pkgs" class='#(title == "Package Search" ? "active" : "")'>Packages</a></li>
<li><a href="/about" class='#(title == "About" ? "active" : "")'>About</a></li>
</ul>
</nav>
<main class="container">
#macro("content")
</main>
<footer class="container">
<div class="headings marginless">
<h2>Copyright &copy; #(now().Year()) LURE Web Contributors</h2>
</div>
<a href="https://github.com/lure-sh/lure-web" target="_blank">Source Code</a>
</footer>
</body>
</html>
-25
View File
@@ -1,25 +0,0 @@
#macro("content"):
<nav aria-label="breadcrumb">
<ul>
<li><a href="/">Home</a></li>
<li>Error</li>
</ul>
</nav>
<h1>Error</h1>
<br><br>
<section class="container centered">
<div>
<hgroup>
<h1>Something Went Wrong!</h1>
<h2>Error #(error.StatusCode): #(error.StatusText())</h2>
</hgroup>
<pre><code>#(error.Msg)</code></pre>
<a href="/" role="button">Go Back Home</a>
</div>
</section>
#!macro
#include("base.html", title = sprintf("Error %d", error.StatusCode), desc = sprintf("Error %d: %s", error.StatusCode, error.StatusText()))
-32
View File
@@ -1,32 +0,0 @@
#macro("content"):
<section class="container centered">
<img src="/static/icons/lure-no-text.svg" alt="LURE logo without text" width="200">
<hgroup>
<h1>LURE</h1>
<h2>The community repository missing from your Linux distro</h2>
</hgroup>
</section>
<section class="container">
<hgroup>
<h1>What does LURE do?</h1>
<h2>LURE allows you to:</h2>
</hgroup>
<ul>
<li>Access a wide range of software beyond what's available in official repositories</li>
<li>Get new versions of software as they come out, before official repositories ship them</li>
<li>Install unofficial software without having to deal with a separate package manager</li>
<li>Release software for Linux without having to package it for different distributions</li>
</ul>
</section>
<section class="container">
<hgroup>
<h1>Install LURE</h1>
<h2>Paste this into your Linux terminal and the install script will set everything up for you</h2>
</hgroup>
<pre><code>curl -fsSL lure.sh/install | bash</code></pre>
<p>LURE is also available on the AUR as <code>linux-user-repository-bin</code> and distro packages are provided at the <a href="https://gitea.elara.ws/lure/lure/releases/latest">latest Gitea release</a></p>
</section>
#!macro
#include("base.html", title = "Home", desc = "The community repository missing from your Linux distro")
-39
View File
@@ -1,39 +0,0 @@
#macro("content"):
<nav aria-label="breadcrumb">
<ul>
<li><a href="/">Home</a></li>
<li>Icons</li>
</ul>
</nav>
<h1>Icons</h1>
<section class="container">
<div class="grid">
<div>
<article class="centered">
<header><strong>Without Text</strong></header>
<img class="icon" src="/static/icons/lure-no-text.svg" alt="LURE icon without text">
<footer><a href="/static/icons/lure-no-text.svg" download>Download</a></footer>
</article>
</div>
<div>
<article class="centered">
<header><strong>With Text</strong></header>
<img class="icon" src="/static/icons/lure-text.svg" alt="LURE icon with text">
<footer><a href="/static/icons/lure-text.svg" download>Download</a></footer>
</article>
</div>
<div>
<article class="centered">
<header><strong>White With Text</strong></header>
<img class="icon" src="/static/icons/lure-text-white.svg" alt="LURE icon with text">
<footer><a href="/static/icons/lure-text-white.svg" download>Download</a></footer>
</article>
</div>
</div>
<strong>Note: The icons on this page are licensed under CC-BY-NC-SA 4.0 unless otherwise specified</strong>
</section>
#!macro
#include("base.html", title = "Icons", desc = "Linux User Repository icons and logos")
-142
View File
@@ -1,142 +0,0 @@
#macro("content"):
<nav aria-label="breadcrumb">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/pkgs">Packages</a></li>
<li>#(pkg.Name)</li>
</ul>
</nav>
<hgroup>
<h1>#(pkg.Name)</h1>
<h2>#(pkg.FullVersion())</h2>
</hgroup>
<a href="/pkg/#(pkg.Repository)/#(pkg.Name)/script" role="button">View script</a>
<a href="javascript:openModal()" role="button" class="secondary">View badge</a>
<br>
<article>
<h3 >Package Information</h3>
<table>
<tbody>
#if(pkg.Description != ""):
<tr>
<th>Description:</th>
<td>#(pkg.Description)</td>
</tr>
#!if
#if(pkg.Homepage != ""):
<tr>
<th>Homepage:</th>
<td><a href="#(pkg.Homepage)" target="_blank">#(pkg.Homepage)</a></td>
</tr>
#!if
#if(pkg.Maintainer != ""):
<tr>
<th>Maintainer:</th>
<td>#(pkg.Maintainer)</td>
</tr>
#!if
#if(len(pkg.Licenses) != 0):
<tr>
<th>Licenses:</th>
<td>
#for(i, license in pkg.Licenses):
#if(hasPrefix(license, "custom")):
#(license)#if(i != len(pkg.Licenses) - 1):,&nbsp;#!if
#else:
<a href="https://spdx.org/licenses/#(license).html" target="_blank">#(license)</a>#if(i != len(pkg.Licenses) - 1):,&nbsp;#!if
#!if
#!for
</td>
</tr>
#!if
#if(len(pkg.Architectures) != 0):
<tr>
<th>Architectures:</th>
<td>#(join(pkg.Architectures, ", "))</td>
</tr>
#!if
#if(len(pkg.Conflicts) != 0):
<tr>
<th>Conflicts:</th>
<td>#(join(pkg.Conflicts, ", "))</td>
</tr>
#!if
#if(len(pkg.Provides) != 0):
<tr>
<th>Provides:</th>
<td>#(join(pkg.Provides, ", "))</td>
</tr>
#!if
<tr>
<th>Repository:</th>
<td>#(pkg.Repository)</td>
</tr>
</tbody>
</table>
</article>
#if(len(pkg.Depends) > 0):
<article>
<h3 >Runtime Dependencies</h3>
<table>
<tbody>
#for(override, pkgList in pkg.Depends):
<tr>
<th>#(override == "" ? "default" : override):</th>
<td>#(join(pkgList, ", "))</td>
</tr>
#!for
</tbody>
</table>
</article>
#!if
#if(len(pkg.BuildDepends) > 0):
<article>
<h3 >Build Dependencies</h3>
<table>
<tbody>
#for(override, pkgList in pkg.BuildDepends):
<tr>
<th>#(override == "" ? "default" : override):</th>
<td>#(join(pkgList, ", "))</td>
</tr>
#!for
</tbody>
</table>
</article>
#!if
<dialog id="badgeModal">
<article style="min-width: 60%">
<header>
<strong>Badge</strong>
<a aria-label="Close" class="close" href="javascript:closeModal()"></a>
</header>
<p class="centered">
<img src="#(url)/badge.svg" alt="LURE badge for #(pkg.Name)">
<hr>
</p>
<label for="badgeMarkdown">Markdown</label>
<input id="badgeMarkdown" value="[![LURE badge for #(pkg.Name)](#(url)/badge.svg)](#(url))" readonly />
<label for="badgeHTML">HTML</label>
<input id="badgeHTML" value='<a href="#(url)"><img src="#(url)/badge.svg" alt="LURE badge for #(pkg.Name)">' readonly />
</article>
</dialog>
<script>
function openModal() {
let modal = document.getElementById("badgeModal");
document.documentElement.classList.add('modal-is-open');
modal.setAttribute('open', true);
}
function closeModal() {
let modal = document.getElementById("badgeModal");
document.documentElement.classList.remove('modal-is-open');
modal.removeAttribute('open');
}
</script>
#!macro
#include("base.html", title = pkg.Name + " Package", desc = sprintf("%s %s - %s", pkg.Name, pkg.FullVersion(), pkg.Description))
-62
View File
@@ -1,62 +0,0 @@
#macro("content"):
<nav aria-label="breadcrumb">
<ul>
<li><a href="/">Home</a></li>
<li>Packages</li>
</ul>
</nav>
<h1>Package Search</h1>
<form action="/pkgs">
<input type="text" id="query" name="q" value='#(query.Get("q"))' placeholder="Query"/>
<div class="grid">
<div>
<select name="sort">
<option value="">Unsorted</option>
<option value="name" #(query.Get("sort") == "name" ? "selected" : "")>Sort by Name</option>
<option value="version" #(query.Get("sort") == "version" ? "selected" : "")>Sort by Version</option>
<option value="repo" #(query.Get("sort") == "repo" ? "selected" : "")>Sort by Repository</option>
</select>
</div>
<div>
<select name="filter" onchange="onFilterChange(event)">
<option value="">No Filter</option>
<option value="inrepo" #(query.Get("filter") == "inrepo" ? "selected" : "")>In Repo</option>
<option value="arch" #(query.Get("filter") == "arch" ? "selected" : "")>Supports Architecture</option>
</select>
</div>
</div>
<input class='#(query.Get("filter") == "" ? "hidden" : "")' type="text" id="filterValue" name="fv" value='#(query.Get("fv"))' placeholder="Filter Value"/>
<button type="submit">Search</button>
</form>
<hr>
#for(pkg in pkgs):
<article>
<header>
<div class="justify-space-between">
<span class="left"><strong>#(pkg.Repository) / #(pkg.Name)</strong></span>
<span class="right">#(pkg.Version)</span>
</div>
</header>
<p>#(pkg.Description)</p>
<footer class="centered">
<a href="/pkg/#(pkg.Repository)/#(pkg.Name)">More Info &rarr;</a>
</footer>
</article>
#!for
<script>
function onFilterChange(e) {
let filterValue = document.getElementById('filterValue');
if (e.srcElement.value == '') {
filterValue.classList.add('hidden');
} else {
filterValue.classList.remove('hidden');
}
}
</script>
#!macro
#include("base.html", title = "Package Search", desc = "Search for LURE packages")
-19
View File
@@ -1,19 +0,0 @@
#macro("content"):
<nav aria-label="breadcrumb">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/pkgs">Packages</a></li>
<li><a href="/pkg/#(repoName)/#(pkgName)">#(pkgName)</a></li>
<li>Script</li>
</ul>
</nav>
<hgroup>
<h1>Build Script</h1>
<h2>#(repoName) / #(pkgName)</h2>
</hgroup>
#(script)
#!macro
#include("base.html", title = pkgName + " Build Script", desc = sprintf("Build script for the %s package", pkgName))
+17
View File
@@ -0,0 +1,17 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}
+20
View File
@@ -0,0 +1,20 @@
import { sveltekit } from '@sveltejs/kit/vite';
/** @type {import('vite').UserConfig} */
const config = {
plugins: [sveltekit()],
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "src/variables.scss" as *;'
}
}
},
server: {
host: '0.0.0.0'
}
};
export default config;
-79
View File
@@ -1,79 +0,0 @@
package main
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"io"
"net/http"
"os"
"strings"
"sync"
"github.com/uptrace/bunrouter"
"go.elara.ws/logger/log"
"lure.sh/lure/pkg/repos"
)
func registerWebhook(ctx context.Context, mux *bunrouter.Router) {
g := mux.WithMiddleware(apiErrorHandler)
g.POST("/webhook", func(w http.ResponseWriter, req bunrouter.Request) error {
if req.Header.Get("X-GitHub-Event") != "push" {
return HTTPError{http.StatusBadRequest, "only push events are accepted by this bot"}
}
err := verifySecure(req.Request)
if err != nil {
return err
}
go pullRepos(ctx)
return nil
})
}
func verifySecure(req *http.Request) error {
sigStr := req.Header.Get("X-Hub-Signature-256")
sig, err := hex.DecodeString(strings.TrimPrefix(sigStr, "sha256="))
if err != nil {
return HTTPError{http.StatusBadRequest, "invalid hmac value"}
}
secretStr, ok := os.LookupEnv("LURE_WEB_GITHUB_SECRET")
if !ok {
return HTTPError{http.StatusInternalServerError, "LURE_WEB_GITHUB_SECRET must be set to the secret used for setting up the github webhook"}
}
secret := []byte(secretStr)
h := hmac.New(sha256.New, secret)
_, err = io.Copy(h, req.Body)
if err != nil {
return err
}
if !hmac.Equal(h.Sum(nil), sig) {
log.Warn("Insecure webhook request").
Str("from", req.RemoteAddr).
Bytes("sig", sig).
Bytes("hmac", h.Sum(nil)).
Send()
return HTTPError{http.StatusUnauthorized, "insecure webhook request"}
}
return nil
}
var pullMtx sync.Mutex
func pullRepos(ctx context.Context) {
pullMtx.Lock()
defer pullMtx.Unlock()
err := repos.Pull(ctx, nil)
if err != nil {
log.Warn("Error while pulling repositories").Err(err).Send()
}
}