feat: change to ssr (#69)

* feat: change to ssr

* fix: move route to nginx

* fix: rename yaml

* ci: add deploy

* fix: real ip
This commit is contained in:
LowderPlay
2026-05-29 14:51:32 +05:00
committed by GitHub
parent 9e56dc06fb
commit 7d11e14804
19 changed files with 663 additions and 294 deletions
+27 -4
View File
@@ -1,4 +1,4 @@
name: Build and Publish Website Docker Image
name: Build and Publish Docker Images
on:
push:
@@ -13,7 +13,19 @@ env:
jobs:
build:
name: Build ${{ matrix.service }} image
runs-on: ubuntu-latest
strategy:
matrix:
include:
- service: website
file: ./website/Dockerfile
context: .
image_suffix: ""
- service: frontend
file: ./frontend/Dockerfile
context: ./frontend
image_suffix: "-frontend"
permissions:
contents: read
packages: write
@@ -38,7 +50,7 @@ jobs:
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}${{ matrix.image_suffix }}
tags: |
type=ref,event=branch
type=ref,event=pr
@@ -51,10 +63,21 @@ jobs:
id: build-and-push
uses: docker/build-push-action@v7
with:
context: .
file: ./website.Dockerfile
context: ${{ matrix.context }}
file: ${{ matrix.file }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build
if: github.event_name != 'pull_request'
steps:
- name: Trigger Coolify deploy
run: |
curl --fail --request GET '${{ secrets.COOLIFY_WEBHOOK }}' --header 'Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}'
Generated
+6 -89
View File
@@ -751,7 +751,7 @@ dependencies = [
"atomic 0.6.1",
"pear",
"serde",
"toml 0.8.23",
"toml",
"uncased",
"version_check",
]
@@ -831,15 +831,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "frontend"
version = "0.0.1"
dependencies = [
"include_dir",
"serde",
"toml 0.9.12+spec-1.1.0",
]
[[package]]
name = "fsevent-sys"
version = "4.1.0"
@@ -1591,25 +1582,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "include_dir"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
dependencies = [
"include_dir_macros",
]
[[package]]
name = "include_dir_macros"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "indexmap"
version = "2.12.1"
@@ -3226,15 +3198,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -3926,26 +3889,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml"
version = "0.9.12+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned 1.1.1",
"toml_datetime 0.7.5+spec-1.1.0",
"toml_parser",
"toml_writer",
"winnow 0.7.13",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
@@ -3955,15 +3903,6 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.22.27"
@@ -3972,19 +3911,10 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap",
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
"serde_spanned",
"toml_datetime",
"toml_write",
"winnow 0.7.13",
]
[[package]]
name = "toml_parser"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow 1.0.3",
"winnow",
]
[[package]]
@@ -3993,12 +3923,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "toml_writer"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]]
name = "tower"
version = "0.5.2"
@@ -4443,7 +4367,6 @@ dependencies = [
"dotenvy",
"env_logger",
"flate2",
"frontend",
"governor",
"log",
"querying",
@@ -4824,12 +4747,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
[[package]]
name = "winreg"
version = "0.50.0"
-1
View File
@@ -1,6 +1,5 @@
[workspace]
members = [
"frontend",
"querying",
"reporter",
"reports",
+73 -51
View File
@@ -1,59 +1,81 @@
# ![Cheburcheck Logo](website/static/favicon.svg) Cheburcheck.ru
[Cheburcheck.ru](https://cheburcheck.ru) — open-source сервис для проверки доменов и IP-адресов на наличие в списках блокировок Роскомнадзора и популярных заблокированных CDN-провайдеров.
---
## О проекте
Этот сервис реализован на языке Rust с использованием веб-фреймворка [Rocket.rs](https://rocket.rs/). Основная задача — проверять, заблокирован ли домен или IP-адрес, основываясь на актуальных данных из реестров Роскомнадзора и других источниках.
---
## Технологии
- **Rust** — язык программирования, на котором написан весь бекенд.
- **Rocket.rs** — веб-фреймворк для построения HTTP API и серверной логики.
- **DoH резолвер Quad9** — для разрешения DNS-запросов используется DNS-over-HTTPS, что позволяет обойти локальные ограничения и получать актуальные данные.
- **Tera** — шаблонизатор для генерации HTML страниц, используется для фронтенда.
- **lucide** — библиотека иконок, применяемая для визуального оформления интерфейса.
---
## Как это работает
1. Пользователь вводит домен или IP-адрес в форму на сайте.
2. Сервер выполняет DNS-запрос через Quad9 DoH для получения актуальной информации.
3. Полученный домен/IP проверяется на наличие в блокировочных списках через префиксные деревья.
4. Результат формируется с помощью Tera и отображается пользователю.
---
## Списки
Для проверки используются списки [123jjck/cdn-ip-ranges](https://github.com/123jjck/cdn-ip-ranges), [antifilter.download](https://antifilter.download/) и [antifilter.network](https://antifilter.network).
Мы собираем собственные белые списки с помощью [Cheburcheck Reporter](reporter/README.md).
---
## Структура проекта
* `querying` — модуль проверки сайтов по базам данных
* `reporter` — [Cheburcheck Reporter](reporter/README.md)
# ![Cheburcheck Logo](website/static/favicon.svg) Cheburcheck.ru
[Cheburcheck.ru](https://cheburcheck.ru) — open-source сервис для проверки доменов и IP-адресов на наличие в списках блокировок Роскомнадзора и популярных заблокированных CDN-провайдеров.
---
## О проекте
Этот сервис реализован на языке Rust с использованием веб-фреймворка [Rocket.rs](https://rocket.rs/). Основная задача — проверять, заблокирован ли домен или IP-адрес, основываясь на актуальных данных из реестров Роскомнадзора и других источниках.
---
## Технологии
- **Rust** — язык программирования, на котором написан весь бекенд.
- **Rocket.rs** — веб-фреймворк для построения HTTP API и серверной логики.
- **DoH резолвер Quad9** — для разрешения DNS-запросов используется DNS-over-HTTPS, что позволяет обойти локальные ограничения и получать актуальные данные.
- **SvelteKit** — SSR-фронтенд, который запускается отдельным Node.js сервисом.
- **nginx** — публичная точка входа, проксирует API в Rocket, а страницы в SvelteKit.
- **lucide** — библиотека иконок, применяемая для визуального оформления интерфейса.
---
## Как это работает
1. Пользователь вводит домен или IP-адрес в форму на сайте.
2. Сервер выполняет DNS-запрос через Quad9 DoH для получения актуальной информации.
3. Полученный домен/IP проверяется на наличие в блокировочных списках через префиксные деревья.
4. Результат возвращается через API и отображается SSR-фронтендом SvelteKit.
---
## Списки
Для проверки используются списки [123jjck/cdn-ip-ranges](https://github.com/123jjck/cdn-ip-ranges), [antifilter.download](https://antifilter.download/) и [antifilter.network](https://antifilter.network).
Мы собираем собственные белые списки с помощью [Cheburcheck Reporter](reporter/README.md).
---
## Структура проекта
* `querying` — модуль проверки сайтов по базам данных
* `reporter` — [Cheburcheck Reporter](reporter/README.md)
* `reports` — общий протокол для отправки отчетов
* `website` — исходный код веб-сайта
* `website` — Rocket API и серверная логика
* `frontend` — SvelteKit SSR-интерфейс
---
## Вклад
## Запуск через Docker Compose
Если хотите помочь с разработкой — открывайте issue или присылайте pull requests.
Если хотите помочь финансово:
- TON: `UQAACsiwpGryjP-kqp4TJPAWpXytuB6M_puuO0Cg5zNvaSJW`
```sh
docker compose up --build
```
---
По умолчанию nginx слушает `http://localhost:8080`. Порт можно изменить через
`HTTP_PORT`, например:
## Контакты
```sh
HTTP_PORT=80 docker compose up --build
```
Для вопросов и обсуждений можно написать на [support@cheburcheck.ru](mailto:support@cheburcheck.ru).
Маршрутизация:
* `/api/v1/*`, `/agency/*`, `/whitelist/*` и устаревший `/feedback/*` идут в Rocket
* остальные запросы идут в SvelteKit SSR
---
## Вклад
Если хотите помочь с разработкой — открывайте issue или присылайте pull requests.
Если хотите помочь финансово:
- TON: `UQAACsiwpGryjP-kqp4TJPAWpXytuB6M_puuO0Cg5zNvaSJW`
---
## Контакты
Для вопросов и обсуждений можно написать на [support@cheburcheck.ru](mailto:support@cheburcheck.ru).
+32
View File
@@ -0,0 +1,32 @@
services:
nginx:
image: docker.io/nginx:1.27-alpine
depends_on:
- frontend
- website
ports:
- "${HTTP_PORT:-8080}:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
environment:
HOST: 0.0.0.0
PORT: 3000
NODE_ENV: production
expose:
- "3000"
website:
build:
context: .
dockerfile: website/Dockerfile
environment:
ROCKET_ADDRESS: "::"
ROCKET_PORT: 8000
DATABASE_URL: "${DATABASE_URL}"
expose:
- "8000"
+10
View File
@@ -0,0 +1,10 @@
node_modules
.svelte-kit
build
dist
.env
.env.*
!.env.example
!.env.test
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
-11
View File
@@ -1,11 +0,0 @@
[package]
name = "frontend"
version = "0.0.1"
edition = "2024"
[dependencies]
include_dir = "0.7"
[build-dependencies]
serde = { workspace = true }
toml = "0.9"
+43
View File
@@ -0,0 +1,43 @@
# syntax=docker/dockerfile:1.10
FROM docker.io/node:24-bookworm-slim AS build
WORKDIR /app
ENV PNPM_HOME=/pnpm
ENV PATH="${PNPM_HOME}:${PATH}"
RUN corepack enable && \
corepack prepare pnpm@10.33.0 --activate && \
pnpm config set store-dir /pnpm/store
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
pnpm fetch --frozen-lockfile
COPY . ./
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
set -eux; \
pnpm install --frozen-lockfile --offline; \
pnpm build; \
pnpm prune --prod
FROM docker.io/node:24-bookworm-slim
WORKDIR /app
ENV HOST=0.0.0.0
ENV PORT=3000
ENV NODE_ENV=production
COPY --from=build /app/build ./build
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package.json ./package.json
EXPOSE 3000
USER node
CMD ["node", "build/index.js"]
-41
View File
@@ -1,41 +0,0 @@
use std::error::Error;
use std::path::PathBuf;
use std::process::Command;
fn main() -> Result<(), Box<dyn Error>> {
println!("cargo:rerun-if-changed=package.json");
println!("cargo:rerun-if-changed=pnpm-lock.yaml");
println!("cargo:rerun-if-changed=svelte.config.js");
println!("cargo:rerun-if-changed=vite.config.ts");
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed=static");
println!("cargo:rerun-if-changed=../website/Cargo.toml");
if std::env::var_os("SKIP_FRONTEND_BUILD").is_some() {
return Ok(());
}
let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
run_pnpm(&manifest_dir, &["install", "--frozen-lockfile"])?;
run_pnpm(&manifest_dir, &["build"])?;
Ok(())
}
fn run_pnpm(manifest_dir: &PathBuf, args: &[&str]) -> Result<(), Box<dyn Error>> {
let mut command = Command::new(pnpm());
command.args(args).current_dir(manifest_dir);
let status = command.status()?;
if status.success() {
Ok(())
} else {
Err(format!("pnpm {} failed", args.join(" ")).into())
}
}
fn pnpm() -> &'static str {
if cfg!(windows) { "pnpm.cmd" } else { "pnpm" }
}
+2 -1
View File
@@ -7,6 +7,7 @@
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"start": "node build/index.js",
"prepare": "svelte-kit sync || echo ''",
"type-check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"type-check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
@@ -16,7 +17,7 @@
},
"devDependencies": {
"@biomejs/biome": "2.4.15",
"@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/adapter-node": "^5.4.0",
"@sveltejs/kit": "^2.57.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tailwindcss/vite": "^4.2.2",
+414 -7
View File
@@ -21,9 +21,9 @@ importers:
'@biomejs/biome':
specifier: 2.4.15
version: 2.4.15
'@sveltejs/adapter-static':
specifier: ^3.0.10
version: 3.0.10(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.10)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))(svelte@5.55.10)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))
'@sveltejs/adapter-node':
specifier: ^5.4.0
version: 5.5.4(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.10)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))(svelte@5.55.10)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))
'@sveltejs/kit':
specifier: ^2.57.0
version: 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.10)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))(svelte@5.55.10)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0))
@@ -278,6 +278,180 @@ packages:
'@rolldown/pluginutils@1.0.1':
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
'@rollup/plugin-commonjs@29.0.2':
resolution: {integrity: sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg==}
engines: {node: '>=16.0.0 || 14 >= 14.17'}
peerDependencies:
rollup: ^2.68.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/plugin-json@6.1.0':
resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/plugin-node-resolve@16.0.3':
resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^2.78.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/pluginutils@5.3.0':
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/rollup-android-arm-eabi@4.60.4':
resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.60.4':
resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.60.4':
resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.60.4':
resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.60.4':
resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.60.4':
resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.60.4':
resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.60.4':
resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.60.4':
resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.60.4':
resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.60.4':
resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.60.4':
resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.60.4':
resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.60.4':
resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.60.4':
resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.60.4':
resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.60.4':
resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.60.4':
resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.60.4':
resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.60.4':
resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openharmony-arm64@4.60.4':
resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.60.4':
resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.60.4':
resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.60.4':
resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.60.4':
resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==}
cpu: [x64]
os: [win32]
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
@@ -286,10 +460,10 @@ packages:
peerDependencies:
acorn: ^8.9.0
'@sveltejs/adapter-static@3.0.10':
resolution: {integrity: sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==}
'@sveltejs/adapter-node@5.5.4':
resolution: {integrity: sha512-45X92CXW+2J8ZUzPv3eLlKWEzINKiiGeFWTjyER4ZN4sGgNoaoeSkCY/QYNxHpPXy71QPsctwccBo9jJs0ySPQ==}
peerDependencies:
'@sveltejs/kit': ^2.0.0
'@sveltejs/kit': ^2.4.0
'@sveltejs/kit@2.61.1':
resolution: {integrity: sha512-Ny8s1SR1TyQS2hD2Rvw0XKzU2Nw1eUF52dTb6T2bdcgz7wSC+Nyb5IwjWYlR4b2dvbbR5NJDiQwHg3rnNseghg==}
@@ -428,6 +602,9 @@ packages:
'@types/d3-contour@3.0.6':
resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==}
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
@@ -437,6 +614,9 @@ packages:
'@types/node@25.9.1':
resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==}
'@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
@@ -465,6 +645,9 @@ packages:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'}
commondir@1.0.1:
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
cookie@0.6.0:
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
engines: {node: '>= 0.6'}
@@ -602,6 +785,10 @@ packages:
resolution: {integrity: sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==}
engines: {node: '>=10.13.0'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
esm-env@1.2.2:
resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
@@ -613,6 +800,9 @@ packages:
'@typescript-eslint/types':
optional: true
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
@@ -627,9 +817,16 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
hasown@2.0.4:
resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
engines: {node: '>= 0.4'}
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
@@ -641,6 +838,16 @@ packages:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
is-core-module@2.16.2:
resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==}
engines: {node: '>= 0.4'}
is-module@1.0.0:
resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
is-reference@1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
is-reference@3.0.3:
resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
@@ -765,6 +972,9 @@ packages:
obug@2.1.1:
resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -780,6 +990,11 @@ packages:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
resolve@1.22.12:
resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==}
engines: {node: '>= 0.4'}
hasBin: true
robust-predicates@3.0.3:
resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==}
@@ -788,6 +1003,11 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
rollup@4.60.4:
resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
runed@0.37.1:
resolution: {integrity: sha512-MeFY73xBW8IueWBm012nNFIGy19WUGPLtknavyUPMpnyt350M47PhGSGrGoSLbidwn+Zlt/O0cp8/OZE3LASWA==}
peerDependencies:
@@ -821,6 +1041,10 @@ packages:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
svelte-check@4.4.8:
resolution: {integrity: sha512-67adfgBox5eNSNIvIIwgFizKGdcRrGpiMoNO2obHcYuLz7iTa8Xgm/NGU3ntMFnNm8K1grFOIG6HhMLX/vcN8w==}
engines: {node: '>= 18.0.0'}
@@ -1094,15 +1318,130 @@ snapshots:
'@rolldown/pluginutils@1.0.1': {}
'@rollup/plugin-commonjs@29.0.2(rollup@4.60.4)':
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.4)
commondir: 1.0.1
estree-walker: 2.0.2
fdir: 6.5.0(picomatch@4.0.4)
is-reference: 1.2.1
magic-string: 0.30.21
picomatch: 4.0.4
optionalDependencies:
rollup: 4.60.4
'@rollup/plugin-json@6.1.0(rollup@4.60.4)':
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.4)
optionalDependencies:
rollup: 4.60.4
'@rollup/plugin-node-resolve@16.0.3(rollup@4.60.4)':
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.4)
'@types/resolve': 1.20.2
deepmerge: 4.3.1
is-module: 1.0.0
resolve: 1.22.12
optionalDependencies:
rollup: 4.60.4
'@rollup/pluginutils@5.3.0(rollup@4.60.4)':
dependencies:
'@types/estree': 1.0.9
estree-walker: 2.0.2
picomatch: 4.0.4
optionalDependencies:
rollup: 4.60.4
'@rollup/rollup-android-arm-eabi@4.60.4':
optional: true
'@rollup/rollup-android-arm64@4.60.4':
optional: true
'@rollup/rollup-darwin-arm64@4.60.4':
optional: true
'@rollup/rollup-darwin-x64@4.60.4':
optional: true
'@rollup/rollup-freebsd-arm64@4.60.4':
optional: true
'@rollup/rollup-freebsd-x64@4.60.4':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.60.4':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.60.4':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.60.4':
optional: true
'@rollup/rollup-linux-arm64-musl@4.60.4':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.60.4':
optional: true
'@rollup/rollup-linux-loong64-musl@4.60.4':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.60.4':
optional: true
'@rollup/rollup-linux-ppc64-musl@4.60.4':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.60.4':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.60.4':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.60.4':
optional: true
'@rollup/rollup-linux-x64-gnu@4.60.4':
optional: true
'@rollup/rollup-linux-x64-musl@4.60.4':
optional: true
'@rollup/rollup-openbsd-x64@4.60.4':
optional: true
'@rollup/rollup-openharmony-arm64@4.60.4':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.60.4':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.60.4':
optional: true
'@rollup/rollup-win32-x64-gnu@4.60.4':
optional: true
'@rollup/rollup-win32-x64-msvc@4.60.4':
optional: true
'@standard-schema/spec@1.1.0': {}
'@sveltejs/acorn-typescript@1.0.10(acorn@8.16.0)':
dependencies:
acorn: 8.16.0
'@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.10)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))(svelte@5.55.10)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))':
'@sveltejs/adapter-node@5.5.4(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.10)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))(svelte@5.55.10)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))':
dependencies:
'@rollup/plugin-commonjs': 29.0.2(rollup@4.60.4)
'@rollup/plugin-json': 6.1.0(rollup@4.60.4)
'@rollup/plugin-node-resolve': 16.0.3(rollup@4.60.4)
'@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.10)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))(svelte@5.55.10)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0))
rollup: 4.60.4
'@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.10)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))(svelte@5.55.10)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0))':
dependencies:
@@ -1222,6 +1561,8 @@ snapshots:
'@types/d3-array': 3.2.2
'@types/geojson': 7946.0.16
'@types/estree@1.0.8': {}
'@types/estree@1.0.9': {}
'@types/geojson@7946.0.16': {}
@@ -1230,6 +1571,8 @@ snapshots:
dependencies:
undici-types: 7.24.6
'@types/resolve@1.20.2': {}
'@types/trusted-types@2.0.7': {}
acorn@8.16.0: {}
@@ -1246,6 +1589,8 @@ snapshots:
commander@7.2.0: {}
commondir@1.0.1: {}
cookie@0.6.0: {}
d3-array@2.12.1:
@@ -1373,12 +1718,16 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.3.3
es-errors@1.3.0: {}
esm-env@1.2.2: {}
esrap@2.2.9:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
estree-walker@2.0.2: {}
fdir@6.5.0(picomatch@4.0.4):
optionalDependencies:
picomatch: 4.0.4
@@ -1386,8 +1735,14 @@ snapshots:
fsevents@2.3.3:
optional: true
function-bind@1.1.2: {}
graceful-fs@4.2.11: {}
hasown@2.0.4:
dependencies:
function-bind: 1.1.2
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
@@ -1396,6 +1751,16 @@ snapshots:
internmap@2.0.3: {}
is-core-module@2.16.2:
dependencies:
hasown: 2.0.4
is-module@1.0.0: {}
is-reference@1.2.1:
dependencies:
'@types/estree': 1.0.9
is-reference@3.0.3:
dependencies:
'@types/estree': 1.0.9
@@ -1511,6 +1876,8 @@ snapshots:
obug@2.1.1: {}
path-parse@1.0.7: {}
picocolors@1.1.1: {}
picomatch@4.0.4: {}
@@ -1523,6 +1890,13 @@ snapshots:
readdirp@4.1.2: {}
resolve@1.22.12:
dependencies:
es-errors: 1.3.0
is-core-module: 2.16.2
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
robust-predicates@3.0.3: {}
rolldown@1.0.2:
@@ -1546,6 +1920,37 @@ snapshots:
'@rolldown/binding-win32-arm64-msvc': 1.0.2
'@rolldown/binding-win32-x64-msvc': 1.0.2
rollup@4.60.4:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.60.4
'@rollup/rollup-android-arm64': 4.60.4
'@rollup/rollup-darwin-arm64': 4.60.4
'@rollup/rollup-darwin-x64': 4.60.4
'@rollup/rollup-freebsd-arm64': 4.60.4
'@rollup/rollup-freebsd-x64': 4.60.4
'@rollup/rollup-linux-arm-gnueabihf': 4.60.4
'@rollup/rollup-linux-arm-musleabihf': 4.60.4
'@rollup/rollup-linux-arm64-gnu': 4.60.4
'@rollup/rollup-linux-arm64-musl': 4.60.4
'@rollup/rollup-linux-loong64-gnu': 4.60.4
'@rollup/rollup-linux-loong64-musl': 4.60.4
'@rollup/rollup-linux-ppc64-gnu': 4.60.4
'@rollup/rollup-linux-ppc64-musl': 4.60.4
'@rollup/rollup-linux-riscv64-gnu': 4.60.4
'@rollup/rollup-linux-riscv64-musl': 4.60.4
'@rollup/rollup-linux-s390x-gnu': 4.60.4
'@rollup/rollup-linux-x64-gnu': 4.60.4
'@rollup/rollup-linux-x64-musl': 4.60.4
'@rollup/rollup-openbsd-x64': 4.60.4
'@rollup/rollup-openharmony-arm64': 4.60.4
'@rollup/rollup-win32-arm64-msvc': 4.60.4
'@rollup/rollup-win32-ia32-msvc': 4.60.4
'@rollup/rollup-win32-x64-gnu': 4.60.4
'@rollup/rollup-win32-x64-msvc': 4.60.4
fsevents: 2.3.3
runed@0.37.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.10)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))(svelte@5.55.10)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(jiti@2.7.0)))(svelte@5.55.10):
dependencies:
dequal: 2.0.3
@@ -1573,6 +1978,8 @@ snapshots:
source-map-js@1.2.1: {}
supports-preserve-symlinks-flag@1.0.0: {}
svelte-check@4.4.8(picomatch@4.0.4)(svelte@5.55.10)(typescript@6.0.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
-3
View File
@@ -1,3 +0,0 @@
pub use include_dir::{Dir, File, include_dir};
pub static DIST: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/dist");
+6 -1
View File
@@ -2,6 +2,7 @@
import { CloudAlert, Copyright } from "@lucide/svelte";
import { createQuery } from "@tanstack/svelte-query";
import type { Snippet } from "svelte";
import { browser } from "$app/environment";
import type { StatusMetrics } from "$lib/api/status";
import { fetchStatus } from "$lib/api/status";
import { setStatusContext } from "$lib/context/status";
@@ -17,6 +18,10 @@ type CachedStatus = {
};
function getCachedStatus() {
if (!browser) {
return null;
}
const cachedStatus = localStorage.getItem(statusCacheKey);
if (!cachedStatus) {
return null;
@@ -40,7 +45,7 @@ const status = createQuery(() => ({
setStatusContext(status);
$effect(() => {
if (!status.data) {
if (!browser || !status.data) {
return;
}
+1 -1
View File
@@ -1 +1 @@
export const ssr = false;
export const ssr = true;
+2 -7
View File
@@ -1,4 +1,4 @@
import adapter from "@sveltejs/adapter-static";
import adapter from "@sveltejs/adapter-node";
/** @type {import('@sveltejs/kit').Config} */
const config = {
@@ -8,12 +8,7 @@ const config = {
filename.split(/[/\\]/).includes("node_modules") ? undefined : true,
},
kit: {
adapter: adapter({
pages: "dist",
assets: "dist",
fallback: "index.html",
strict: true,
}),
adapter: adapter({ out: "build" }),
},
};
+42
View File
@@ -0,0 +1,42 @@
upstream website_backend {
server website:8000;
}
upstream frontend {
server frontend:3000;
}
server {
listen 80;
server_name _;
set_real_ip_from 172.18.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
location /api/v1/ {
proxy_pass http://website_backend;
}
location /agency/ {
proxy_pass http://website_backend;
}
location /whitelist/ {
proxy_pass http://website_backend;
}
location /feedback/ {
proxy_pass http://website_backend/api/v1/feedback/;
}
location / {
proxy_pass http://frontend;
}
}
-1
View File
@@ -11,7 +11,6 @@ rocket-client-addr = "0.5.4"
serde = { workspace = true }
reports = { path = "../reports" }
querying = { path = "../querying" }
frontend = { path = "../frontend" }
env_logger = "0.11.10"
rocket-cache-response = "0.6.4"
log = { workspace = true }
+4 -24
View File
@@ -4,39 +4,21 @@ FROM docker.io/rust:1-slim-bookworm AS build
WORKDIR /build
ENV PNPM_HOME=/pnpm
ENV PATH="${PNPM_HOME}:${PATH}"
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends ca-certificates curl libssl-dev pkg-config && \
curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \
apt-get install -y --no-install-recommends nodejs && \
corepack enable && \
corepack prepare pnpm@10.33.0 --activate && \
pnpm config set store-dir /pnpm/store && \
apt-get install -y --no-install-recommends ca-certificates libssl-dev pkg-config && \
rm -rf /var/lib/apt/lists/*
COPY frontend/package.json frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml ./frontend/
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
pnpm --dir frontend fetch --frozen-lockfile
COPY . .
RUN --mount=type=cache,id=website-target,target=/build/target \
--mount=type=cache,id=cargo-registry,target=/usr/local/cargo/registry \
--mount=type=cache,id=cargo-git,target=/usr/local/cargo/git \
--mount=type=cache,id=pnpm-store,target=/pnpm/store \
set -eux; \
pnpm --dir frontend install --frozen-lockfile --offline; \
pnpm --dir frontend build; \
RUSTFLAGS="-C strip=symbols" SKIP_FRONTEND_BUILD=true SQLX_OFFLINE=true cargo build --locked --release --package website; \
RUSTFLAGS="-C strip=symbols" SQLX_OFFLINE=true cargo build --locked --release --package website; \
cp target/release/website ./main
################################################################################
FROM docker.io/debian:bookworm-slim
WORKDIR /app
@@ -50,14 +32,12 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
useradd --system --gid app --home-dir /app --shell /usr/sbin/nologin app
COPY --from=build /build/website/Rocket.toml ./
## copy the main binary
COPY --from=build /build/main ./
## ensure the container listens globally on port 8080
ENV ROCKET_ADDRESS=::
ENV ROCKET_PORT=8080
ENV ROCKET_PORT=8000
EXPOSE 8080
EXPOSE 8000
USER app
+1 -52
View File
@@ -9,15 +9,13 @@ use env_logger::Env;
use log::{LevelFilter, error, info};
use querying::Checker;
use rocket::fairing::AdHoc;
use rocket::http::{ContentType, Status};
use rocket::response::content::RawHtml;
use rocket::http::Status;
use rocket::serde::json::Json;
use rocket::tokio::sync::RwLock;
use rocket::tokio::time;
use rocket::{Build, Request, Rocket, fairing, tokio};
use serde::Serialize;
use sqlx::postgres::PgPool;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
@@ -35,53 +33,6 @@ fn api_error(status: Status, _: &Request) -> Json<JsonError> {
})
}
#[get("/")]
fn frontend_index() -> Option<RawHtml<&'static str>> {
frontend::DIST
.get_file("index.html")
.and_then(frontend::File::contents_utf8)
.map(RawHtml)
}
#[get("/<path..>", rank = 20)]
fn frontend_asset(path: PathBuf) -> Option<(ContentType, &'static [u8])> {
if is_backend_path(&path) {
return None;
}
if let Some(file) = embedded_file(&path) {
return Some((content_type(&path), file.contents()));
}
if path.extension().is_none() {
let index = frontend::DIST.get_file("index.html")?;
return Some((ContentType::HTML, index.contents()));
}
None
}
fn embedded_file(path: &PathBuf) -> Option<&'static frontend::File<'static>> {
let path = path.to_string_lossy().replace('\\', "/");
frontend::DIST.get_file(path)
}
fn is_backend_path(path: &PathBuf) -> bool {
matches!(
path.components()
.next()
.and_then(|component| component.as_os_str().to_str()),
Some("api" | "agency" | "whitelist")
)
}
fn content_type(path: &PathBuf) -> ContentType {
path.extension()
.and_then(|extension| extension.to_str())
.and_then(ContentType::from_extension)
.unwrap_or(ContentType::Binary)
}
async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
match rocket.state::<PgPool>() {
Some(db) => match sqlx::migrate!("./migrations").run(db).await {
@@ -158,7 +109,6 @@ async fn rocket() -> _ {
.manage(pool)
.manage(api_limiter)
.attach(AdHoc::try_on_ignite("SQLx Migrations", run_migrations))
.mount("/", routes![api::feedback]) // DEPRECATED: backwards compatibility
.mount(
"/api/v1",
routes![
@@ -171,6 +121,5 @@ async fn rocket() -> _ {
)
.mount("/agency", routes![agency::upload_report])
.mount("/whitelist", routes![whitelist::export_csv])
.mount("/", routes![frontend_index, frontend_asset])
.register("/", catchers![api_error])
}