remove submodule
This commit is contained in:
parent
ed13626f83
commit
ad6788639e
@ -19,19 +19,6 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# IDK if I need this
|
||||
# - name: Set docker metadata
|
||||
# id: meta
|
||||
# uses: docker/metadata-action@v5
|
||||
# with:
|
||||
# images: |
|
||||
# gitea.futureporn.net/futureporn/tracker-helper:latest
|
||||
# tags: |
|
||||
# type=ref,event=branch
|
||||
# type=ref,event=pr
|
||||
# type=semver,pattern={{version}}
|
||||
# type=semver,pattern={{major}}.{{minor}}
|
||||
|
||||
- name: Login to Gitea Docker Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@ -39,31 +26,30 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build futureporn/tracker-helper
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./services/tracker-helper
|
||||
push: true
|
||||
tags: gitea.futureporn.net/futureporn/tracker-helper:latest
|
||||
labels: |
|
||||
org.opencontainers.image.description=Opentracker helper service. Adds info_hash whitelisting via HTTP
|
||||
org.opencontainers.image.title=tracker-helper
|
||||
org.opencontainers.image.created={{commit_date 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'}}
|
||||
org.opencontainers.image.licenses=unlicense
|
||||
org.opencontainers.image.source=https://gitea.futureporn.net/futureporn/fp
|
||||
org.opencontainers.image.url=https://gitea.futureporn.net/futureporn/-/packages/container/tracker-helper
|
||||
secrets: |
|
||||
WL_USERNAME=${{ secrets.WL_USERNAME }}
|
||||
WL_PASSWORD=${{ secrets.WL_PASSWORD }}
|
||||
|
||||
- name: Build futureporn/aquatic
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./apps/aquatic-tracker
|
||||
file: ./apps/aquatic-tracker/docker/aquatic_udp.Dockerfile
|
||||
context: ./apps/aquatic
|
||||
file: ./apps/aquatic/docker/aquatic_udp.Dockerfile
|
||||
push: true
|
||||
tags: gitea.futureporn.net/futureporn/aquatic:latest
|
||||
|
||||
- name: Build futureporn/tracker
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./services/tracker
|
||||
push: true
|
||||
tags: gitea.futureporn.net/futureporn/tracker:latest
|
||||
labels: |
|
||||
org.opencontainers.image.description=Aquatic tracker with custom helper service, adding info_hash accesslist operations via HTTP
|
||||
org.opencontainers.image.title=tracker
|
||||
org.opencontainers.image.licenses=unlicense
|
||||
org.opencontainers.image.source=https://gitea.futureporn.net/futureporn/fp
|
||||
org.opencontainers.image.url=https://gitea.futureporn.net/futureporn/-/packages/container/tracker
|
||||
secrets: |
|
||||
TRACKER_HELPER_USERNAME=${{ secrets.TRACKER_HELPER_USERNAME }}
|
||||
TRACKER_HELPER_PASSWORD=${{ secrets.TRACKER_HELPER_PASSWORD }}
|
||||
|
||||
- name: Build futureporn/bright
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
@ -80,18 +66,3 @@ jobs:
|
||||
org.opencontainers.image.licenses=unlicense
|
||||
org.opencontainers.image.source=https://gitea.futureporn.net/futureporn/fp
|
||||
org.opencontainers.image.url=https://gitea.futureporn.net/futureporn/-/packages/container/bright
|
||||
|
||||
# - name: Build futureporn/opentracker
|
||||
# uses: docker/build-push-action@v6
|
||||
# with:
|
||||
# context: ./apps/opentracker
|
||||
# push: true
|
||||
# tags: gitea.futureporn.net/futureporn/opentracker:latest
|
||||
# labels: |
|
||||
# org.opencontainers.image.description=opentracker is an open and free bittorrent tracker project.
|
||||
# org.opencontainers.image.title=opentracker
|
||||
# org.opencontainers.image.created={{commit_date 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'}}
|
||||
# org.opencontainers.image.version={{version}}
|
||||
# org.opencontainers.image.licenses=beerware
|
||||
# org.opencontainers.image.source=https://erdgeist.org/arts/software/opentracker
|
||||
# org.opencontainers.image.url=https://gitea.futureporn.net/futureporn/-/packages/container/opentracker
|
||||
|
@ -34,9 +34,9 @@ jobs:
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
PUBLIC_S3_ENDPOINT: ${{ vars.PUBLIC_S3_ENDPOINT }}
|
||||
SITE_URL: https://futureporn.net
|
||||
WL_URL: ${{ vars.WL_URL }}
|
||||
WL_USERNAME: ${{ secrets.WL_USERNAME }}
|
||||
WL_PASSWORD: ${{ secrets.WL_PASSWORD }}
|
||||
TRACKER_HELPER_URL: ${{ vars.TRACKER_HELPER_URL }}
|
||||
TRACKER_HELPER_USERNAME: ${{ secrets.TRACKER_HELPER_USERNAME }}
|
||||
TRACKER_HELPER_PASSWORD: ${{ secrets.TRACKER_HELPER_PASSWORD }}
|
||||
DB_NAME: ${{ vars.DB_NAME }}
|
||||
DB_HOST: ${{ vars.DB_HOST }}
|
||||
DB_PORT: ${{ vars.DB_PORT }}
|
||||
@ -72,29 +72,17 @@ jobs:
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
tracker-helper:
|
||||
image: gitea.futureporn.net/futureporn/tracker-helper:latest
|
||||
tracker:
|
||||
image: gitea.futureporn.net/futureporn/tracker:latest
|
||||
ports:
|
||||
- 5063:5063
|
||||
env:
|
||||
WL_FILE_PATH: /var/lib/aquatic/whitelist
|
||||
WL_USERNAME: ${{ secrets.WL_USERNAME }}
|
||||
WL_PASSWORD: ${{ secrets.WL_PASSWORD }}
|
||||
WL_PORT: 5063
|
||||
volumes:
|
||||
- aquatic:/var/lib/aquatic
|
||||
- /tmp/aquatic:/tmp/aquatic
|
||||
- /tmp/test:/root/test
|
||||
- /test2:/root/test2
|
||||
|
||||
aquatic:
|
||||
image: gitea.futureporn.net/futureporn/aquatic:latest
|
||||
ports:
|
||||
- 3003:3003
|
||||
- 9000:9000
|
||||
volumes:
|
||||
- aquatic:/var/lib/aquatic
|
||||
env:
|
||||
TRACKER_HELPER_ACCESSLIST_PATH: /var/lib/aquatic/whitelist
|
||||
TRACKER_HELPER_USERNAME: ${{ secrets.TRACKER_HELPER_USERNAME }}
|
||||
TRACKER_HELPER_PASSWORD: ${{ secrets.TRACKER_HELPER_PASSWORD }}
|
||||
TRACKER_HELPER_PORT: 5063
|
||||
ACCESS_LIST_CONTENTS: ""
|
||||
CONFIG_FILE_CONTENTS: |
|
||||
log_level = 'debug'
|
||||
@ -105,12 +93,12 @@ jobs:
|
||||
address_ipv6 = "[::]:3003"
|
||||
[statistics]
|
||||
interval = 5
|
||||
print_to_stdout = true
|
||||
print_to_stdout = false
|
||||
run_prometheus_endpoint = true
|
||||
prometheus_endpoint_address = "0.0.0.0:9000"
|
||||
[access_list]
|
||||
mode = "allow"
|
||||
path = "/var/lib/aquatic/whitelist"
|
||||
path = "/var/lib/aquatic/accesslist"
|
||||
[privileges]
|
||||
# Chroot and switch group and user after binding to sockets
|
||||
drop_privileges = true
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "apps/aquatic-tracker"]
|
||||
path = apps/aquatic-tracker
|
||||
url = https://github.com/insanity54/aquatic
|
@ -1 +0,0 @@
|
||||
Subproject commit 96e69754510c82ad2bbb9d0a2967c8bca0696bbd
|
@ -32,9 +32,9 @@ config :bright,
|
||||
|
||||
config :bright, :torrent,
|
||||
tracker_url: System.get_env("TRACKER_URL"),
|
||||
whitelist_url: System.get_env("WL_URL"),
|
||||
whitelist_username: System.get_env("WL_USERNAME"),
|
||||
whitelist_password: System.get_env("WL_PASSWORD")
|
||||
tracker_helper_url: System.get_env("TRACKER_HELPER_URL"),
|
||||
tracker_helper_username: System.get_env("TRACKER_HELPER_USERNAME"),
|
||||
tracker_helper_password: System.get_env("TRACKER_HELPER_PASSWORD")
|
||||
|
||||
config :bright, :buckets, media: System.get_env("AWS_BUCKET")
|
||||
|
||||
|
@ -26,8 +26,8 @@ defmodule Bright.ObanWorkers.CreateTorrent do
|
||||
cdn_url: cdn_url,
|
||||
magnet: tf.magnet
|
||||
}),
|
||||
{_, _} <- Tracker.whitelist_info_hash(torrent.info_hash_v1),
|
||||
{_, _} <- Tracker.whitelist_info_hash(torrent.info_hash_v2),
|
||||
{_, _} <- Tracker.accesslist_info_hash(torrent.info_hash_v1),
|
||||
{_, _} <- Tracker.accesslist_info_hash(torrent.info_hash_v2),
|
||||
{:ok, _updated_vod} <- Streams.update_vod(vod, %{}) do
|
||||
{:ok, torrent}
|
||||
end
|
||||
|
@ -13,72 +13,55 @@ defmodule Bright.Tracker do
|
||||
end
|
||||
|
||||
@doc """
|
||||
get the URL to our Bittorrent tracker accesslist URL, which is part of our opentracker docker container.
|
||||
This is where we send infohashes that end up in opentracker's adder.fifo
|
||||
Usually it's port 8666, path /whitelist
|
||||
get the URL to our tracker helper accesslist URL, which is part of our tracker docker container.
|
||||
This is where we send infohashes that end up in aquatic's accesslist
|
||||
Usually it's port 5063, path /accesslist
|
||||
"""
|
||||
@spec whitelist_url() :: binary()
|
||||
def whitelist_url do
|
||||
@spec accesslist_url() :: binary()
|
||||
def accesslist_url do
|
||||
url =
|
||||
case Application.fetch_env!(:bright, :torrent)[:whitelist_url] do
|
||||
nil -> raise "whitelist_url missing or empty in app config"
|
||||
"" -> raise "whitelist_url missing or empty in app config"
|
||||
case Application.fetch_env!(:bright, :torrent)[:tracker_helper_accesslist_url] do
|
||||
nil -> raise "accesslist_url missing or empty in app config"
|
||||
"" -> raise "accesslist_url missing or empty in app config"
|
||||
url -> url
|
||||
end
|
||||
|
||||
Logger.debug("whitelist_url=#{url}")
|
||||
Logger.debug("accesslist_url=#{url}")
|
||||
url
|
||||
end
|
||||
|
||||
@spec whitelist_username() :: binary()
|
||||
def whitelist_username do
|
||||
case Application.fetch_env!(:bright, :torrent)[:whitelist_username] do
|
||||
nil -> raise "whitelist_username missing or empty in app config"
|
||||
"" -> raise "whitelist_username missing or empty in app config"
|
||||
@spec tracker_helper_username() :: binary()
|
||||
def tracker_helper_username do
|
||||
case Application.fetch_env!(:bright, :torrent)[:tracker_helper_username] do
|
||||
nil -> raise "tracker_helper_username missing or empty in app config"
|
||||
"" -> raise "tracker_helper_username missing or empty in app config"
|
||||
username -> username
|
||||
end
|
||||
end
|
||||
|
||||
@spec whitelist_password() :: binary()
|
||||
def whitelist_password do
|
||||
case Application.fetch_env!(:bright, :torrent)[:whitelist_password] do
|
||||
nil -> raise "whitelist_password missing or empty in app config"
|
||||
"" -> raise "whitelist_password missing or empty in app config"
|
||||
@spec tracker_helper_password() :: binary()
|
||||
def tracker_helper_password do
|
||||
case Application.fetch_env!(:bright, :torrent)[:tracker_helper_password] do
|
||||
nil -> raise "tracker_helper_password missing or empty in app config"
|
||||
"" -> raise "tracker_helper_password missing or empty in app config"
|
||||
password -> password
|
||||
end
|
||||
end
|
||||
|
||||
@spec whitelist_feed_url() :: binary()
|
||||
def whitelist_feed_url do
|
||||
case Application.fetch_env!(:bright, :torrent)[:whitelist_feed_url] do
|
||||
nil -> raise "whitelist_feed_url missing or empty in app config"
|
||||
"" -> raise "whitelist_feed_url missing or empty in app config"
|
||||
feed_url -> feed_url
|
||||
end
|
||||
end
|
||||
|
||||
def whitelist_info_hash(info_hash) do
|
||||
whitelist_url = whitelist_url()
|
||||
username = whitelist_username()
|
||||
password = whitelist_password()
|
||||
def accesslist_info_hash(info_hash) do
|
||||
accesslist_url = accesslist_url()
|
||||
username = tracker_helper_username()
|
||||
password = tracker_helper_password()
|
||||
|
||||
Logger.debug(
|
||||
"Attempting to whitelist info_hash=#{info_hash}, whitelist_url=#{whitelist_url}, whitelist_username=#{username}, whitelist_password=#{password}"
|
||||
"Attempting to accesslist info_hash=#{info_hash}, accesslist_url=#{accesslist_url}, tracker_helper_username=#{username}, tracker_helper_password=#{password}"
|
||||
)
|
||||
|
||||
send_whitelist_request(whitelist_url, info_hash, username, password)
|
||||
# case URI.parse(whitelist_url) do
|
||||
# %URI{scheme: scheme, host: host, port: port} when host not in [nil, ""] and port != nil ->
|
||||
# full_url = "#{scheme}://#{host}:#{port}"
|
||||
|
||||
# _ ->
|
||||
# Logger.error("Invalid whitelist_url: #{whitelist_url}")
|
||||
# {:error, :invalid_url}
|
||||
# end
|
||||
send_accesslist_request(accesslist_url, info_hash, username, password)
|
||||
end
|
||||
|
||||
defp send_whitelist_request(url, info_hash, username, password) do
|
||||
Logger.debug("send_whitelist_request to url=#{url}")
|
||||
defp send_accesslist_request(url, info_hash, username, password) do
|
||||
Logger.debug("send_accesslist_request to url=#{url}")
|
||||
|
||||
headers = [
|
||||
{"Content-Type", "text/plain"},
|
||||
@ -87,12 +70,12 @@ defmodule Bright.Tracker do
|
||||
|
||||
case HTTPoison.post(url, info_hash, headers) do
|
||||
{:ok, %HTTPoison.Response{status_code: 201, body: response_body}} ->
|
||||
Logger.info("Successfully whitelisted info_hash=#{info_hash}")
|
||||
Logger.info("Successfully accesslisted info_hash=#{info_hash}")
|
||||
{:ok, response_body}
|
||||
|
||||
{:ok, %HTTPoison.Response{status_code: status_code, body: error_body}} ->
|
||||
Logger.warning(
|
||||
"Whitelist failed: info_hash=#{info_hash}, status=#{status_code}, error=#{error_body}"
|
||||
"accesslist failed: info_hash=#{info_hash}, status=#{status_code}, error=#{error_body}"
|
||||
)
|
||||
|
||||
{:error, %{status: status_code, body: error_body}}
|
||||
|
56
apps/tracker-helper/Dockerfile
Normal file
56
apps/tracker-helper/Dockerfile
Normal file
@ -0,0 +1,56 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM debian:stable-slim AS release
|
||||
ENV PATH=/command:$PATH
|
||||
|
||||
RUN apt-get update -qq \
|
||||
&& apt-get install -y -qq --no-install-recommends \
|
||||
xz-utils \
|
||||
unzip \
|
||||
ca-certificates \
|
||||
curl \
|
||||
&& export BUN_INSTALL=/home/bun \
|
||||
&& curl -fsSL https://bun.sh/install | bash -s "bun-v1.2.2" \
|
||||
&& groupadd bun \
|
||||
&& useradd bun \
|
||||
--gid bun \
|
||||
--shell /bin/sh \
|
||||
--create-home \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& chown -R bun:bun /home/bun
|
||||
|
||||
|
||||
# copy tracker-helper app
|
||||
COPY --from=gitea.futureporn.net/futureporn/tracker-helper:latest /usr/src/app/node_modules /usr/src/tracker-helper/node_modules
|
||||
COPY --from=gitea.futureporn.net/futureporn/tracker-helper:latest /usr/src/app/index.ts /usr/src/tracker-helper/
|
||||
COPY --from=gitea.futureporn.net/futureporn/tracker-helper:latest /usr/src/app/app.ts /usr/src/tracker-helper/
|
||||
COPY --from=gitea.futureporn.net/futureporn/tracker-helper:latest /usr/src/app/package.json /usr/src/tracker-helper/
|
||||
|
||||
# copy aquatic app
|
||||
COPY --from=gitea.futureporn.net/futureporn/aquatic:latest /usr/local/bin/aquatic_udp /usr/local/bin/aquatic_udp
|
||||
|
||||
# copy caddy
|
||||
COPY --from=caddy:alpine /usr/bin/caddy /usr/bin/caddy
|
||||
|
||||
# copy our app (s6-overlay dir structure)
|
||||
COPY ./root /
|
||||
|
||||
|
||||
# 9000/tcp for aquatic_udp metrics /metrics
|
||||
# and tracker-helper api /helper/*
|
||||
# Caddy handles the routing for us
|
||||
EXPOSE 9000/tcp
|
||||
|
||||
# 6969/udp for aquatic_udp
|
||||
EXPOSE 6969/udp
|
||||
|
||||
|
||||
HEALTHCHECK --interval=5s --timeout=3s --retries=3 \
|
||||
CMD /etc/tracker/healthcheck.sh
|
||||
|
||||
|
||||
ADD https://github.com/just-containers/s6-overlay/releases/download/v3.2.0.2/s6-overlay-noarch.tar.xz /tmp
|
||||
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
|
||||
ADD https://github.com/just-containers/s6-overlay/releases/download/v3.2.0.2/s6-overlay-x86_64.tar.xz /tmp
|
||||
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
|
||||
ENTRYPOINT ["/init"]
|
27
apps/tracker-helper/root/etc/aquatic/config.toml
Normal file
27
apps/tracker-helper/root/etc/aquatic/config.toml
Normal file
@ -0,0 +1,27 @@
|
||||
log_level = 'debug'
|
||||
|
||||
[network]
|
||||
use_ipv4 = true
|
||||
use_ipv6 = true
|
||||
address_ipv4 = "0.0.0.0:3003"
|
||||
address_ipv6 = "[::]:3003"
|
||||
|
||||
[statistics]
|
||||
interval = 5
|
||||
print_to_stdout = false
|
||||
run_prometheus_endpoint = true
|
||||
prometheus_endpoint_address = "0.0.0.0:9000"
|
||||
|
||||
[access_list]
|
||||
mode = "allow"
|
||||
path = "/var/lib/aquatic/accesslist"
|
||||
|
||||
[privileges]
|
||||
# Chroot and switch group and user after binding to sockets
|
||||
drop_privileges = true
|
||||
# Chroot to this path
|
||||
chroot_path = "/var/lib/aquatic"
|
||||
# Group to switch to after chrooting
|
||||
group = "nogroup"
|
||||
# User to switch to after chrooting
|
||||
user = "nobody"
|
18
apps/tracker-helper/root/etc/caddy/Caddyfile
Normal file
18
apps/tracker-helper/root/etc/caddy/Caddyfile
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
auto_https off
|
||||
admin off
|
||||
http_port 9000
|
||||
}
|
||||
|
||||
:9000 {
|
||||
|
||||
# Route for tracker-helper service
|
||||
handle_path /helper/* {
|
||||
reverse_proxy localhost:5063
|
||||
}
|
||||
|
||||
# Route for Prometheus metrics
|
||||
handle_path /metrics/* {
|
||||
reverse_proxy localhost:9000
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
#!/command/with-contenv sh
|
||||
|
||||
exec /usr/local/bin/aquatic_udp -c /etc/aquatic/config.toml
|
@ -0,0 +1 @@
|
||||
longrun
|
@ -0,0 +1,3 @@
|
||||
#!/command/with-contenv sh
|
||||
|
||||
/usr/bin/caddy run --config /etc/caddy/Caddyfile
|
@ -0,0 +1 @@
|
||||
longrun
|
@ -0,0 +1,7 @@
|
||||
#!/command/with-contenv sh
|
||||
|
||||
export PORT=5063
|
||||
|
||||
exec 2>&1
|
||||
exec s6-setuidgid bun \
|
||||
/home/bun/bin/bun run /usr/src/tracker-helper/index.ts
|
@ -0,0 +1 @@
|
||||
longrun
|
5
apps/tracker-helper/root/etc/tracker/healthcheck.sh
Executable file
5
apps/tracker-helper/root/etc/tracker/healthcheck.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
pidof aquatic_udp || exit 111
|
||||
curl -f http://localhost:5063/helper || exit 112
|
||||
curl -f http://localhost:9000/metrics || exit 113
|
0
apps/tracker-helper/root/var/lib/aquatic/accesslist
Normal file
0
apps/tracker-helper/root/var/lib/aquatic/accesslist
Normal file
@ -107,49 +107,24 @@ accessories:
|
||||
- opentracker-etc:/etc/opentracker
|
||||
- opentracker-var:/var/run/opentracker
|
||||
|
||||
# opentracker:
|
||||
# image: gitea.futureporn.net/futureporn/opentracker:latest
|
||||
# host: 45.76.57.101
|
||||
# port: "127.0.0.1:6969:6969"
|
||||
# env:
|
||||
# clear:
|
||||
# WHITELIST_FEED_URL: https://bright.futureporn.net/torrents
|
||||
# secret:
|
||||
# - WHITELIST_USERNAME
|
||||
# - WHITELIST_PASSWORD
|
||||
# proxy:
|
||||
# ssl: true
|
||||
# forward_headers: true
|
||||
# app_port: 6969
|
||||
# host: tracker.futureporn.net
|
||||
# healthcheck:
|
||||
# path: /stats
|
||||
# volumes:
|
||||
# - opentracker-etc:/etc/opentracker
|
||||
# - opentracker-var:/var/run/opentracker
|
||||
|
||||
aquatic:
|
||||
image: gitea.futureporn.net/futureporn/aquatic:latest
|
||||
tracker:
|
||||
image: gitea.futureporn.net/futureporn/tracker:latest
|
||||
host: 45.76.57.101
|
||||
port: "127.0.0.1:3000:3000"
|
||||
port: "0.0.0.0:5063:5063"
|
||||
env:
|
||||
clear:
|
||||
WHITELIST_FEED_URL: https://bright.futureporn.net/torrents
|
||||
HELPER_ACCESSLIST_PATH: "/var/lib/aquatic/accesslist"
|
||||
secret:
|
||||
- WHITELIST_USERNAME
|
||||
- WHITELIST_PASSWORD
|
||||
- HELPER_USERNAME
|
||||
- HELPER_PASSWORD
|
||||
|
||||
proxy:
|
||||
ssl: true
|
||||
forward_headers: true
|
||||
app_port: 3000
|
||||
app_port: 5063 # note: tracker also uses port 6969/udp and 9000/tcp, but the api at 5063/tcp is what we specify here. # @todo @blocking https://github.com/basecamp/kamal-proxy/issues/48
|
||||
host: tracker.futureporn.net
|
||||
## we can't do the healthcheck on the prometheus port because kamal only allows one port per container
|
||||
## @blocking https://github.com/basecamp/kamal-proxy/issues/48
|
||||
# healthcheck:
|
||||
# path: /stats
|
||||
volumes:
|
||||
- opentracker-etc:/etc/opentracker
|
||||
- opentracker-var:/var/run/opentracker
|
||||
healthcheck:
|
||||
path: /health
|
||||
|
||||
qbittorrent:
|
||||
image: lscr.io/linuxserver/qbittorrent:latest
|
||||
|
@ -9,38 +9,6 @@ services:
|
||||
volumes:
|
||||
- aquatic
|
||||
|
||||
# opentracker:
|
||||
# build:
|
||||
# context: ./apps/opentracker
|
||||
# container_name: opentracker
|
||||
# environment:
|
||||
# - WHITELIST_FEED_URL=http://bright:4000/torrents
|
||||
# env_file:
|
||||
# - .kamal/secrets.development
|
||||
# ports:
|
||||
# - "6969:6969/tcp"
|
||||
# - "6969:6969/udp"
|
||||
# volumes:
|
||||
# - opentracker-etc:/etc/opentracker
|
||||
|
||||
# qbittorrent:
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: dockerfiles/qbittorrent.dockerfile
|
||||
# environment:
|
||||
# - PUID=1000
|
||||
# - PGID=1000
|
||||
# - UMASK=002
|
||||
# - TZ=Etc/UTC
|
||||
# - WEBUI_PORT=8181
|
||||
# - WEBUI_PASSWORD=passwordpassword
|
||||
# volumes:
|
||||
# - cache:/root/.cache/futureporn
|
||||
# container_name: qbittorrent
|
||||
# ports:
|
||||
# - "8181:8181/tcp"
|
||||
|
||||
|
||||
|
||||
# bright:
|
||||
# container_name: bright
|
||||
|
@ -1,158 +0,0 @@
|
||||
import {
|
||||
describe
|
||||
, expect
|
||||
, it
|
||||
, beforeEach
|
||||
} from 'bun:test'
|
||||
import {
|
||||
Elysia
|
||||
} from 'elysia'
|
||||
import {
|
||||
treaty
|
||||
} from '@elysiajs/eden'
|
||||
import app from '../app.ts'
|
||||
import Docker from 'dockerode'
|
||||
|
||||
|
||||
if (!process.env.WL_FILE_PATH) throw new Error("WL_FILE_PATH is missing in env");
|
||||
if (!process.env.WL_USERNAME) throw new Error("WL_USERNAME is missing in env.");
|
||||
if (!process.env.WL_PASSWORD) throw new Error("WL_PASSWORD is missing in env.");
|
||||
|
||||
const whitelistFilePath = process.env.WL_FILE_PATH!
|
||||
const fixture = "3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01"
|
||||
|
||||
|
||||
const username = process.env.WL_USERNAME!
|
||||
const password = process.env.WL_PASSWORD!
|
||||
const opts = {
|
||||
headers: {
|
||||
authorization: "Basic " + btoa(username + ':' + password)
|
||||
}
|
||||
}
|
||||
const api = treaty(app)
|
||||
|
||||
|
||||
|
||||
describe
|
||||
('tracker-helper', () => {
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
let whitelistFilePath = process.env.WL_FILE_PATH!
|
||||
console.log(`Asserting existance of whitelist at ${whitelistFilePath}`)
|
||||
// create whitelist file if it doesn't exist
|
||||
const assertWhitelistExists = async function assertWhitelistExists(whitelistFilePath: string) {
|
||||
const wlFile = Bun.file(whitelistFilePath);
|
||||
const exists = await wlFile.exists()
|
||||
if (!exists) {
|
||||
console.log(`creating whitelist file at ${whitelistFilePath}`)
|
||||
await wlFile.write("")
|
||||
}
|
||||
}
|
||||
const clearWhitelist = async function clearWhitelist(whitelistFilePath: string) {
|
||||
const wlFile = Bun.file(whitelistFilePath);
|
||||
await wlFile.write("")
|
||||
}
|
||||
assertWhitelistExists(whitelistFilePath)
|
||||
clearWhitelist(whitelistFilePath)
|
||||
});
|
||||
|
||||
it('return a health response', async () => {
|
||||
const { data, status } = await api.health.get()
|
||||
expect(status).toBe(200)
|
||||
expect(data).toBe("OK")
|
||||
})
|
||||
|
||||
it('return a version', async () => {
|
||||
const { data, status } = await api.version.get(opts)
|
||||
expect(status).toBe(200)
|
||||
expect(data).toContain("version")
|
||||
})
|
||||
|
||||
it('return a whitelist', async () => {
|
||||
const seedWhitelist = async function clearWhitelist(p: string, f: string) {
|
||||
const wlFile = Bun.file(p);
|
||||
await wlFile.write(f)
|
||||
}
|
||||
await seedWhitelist(whitelistFilePath, fixture)
|
||||
const { data, status } = await api.whitelist.get(opts)
|
||||
expect(status).toBe(200)
|
||||
expect(data).toContain("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01")
|
||||
})
|
||||
|
||||
it('expects the whitelist to already exist', async () => {
|
||||
const whitelist = Bun.file(whitelistFilePath)
|
||||
const whitelistExists = await whitelist.exists()
|
||||
expect(whitelistExists).toBe(true)
|
||||
})
|
||||
|
||||
it('appends a new info_hash to the whitelist file', async () => {
|
||||
|
||||
|
||||
// make an api call which is supposed to add an entry to the whitelist
|
||||
const { data, status } = await api.whitelist.post(fixture, opts)
|
||||
|
||||
// assert that the entry has been added to the whitelist
|
||||
|
||||
|
||||
const w = Bun.file(whitelistFilePath)
|
||||
const whitelistAfter = await w.text()
|
||||
console.log('whitelistAfter as follows')
|
||||
console.log(whitelistAfter)
|
||||
|
||||
expect(status).toBe(201)
|
||||
expect(data).toMatch(fixture)
|
||||
expect(whitelistAfter).toMatch(fixture)
|
||||
|
||||
})
|
||||
|
||||
// it('sends a SIGHUP to opentracker', async () => {
|
||||
|
||||
// const { data, status } = await api.whitelist.post(fixture, opts)
|
||||
// const containerId = "act-ci-Tests-Checks-6e6f12196682961041a41a25b9d0dcf00e4d0f8e58f-7cb37eebfe9e1670328d58ad1f7c7bdf0fa078298ca6dd299e67d0141a4b9579"
|
||||
// // await docker.getContainer(containerId).kill({ signal: 'SIGHUP' })
|
||||
// let container = await docker.getContainer(containerId)
|
||||
// container.inspect
|
||||
|
||||
// })
|
||||
|
||||
|
||||
// // This is skipped because I couldn't figure out opentracker's whitelist add/delete via FIFO functionality.
|
||||
// // I got as far as writing to the FIFO, and seeing opentracker acknowledge the line in it's logs.
|
||||
// // Despite this, requests from qbittorrent to opentracker responded with,
|
||||
// // "Requested download is not authorized for use with this tracker"
|
||||
// // About a week on this problem, and I give up! Using the whitelist reloading strat instead.
|
||||
// it.skip('writes a new info_hash to a fifo', async () => {
|
||||
// const fifoFilePath = process.env.WL_FIFO_PATH!
|
||||
// const fifo = Bun.file(fifoFilePath)
|
||||
// const fifoExists = await fifo.exists();
|
||||
|
||||
|
||||
// // create fifo if it doesn't exist
|
||||
// if (!fifoExists) {
|
||||
// await Bun.spawn(["mkfifo", fifoFilePath]).exited;
|
||||
// }
|
||||
|
||||
// // Start a process to read from the FIFO
|
||||
// const reader = Bun.spawn(["cat", fifoFilePath], { stdout: "pipe" });
|
||||
|
||||
// const { data, status } = await api.whitelist.post("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01", opts)
|
||||
|
||||
// const text = await new Response(reader.stdout).text();
|
||||
|
||||
// expect(text).toBe("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01\n")
|
||||
// expect(status).toBe(200)
|
||||
// expect(data).toBe("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01")
|
||||
// })
|
||||
|
||||
it('returns 401 when username/password is missing from GET /whitelist ', async () => {
|
||||
const { status } = await api.whitelist.get()
|
||||
expect(status).toBe(401)
|
||||
})
|
||||
|
||||
it('returns 401 when username/password is missing from POST /whitelist ', async () => {
|
||||
const { status } = await api.whitelist.post()
|
||||
expect(status).toBe(401)
|
||||
})
|
||||
|
||||
})
|
4
services/tracker-helper/test/fixtures/taco
vendored
Normal file
4
services/tracker-helper/test/fixtures/taco
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
b22ea43e4c0a7f73fc706b5faf1c35bb078d3722
|
||||
3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01
|
||||
3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01
|
||||
3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01
|
@ -28,8 +28,8 @@ COPY . .
|
||||
|
||||
# [optional] tests & build
|
||||
ENV NODE_ENV=test WL_FILE_PATH=/tmp/whitelist
|
||||
RUN --mount=type=secret,id=WL_USERNAME,env=WL_USERNAME,required=true \
|
||||
--mount=type=secret,id=WL_PASSWORD,env=WL_PASSWORD,required=true \
|
||||
RUN --mount=type=secret,id=TRACKER_HELPER_USERNAME,env=TRACKER_HELPER_USERNAME,required=true \
|
||||
--mount=type=secret,id=TRACKER_HELPER_PASSWORD,env=TRACKER_HELPER_PASSWORD,required=true \
|
||||
bun test
|
||||
|
||||
# copy production dependencies and source code into final image
|
@ -4,12 +4,12 @@ import { basicAuth } from '@eelkevdbos/elysia-basic-auth'
|
||||
import net from 'net'
|
||||
import { appendFile } from "node:fs/promises";
|
||||
|
||||
if (!process.env.WL_USERNAME) throw new Error('WL_USERNAME missing in env');
|
||||
if (!process.env.WL_PASSWORD) throw new Error('WL_PASSWORD missing in env');
|
||||
if (!process.env.TRACKER_HELPER_USERNAME) throw new Error('TRACKER_HELPER_USERNAME missing in env');
|
||||
if (!process.env.TRACKER_HELPER_PASSWORD) throw new Error('TRACKER_HELPER_PASSWORD missing in env');
|
||||
|
||||
const whitelistFilePath = process.env.WL_FILE_PATH || "/etc/opentracker/whitelist"
|
||||
const username = process.env.WL_USERNAME!
|
||||
const password = process.env.WL_PASSWORD!
|
||||
const accesslistFilePath = process.env.TRACKER_HELPER_ACCESSLIST_PATH || "/var/lib/aquatic/accesslist"
|
||||
const username = process.env.TRACKER_HELPER_USERNAME!
|
||||
const password = process.env.TRACKER_HELPER_PASSWORD!
|
||||
|
||||
interface DockerContainer {
|
||||
Id: string;
|
||||
@ -18,7 +18,7 @@ interface DockerContainer {
|
||||
|
||||
const authOpts = {
|
||||
scope: [
|
||||
"/whitelist",
|
||||
"/accesslist",
|
||||
"/version"
|
||||
],
|
||||
credentials: [
|
||||
@ -32,8 +32,8 @@ const authOpts = {
|
||||
const startupChecks = async function startupChecks() {
|
||||
|
||||
|
||||
if (!process.env.WL_FILE_PATH) {
|
||||
console.warn(`WL_FILE_PATH is missing in env. Using default ${whitelistFilePath}`)
|
||||
if (!process.env.TRACKER_HELPER_ACCESSLIST_PATH) {
|
||||
console.warn(`TRACKER_HELPER_ACCESSLIST_PATH is missing in env. Using default ${accesslistFilePath}`)
|
||||
}
|
||||
|
||||
}
|
||||
@ -41,9 +41,9 @@ const startupChecks = async function startupChecks() {
|
||||
|
||||
|
||||
|
||||
const getWhitelist = async function getWhitelist(ctx: Context) {
|
||||
const wl = Bun.file(whitelistFilePath); // relative to cwd
|
||||
console.debug(`read from whitelist file at ${whitelistFilePath}. size=${wl.size}, type=${wl.type}`)
|
||||
const getAccesslist = async function getAccesslist(ctx: Context) {
|
||||
const wl = Bun.file(accesslistFilePath); // relative to cwd
|
||||
console.debug(`read from accesslist file at ${accesslistFilePath}. size=${wl.size}, type=${wl.type}`)
|
||||
return wl.text()
|
||||
}
|
||||
|
||||
@ -127,11 +127,11 @@ const maybeKillContainer = async function maybeKillContainer(signal: string = "S
|
||||
}
|
||||
|
||||
|
||||
const postWhitelist = async function postWhitelist(ctx: Context) {
|
||||
const postAccesslist = async function postAccesslist(ctx: Context) {
|
||||
let body = ctx.body
|
||||
|
||||
console.debug('appending to whitelist at ' + whitelistFilePath)
|
||||
await appendFile(whitelistFilePath, body + "\n");
|
||||
console.debug('appending to accesslist at ' + accesslistFilePath)
|
||||
await appendFile(accesslistFilePath, body + "\n");
|
||||
|
||||
await maybeKillContainer("SIGUSR1")
|
||||
|
||||
@ -148,8 +148,8 @@ const app = new Elysia()
|
||||
.use(basicAuth(authOpts))
|
||||
.get('/health', () => 'OK')
|
||||
.get('/version', () => `version ${version}`)
|
||||
.get('/whitelist', getWhitelist)
|
||||
.post('/whitelist', postWhitelist, {
|
||||
.get('/accesslist', getAccesslist)
|
||||
.post('/accesslist', postAccesslist, {
|
||||
body: t.String()
|
||||
})
|
||||
|
156
services/tracker/test/app.test.ts
Normal file
156
services/tracker/test/app.test.ts
Normal file
@ -0,0 +1,156 @@
|
||||
import {
|
||||
describe
|
||||
, expect
|
||||
, it
|
||||
, beforeEach
|
||||
} from 'bun:test'
|
||||
import {
|
||||
Elysia
|
||||
} from 'elysia'
|
||||
import {
|
||||
treaty
|
||||
} from '@elysiajs/eden'
|
||||
import app from '../app.ts'
|
||||
import Docker from 'dockerode'
|
||||
import { mkdir } from "node:fs/promises";
|
||||
import path from 'node:path'
|
||||
|
||||
|
||||
if (!process.env.TRACKER_HELPER_USERNAME) throw new Error("TRACKER_HELPER_USERNAME is missing in env.");
|
||||
if (!process.env.TRACKER_HELPER_PASSWORD) throw new Error("TRACKER_HELPER_PASSWORD is missing in env.");
|
||||
|
||||
const accesslistFilePath = process.env.TRACKER_HELPER_ACCESSLIST_PATH || "/var/lib/aquatic/accesslist"
|
||||
const fixture = "3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01" // ubuntustudio-22.04.5-dvd-amd64.iso
|
||||
const fixture2 = "b22ea43e4c0a7f73fc706b5faf1c35bb078d3722" // Solus-GNOME-Release-2025-01-26.iso
|
||||
|
||||
const username = process.env.TRACKER_HELPER_USERNAME!
|
||||
const password = process.env.TRACKER_HELPER_PASSWORD!
|
||||
const opts = {
|
||||
headers: {
|
||||
authorization: "Basic " + btoa(username + ':' + password)
|
||||
}
|
||||
}
|
||||
const api = treaty(app)
|
||||
|
||||
|
||||
|
||||
describe
|
||||
('tracker-helper', () => {
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
console.log(`Asserting existence of accesslist at ${accesslistFilePath}`);
|
||||
|
||||
const assertAccesslistExists = async function (accesslistFilePath: string) {
|
||||
const wlFile = Bun.file(accesslistFilePath);
|
||||
const exists = await wlFile.exists();
|
||||
|
||||
if (!exists) {
|
||||
console.log(`Creating accesslist file at ${accesslistFilePath}`);
|
||||
|
||||
// Ensure the parent directory exists
|
||||
await mkdir(path.dirname(accesslistFilePath), { recursive: true });
|
||||
|
||||
await wlFile.write(`${fixture2}\n${fixture}\n`);
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
await assertAccesslistExists(accesslistFilePath);
|
||||
});
|
||||
|
||||
it('return a health response', async () => {
|
||||
const { data, status } = await api.health.get()
|
||||
expect(status).toBe(200)
|
||||
expect(data).toBe("OK")
|
||||
})
|
||||
|
||||
it('return a version', async () => {
|
||||
const { data, status } = await api.version.get(opts)
|
||||
expect(status).toBe(200)
|
||||
expect(data).toContain("version")
|
||||
})
|
||||
|
||||
it('return a accesslist', async () => {
|
||||
const { data, status } = await api.accesslist.get(opts)
|
||||
expect(status).toBe(200)
|
||||
expect(data).toContain(fixture)
|
||||
})
|
||||
|
||||
it('expects the accesslist to already exist', async () => {
|
||||
const accesslist = Bun.file(accesslistFilePath)
|
||||
const accesslistExists = await accesslist.exists()
|
||||
expect(accesslistExists).toBe(true)
|
||||
})
|
||||
|
||||
it('appends a new info_hash to the accesslist file', async () => {
|
||||
|
||||
|
||||
// make an api call which is supposed to add an entry to the accesslist
|
||||
const { data, status } = await api.accesslist.post(fixture, opts)
|
||||
|
||||
// assert that the entry has been added to the accesslist
|
||||
|
||||
|
||||
const w = Bun.file(accesslistFilePath)
|
||||
const accesslistAfter = await w.text()
|
||||
console.log('accesslistAfter as follows')
|
||||
console.log(accesslistAfter)
|
||||
|
||||
expect(status).toBe(201)
|
||||
expect(data).toMatch(fixture)
|
||||
expect(accesslistAfter).toMatch(fixture)
|
||||
|
||||
})
|
||||
|
||||
// it('sends a SIGHUP to opentracker', async () => {
|
||||
|
||||
// const { data, status } = await api.accesslist.post(fixture, opts)
|
||||
// const containerId = "act-ci-Tests-Checks-6e6f12196682961041a41a25b9d0dcf00e4d0f8e58f-7cb37eebfe9e1670328d58ad1f7c7bdf0fa078298ca6dd299e67d0141a4b9579"
|
||||
// // await docker.getContainer(containerId).kill({ signal: 'SIGHUP' })
|
||||
// let container = await docker.getContainer(containerId)
|
||||
// container.inspect
|
||||
|
||||
// })
|
||||
|
||||
|
||||
// // This is skipped because I couldn't figure out opentracker's accesslist add/delete via FIFO functionality.
|
||||
// // I got as far as writing to the FIFO, and seeing opentracker acknowledge the line in it's logs.
|
||||
// // Despite this, requests from qbittorrent to opentracker responded with,
|
||||
// // "Requested download is not authorized for use with this tracker"
|
||||
// // About a week on this problem, and I give up! Using the accesslist reloading strat instead.
|
||||
// it.skip('writes a new info_hash to a fifo', async () => {
|
||||
// const fifoFilePath = process.env.TRACKER_HELPER_FIFO_PATH!
|
||||
// const fifo = Bun.file(fifoFilePath)
|
||||
// const fifoExists = await fifo.exists();
|
||||
|
||||
|
||||
// // create fifo if it doesn't exist
|
||||
// if (!fifoExists) {
|
||||
// await Bun.spawn(["mkfifo", fifoFilePath]).exited;
|
||||
// }
|
||||
|
||||
// // Start a process to read from the FIFO
|
||||
// const reader = Bun.spawn(["cat", fifoFilePath], { stdout: "pipe" });
|
||||
|
||||
// const { data, status } = await api.accesslist.post("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01", opts)
|
||||
|
||||
// const text = await new Response(reader.stdout).text();
|
||||
|
||||
// expect(text).toBe("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01\n")
|
||||
// expect(status).toBe(200)
|
||||
// expect(data).toBe("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01")
|
||||
// })
|
||||
|
||||
it('returns 401 when username/password is missing from GET /accesslist ', async () => {
|
||||
const { status } = await api.accesslist.get()
|
||||
expect(status).toBe(401)
|
||||
})
|
||||
|
||||
it('returns 401 when username/password is missing from POST /accesslist ', async () => {
|
||||
const { status } = await api.accesslist.post()
|
||||
expect(status).toBe(401)
|
||||
})
|
||||
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user