feat(app): add nxv

This commit is contained in:
okxlin
2026-08-01 04:27:20 +08:00
parent f56a62270e
commit fbaa67eb8d
15 changed files with 374 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
PANEL_APP_PORT_HTTP=8080
APP_DATA_DIR=./data
CONTAINER_NAME=1Panel-localnxv
+35
View File
@@ -0,0 +1,35 @@
additionalProperties:
formFields:
- default: 8080
edit: true
envKey: PANEL_APP_PORT_HTTP
labelEn: HTTP Port
labelZh: HTTP 端口
label:
en: HTTP Port
zh: HTTP 端口
zh-Hant: HTTP 連接埠
ja: HTTP ポート
ko: HTTP 포트
ru: Порт HTTP
ms: Port HTTP
pt-br: Porta HTTP
required: true
rule: paramPort
type: number
- default: ./data
edit: true
envKey: APP_DATA_DIR
labelEn: Data Directory
labelZh: 数据目录
label:
en: Data Directory
zh: 数据目录
zh-Hant: 資料目錄
ja: データディレクトリ
ko: 데이터 디렉터리
ru: Каталог данных
ms: Direktori data
pt-br: Diretorio de dados
required: true
type: text
+36
View File
@@ -0,0 +1,36 @@
services:
nxv:
image: "ghcr.io/utensils/nxv:0.7.0@sha256:5c39000455d3bb91f91f311b98df42127fbf926381eaa67abdb4c2d234b0d799"
container_name: ${CONTAINER_NAME}
command:
- --db-path
- /data/index.db
- serve
- --host
- 0.0.0.0
- --port
- "8080"
ports:
- "${PANEL_APP_PORT_HTTP}:8080"
volumes:
- "${APP_DATA_DIR}:/data"
restart: unless-stopped
healthcheck:
test:
- CMD
- nxv
- --db-path
- /data/index.db
- stats
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
labels:
createdBy: "Apps"
networks:
- 1panel-network
networks:
1panel-network:
external: true
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
IMAGE="ghcr.io/utensils/nxv:0.7.0@sha256:5c39000455d3bb91f91f311b98df42127fbf926381eaa67abdb4c2d234b0d799"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
strip_matching_quotes() {
local value="$1"
if [[ ${#value} -ge 2 ]]; then
if [[ "${value:0:1}" == '"' && "${value: -1}" == '"' ]]; then
value="${value:1:${#value}-2}"
elif [[ "${value:0:1}" == "'" && "${value: -1}" == "'" ]]; then
value="${value:1:${#value}-2}"
fi
fi
printf '%s\n' "$value"
}
read_env_value() {
local value=""
if [[ -f "$ENV_FILE" ]]; then
value="$(grep -E '^APP_DATA_DIR=' "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)"
fi
value="${value%$'\r'}"
strip_matching_quotes "$value"
}
if [[ ${APP_DATA_DIR+x} ]]; then
DATA_DIR_RAW="$APP_DATA_DIR"
else
DATA_DIR_RAW="$(read_env_value)"
fi
DATA_DIR_RAW="$(strip_matching_quotes "${DATA_DIR_RAW:-./data}")"
[[ -n "$DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be empty"
case "$DATA_DIR_RAW" in
*$'\n'* | *$'\r'* | *:*) fail "APP_DATA_DIR contains unsupported characters" ;;
esac
if [[ "$DATA_DIR_RAW" = /* ]]; then
[[ ! -L "$DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be a symbolic link"
DATA_DIR_ABS="$(realpath -m -- "$DATA_DIR_RAW")"
[[ "$DATA_DIR_ABS" != "/" ]] || fail "APP_DATA_DIR must not be the filesystem root"
else
DATA_DIR_PATH="${ROOT_DIR}/${DATA_DIR_RAW#./}"
[[ ! -L "$DATA_DIR_PATH" ]] || fail "APP_DATA_DIR must not be a symbolic link"
ROOT_DIR_ABS="$(realpath -m -- "$ROOT_DIR")"
DATA_DIR_ABS="$(realpath -m -- "$DATA_DIR_PATH")"
case "$DATA_DIR_ABS" in
"${ROOT_DIR_ABS}"/*) ;;
*) fail "Relative APP_DATA_DIR must remain inside the app version directory" ;;
esac
fi
if [[ -e "$DATA_DIR_ABS" && ! -d "$DATA_DIR_ABS" ]]; then
fail "APP_DATA_DIR must be a directory"
fi
install -d -m 0750 -- "$DATA_DIR_ABS"
docker run --rm --volume "${DATA_DIR_ABS}:/data" "$IMAGE" \
--db-path /data/index.db sync
[[ -s "$DATA_DIR_ABS/index.db" ]] || fail "nxv sync did not create index.db"
[[ -s "$DATA_DIR_ABS/index.bloom" ]] || fail "nxv sync did not create index.bloom"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
# The package index is intentionally retained for recovery or reinstallation.
exit 0
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
exec "$SCRIPT_DIR/init.sh"
+32
View File
@@ -0,0 +1,32 @@
# nxv
## 产品介绍
nxv 是 Nix 软件包历史版本索引服务。它下载并验证上游预构建索引,在浏览器界面和 HTTP API 中快速查询软件包版本、出现时间及对应的 nixpkgs 提交。
## 主要功能
- 按软件包名、版本和描述检索 nixpkgs 历史
- 查看软件包版本时间线和对应提交
- 提供 Web 界面、OpenAPI 文档和 HTTP API
- 使用 SQLite 与 Bloom Filter 提供本地快速查询
## 访问说明
- 首次安装需要下载并验证约 220 MB 的压缩索引,完成前主服务不会启动;实际磁盘占用会高于下载大小。
- 索引保存在数据目录中,重启不会重复下载完整索引;重新安装前请按需要备份或清理该目录。
- nxv 本身不提供用户认证。不要直接暴露到不受信任的公网,建议通过 1Panel 网站反向代理配置访问控制。
- 固定版本用于可复现部署,`latest` 会跟随上游镜像更新。
## Introduction
nxv indexes historical nixpkgs releases and exposes fast package-version search through a web interface and HTTP API. The first installation downloads and verifies an index of about 220 MB before the server starts. Keep the data directory persistent to avoid unnecessary downloads.
## Features
- Searches package names, versions, and descriptions across nixpkgs history
- Shows version timelines and the corresponding nixpkgs commits
- Provides a web interface, OpenAPI documentation, and an HTTP API
- Uses a local SQLite index and Bloom filter for fast queries
nxv does not provide built-in authentication. Restrict public access with 1Panel reverse-proxy authentication or another trusted access-control layer.
+30
View File
@@ -0,0 +1,30 @@
name: nxv
tags:
- 实用工具
title: 快速检索 Nix 软件包历史版本的索引服务
description: 快速检索 Nix 软件包历史版本的索引服务
additionalProperties:
key: nxv
name: nxv
tags:
- Tool
shortDescZh: 快速检索 Nix 软件包历史版本的索引服务
shortDescEn: Index service for quickly searching historical Nix package versions
description:
en: Index service for quickly searching historical Nix package versions
zh: 快速检索 Nix 软件包历史版本的索引服务
zh-Hant: 快速搜尋 Nix 軟體套件歷史版本的索引服務
ja: Nix パッケージの過去のバージョンを素早く検索するインデックスサービス
ko: Nix 패키지의 이전 버전을 빠르게 검색하는 인덱스 서비스
ru: Сервис индекса для быстрого поиска исторических версий пакетов Nix
ms: Perkhidmatan indeks untuk mencari versi sejarah pakej Nix dengan pantas
pt-br: Servico de indice para pesquisar rapidamente versoes historicas de pacotes Nix
type: tool
crossVersionUpdate: true
limit: 0
recommend: 0
website: https://utensils.io/nxv/
github: https://github.com/utensils/nxv
document: https://utensils.io/nxv/
architectures:
- amd64
+3
View File
@@ -0,0 +1,3 @@
PANEL_APP_PORT_HTTP=8080
APP_DATA_DIR=./data
CONTAINER_NAME=1Panel-localnxv
+35
View File
@@ -0,0 +1,35 @@
additionalProperties:
formFields:
- default: 8080
edit: true
envKey: PANEL_APP_PORT_HTTP
labelEn: HTTP Port
labelZh: HTTP 端口
label:
en: HTTP Port
zh: HTTP 端口
zh-Hant: HTTP 連接埠
ja: HTTP ポート
ko: HTTP 포트
ru: Порт HTTP
ms: Port HTTP
pt-br: Porta HTTP
required: true
rule: paramPort
type: number
- default: ./data
edit: true
envKey: APP_DATA_DIR
labelEn: Data Directory
labelZh: 数据目录
label:
en: Data Directory
zh: 数据目录
zh-Hant: 資料目錄
ja: データディレクトリ
ko: 데이터 디렉터리
ru: Каталог данных
ms: Direktori data
pt-br: Diretorio de dados
required: true
type: text
+36
View File
@@ -0,0 +1,36 @@
services:
nxv:
image: "ghcr.io/utensils/nxv:latest"
container_name: ${CONTAINER_NAME}
command:
- --db-path
- /data/index.db
- serve
- --host
- 0.0.0.0
- --port
- "8080"
ports:
- "${PANEL_APP_PORT_HTTP}:8080"
volumes:
- "${APP_DATA_DIR}:/data"
restart: unless-stopped
healthcheck:
test:
- CMD
- nxv
- --db-path
- /data/index.db
- stats
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
labels:
createdBy: "Apps"
networks:
- 1panel-network
networks:
1panel-network:
external: true
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
IMAGE="ghcr.io/utensils/nxv:latest"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
strip_matching_quotes() {
local value="$1"
if [[ ${#value} -ge 2 ]]; then
if [[ "${value:0:1}" == '"' && "${value: -1}" == '"' ]]; then
value="${value:1:${#value}-2}"
elif [[ "${value:0:1}" == "'" && "${value: -1}" == "'" ]]; then
value="${value:1:${#value}-2}"
fi
fi
printf '%s\n' "$value"
}
read_env_value() {
local value=""
if [[ -f "$ENV_FILE" ]]; then
value="$(grep -E '^APP_DATA_DIR=' "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)"
fi
value="${value%$'\r'}"
strip_matching_quotes "$value"
}
if [[ ${APP_DATA_DIR+x} ]]; then
DATA_DIR_RAW="$APP_DATA_DIR"
else
DATA_DIR_RAW="$(read_env_value)"
fi
DATA_DIR_RAW="$(strip_matching_quotes "${DATA_DIR_RAW:-./data}")"
[[ -n "$DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be empty"
case "$DATA_DIR_RAW" in
*$'\n'* | *$'\r'* | *:*) fail "APP_DATA_DIR contains unsupported characters" ;;
esac
if [[ "$DATA_DIR_RAW" = /* ]]; then
[[ ! -L "$DATA_DIR_RAW" ]] || fail "APP_DATA_DIR must not be a symbolic link"
DATA_DIR_ABS="$(realpath -m -- "$DATA_DIR_RAW")"
[[ "$DATA_DIR_ABS" != "/" ]] || fail "APP_DATA_DIR must not be the filesystem root"
else
DATA_DIR_PATH="${ROOT_DIR}/${DATA_DIR_RAW#./}"
[[ ! -L "$DATA_DIR_PATH" ]] || fail "APP_DATA_DIR must not be a symbolic link"
ROOT_DIR_ABS="$(realpath -m -- "$ROOT_DIR")"
DATA_DIR_ABS="$(realpath -m -- "$DATA_DIR_PATH")"
case "$DATA_DIR_ABS" in
"${ROOT_DIR_ABS}"/*) ;;
*) fail "Relative APP_DATA_DIR must remain inside the app version directory" ;;
esac
fi
if [[ -e "$DATA_DIR_ABS" && ! -d "$DATA_DIR_ABS" ]]; then
fail "APP_DATA_DIR must be a directory"
fi
install -d -m 0750 -- "$DATA_DIR_ABS"
docker run --rm --volume "${DATA_DIR_ABS}:/data" "$IMAGE" \
--db-path /data/index.db sync
[[ -s "$DATA_DIR_ABS/index.db" ]] || fail "nxv sync did not create index.db"
[[ -s "$DATA_DIR_ABS/index.bloom" ]] || fail "nxv sync did not create index.bloom"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
# The package index is intentionally retained for recovery or reinstallation.
exit 0
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
exec "$SCRIPT_DIR/init.sh"
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B