fix: fix testing torrents container

This commit is contained in:
master 2025-04-01 03:45:56 +08:00
parent a0fc4c04d9
commit 1fca69fa66
10 changed files with 159 additions and 1721 deletions

View File

@ -3,6 +3,11 @@ name: Build and Push Testing Torrents Container
on: on:
workflow_dispatch: workflow_dispatch:
env:
REGISTRY: ghcr.io
ORG: dumtruck
PROJECT: konobangu
jobs: jobs:
build-container: build-container:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -23,6 +28,6 @@ jobs:
context: 'packages/testing-torrents' context: 'packages/testing-torrents'
file: './Dockerfile' file: './Dockerfile'
push: true push: true
tags: 'ghcr.io/${{ env.ORG }}/konobangu-testing-torrents:latest' tags: 'ghcr.io/${{ env.ORG }}/${{ env.PROJECT }}-testing-torrents:latest'
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max

View File

@ -5,7 +5,6 @@
"unifiedjs.vscode-mdx", "unifiedjs.vscode-mdx",
"mikestead.dotenv", "mikestead.dotenv",
"christian-kohler.npm-intellisense", "christian-kohler.npm-intellisense",
"skellock.just", "skellock.just"
"charliermarsh.ruff"
] ]
} }

View File

@ -28,8 +28,5 @@
"emmet.showExpandedAbbreviation": "never", "emmet.showExpandedAbbreviation": "never",
"prettier.enable": false, "prettier.enable": false,
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"rust-analyzer.cargo.features": ["testcontainers"], "rust-analyzer.cargo.features": ["testcontainers"]
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff"
}
} }

View File

@ -10,7 +10,9 @@
"noNonNullAssertion": "off" "noNonNullAssertion": "off"
}, },
"suspicious": { "suspicious": {
"noExplicitAny": "off" "noExplicitAny": "off",
"noConsole": "off",
"noConsoleLog": "off"
}, },
"a11y": { "a11y": {
"noSvgWithoutTitle": "off" "noSvgWithoutTitle": "off"
@ -27,14 +29,6 @@
} }
}, },
"files": { "files": {
"ignore": [ "ignore": [".vscode/*.json"]
"packages/design-system/components/ui/**",
"packages/design-system/lib/**",
"packages/design-system/hooks/**",
"packages/collaboration/config.ts",
"apps/docs/**/*.json",
"apps/email/.react-email/**",
".vscode/*.json"
]
} }
} }

View File

@ -1,31 +0,0 @@
version: '3'
services:
webui:
image: node:22-alpine
ports:
- '5000:5000'
volumes:
- ./apps/webui:/home/node/app
- node_modules:/home/node/app/node_modules
working_dir: /home/node/app/
command: sh -c "yarn install && yarn dev"
depends_on:
- mongo
env_file:
- .env
mongo:
image: mongo:latest
ports:
- '27017:27017'
command:
- --storageEngine=wiredTiger
volumes:
- data:/data/db
logging:
driver: none
volumes:
data:
node_modules:

View File

@ -1,11 +1,14 @@
FROM node:23-slim AS nodebt FROM node:23-slim AS nodebt
RUN npm install -g pnpm
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
FROM nodebt AS deps FROM nodebt AS deps
RUN mkdir -p /app/workspace RUN mkdir -p /app/workspace
WORKDIR /app WORKDIR /app
COPY package.json /app/ COPY package.json /app/
RUN npm install RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --no-frozen-lockfile
FROM deps AS app FROM deps AS app

View File

@ -1,28 +1,30 @@
import Fastify from 'fastify';
import fastifyStatic from '@fastify/static'; import fastifyStatic from '@fastify/static';
import { join } from 'node:path'; import Fastify from 'fastify';
import WebTorrent, { Torrent } from 'webtorrent'; import fs from 'node:fs';
import createTorrent from 'create-torrent' import fsp from 'node:fs/promises';
import path from 'node:path';
// @ts-ignore // @ts-ignore
import TrackerServer from 'bittorrent-tracker/server' import TrackerServer from 'bittorrent-tracker/server';
import fs, { existsSync, mkdirSync, writeFileSync } from 'node:fs'; import createTorrent from 'create-torrent';
import WebTorrent, { type Torrent } from 'webtorrent';
// Configuration // Configuration
const API_PORT = 6080; const API_PORT = 6080;
const TRACKER_PORT = 6081; const TRACKER_PORT = 6081;
// Get local IP address for broader accessibility const STATIC_API_PATH = '/api/static';
const LOCAL_IP = '127.0.0.1'; const LOCAL_IP = '127.0.0.1';
const WORKSPACE_PATH = 'workspace';
const TRACKER_URL = `http://${LOCAL_IP}:${TRACKER_PORT}/announce`; const TRACKER_URL = `http://${LOCAL_IP}:${TRACKER_PORT}/announce`;
const API_BASE_URL = `http://${LOCAL_IP}:${API_PORT}/api/static/`; const API_BASE_URL = `http://${LOCAL_IP}:${API_PORT}/${STATIC_API_PATH}/`;
// Initialize Fastify instance // Initialize Fastify instance
const app = Fastify({ logger: true }); const app = Fastify({ logger: true });
// Mount static file service, mapping ./workspace directory to /api/static route // Mount static file service, mapping ./workspace directory to /api/static route
app.register(fastifyStatic, { app.register(fastifyStatic, {
root: join(process.cwd(), 'workspace'), root: path.join(process.cwd(), WORKSPACE_PATH),
prefix: '/api/static', prefix: STATIC_API_PATH,
}); });
const tracker = new TrackerServer({ const tracker = new TrackerServer({
@ -33,7 +35,6 @@ const tracker = new TrackerServer({
trustProxy: true, // enable trusting x-forwarded-for header for remote IP [default=false] trustProxy: true, // enable trusting x-forwarded-for header for remote IP [default=false]
}); });
// Define request and response type definitions // Define request and response type definitions
interface FileItem { interface FileItem {
path: string; path: string;
@ -53,23 +54,20 @@ interface ResponseSchema {
// Start local Tracker // Start local Tracker
async function startTracker(): Promise<void> { async function startTracker(): Promise<void> {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
tracker.listen(TRACKER_PORT, "localhost", () => { tracker.listen(TRACKER_PORT, 'localhost', () => {
console.log(`Tracker listening on port ${TRACKER_PORT}`); console.log(`Tracker listening on port ${TRACKER_PORT}`);
resolve(); resolve();
}); });
tracker.on('error', (err: any) => { tracker.on('error', (err: any) => {
console.error(`Tracker error: ${err}`)
reject(`Tracker error: ${err}`); reject(`Tracker error: ${err}`);
}); });
tracker.on('warning', (warn: any) => console.warn(`Tracker warning: ${warn}`)); tracker.on('warning', (warn: any) =>
console.warn(`Tracker warning: ${warn}`)
);
// Log tracked torrents // Log tracked torrents
tracker.on('update', (addr: any, params: any) => { tracker.on('update', (addr: any, params: any) => {
console.log(`Tracker update: ${params.info_hash} from ${addr}`); console.log(`Tracker update: ${params.info_hash} from ${addr}`);
}); });
tracker.on('stop', function () {
reject(`Tracker stopped`);
})
}); });
} }
@ -78,18 +76,17 @@ const webTorrent = new WebTorrent({});
// Generate mock file // Generate mock file
async function generateMockFile(filePath: string, size: number) { async function generateMockFile(filePath: string, size: number) {
const dir = join(filePath, '..'); const dir = path.dirname(filePath);
if (!existsSync(dir)) { if (!fs.existsSync(dir)) {
mkdirSync(dir, { recursive: true }) await fsp.mkdir(dir, { recursive: true });
}; }
fs.writeFileSync(filePath, 'w'); await fsp.truncate(filePath, size);
fs.truncateSync(filePath, size);
} }
// Generate torrent file // Generate torrent file
function generateTorrent(folderPath: string, torrentPath: string): Promise<void> { function generateTorrent(folderPath: string, torrentPath: string) {
return new Promise((resolve, reject) => { return new Promise<void>((resolve, reject) => {
createTorrent( createTorrent(
folderPath, folderPath,
{ {
@ -97,15 +94,15 @@ function generateTorrent(folderPath: string, torrentPath: string): Promise<void>
private: false, private: false,
createdBy: 'WebTorrent', createdBy: 'WebTorrent',
comment: 'Generated by WebTorrent server', comment: 'Generated by WebTorrent server',
urlList: [API_BASE_URL] urlList: [API_BASE_URL],
}, },
(err, torrent) => { async (err, torrent) => {
if (err) { if (err) {
reject(new Error(`Failed to create torrent: ${err}`)); reject(new Error(`Failed to create torrent: ${err}`));
return; return;
} }
writeFileSync(torrentPath, torrent); await fsp.writeFile(torrentPath, torrent);
if (!existsSync(torrentPath)) { if (!fs.existsSync(torrentPath)) {
reject(new Error(`Torrent file ${torrentPath} was not created`)); reject(new Error(`Torrent file ${torrentPath} was not created`));
return; return;
} }
@ -119,32 +116,40 @@ function generateTorrent(folderPath: string, torrentPath: string): Promise<void>
// Add torrent and seed // Add torrent and seed
async function seedTorrent(torrentPath: string): Promise<Torrent> { async function seedTorrent(torrentPath: string): Promise<Torrent> {
return new Promise((resolve) => { return new Promise((resolve) => {
const torrent = webTorrent.seed(torrentPath, { const torrent = webTorrent.seed(
torrentPath,
{
announce: [TRACKER_URL], announce: [TRACKER_URL],
}, (t) => { },
(t) => {
resolve(t); resolve(t);
}); }
);
torrent.on('error', (err) => console.error(`Torrent error: ${err}`)); torrent.on('error', (err) => console.error(`Torrent error: ${err}`));
torrent.on('wire', (wire) => console.log(`Connected to peer: ${wire.peerId}`)); torrent.on('wire', (wire) =>
torrent.on('done', () => console.log(`Torrent ${torrent.infoHash} fully seeded`)); console.log(`Connected to peer: ${wire.peerId}`)
}) );
torrent.on('done', () =>
console.log(`Torrent ${torrent.infoHash} fully seeded`)
);
});
} }
// Handle POST request to /api/torrents/mock // Handle POST request to /api/torrents/mock
app.post<{ Body: RequestSchema }>('/api/torrents/mock', async (req, _reply) => { app.post<{ Body: RequestSchema }>('/api/torrents/mock', async (req, _reply) => {
const { id, fileList } = req.body; const { id, fileList } = req.body;
const idFolder = join('./workspace', id); const idFolder = path.join(WORKSPACE_PATH, id);
if (!existsSync(idFolder)) { if (!fs.existsSync(idFolder)) {
mkdirSync(idFolder, { recursive: true }) await fsp.mkdir(idFolder, { recursive: true });
}; }
for (const fileItem of fileList) { for (const fileItem of fileList) {
const filePath = join(idFolder, fileItem.path); const filePath = path.join(idFolder, fileItem.path);
await generateMockFile(filePath, fileItem.size); await generateMockFile(filePath, fileItem.size);
} }
const torrentPath = join('./workspace', `${id}.torrent`); const torrentPath = path.join(WORKSPACE_PATH, `${id}.torrent`);
await generateTorrent(idFolder, torrentPath); await generateTorrent(idFolder, torrentPath);
const torrent = await seedTorrent(torrentPath); const torrent = await seedTorrent(torrentPath);
@ -160,8 +165,7 @@ app.post<{ Body: RequestSchema }>('/api/torrents/mock', async (req, _reply) => {
async function main() { async function main() {
try { try {
await startTracker(); await startTracker();
await app.listen({ port: API_PORT, host: '0.0.0.0' }); await app.listen({ port: API_PORT, host: LOCAL_IP });
console.log(`Fastify running on http://0.0.0.0:${API_PORT}`);
} catch (err) { } catch (err) {
console.error('Startup error:', err); console.error('Startup error:', err);
webTorrent.destroy(); webTorrent.destroy();

View File

@ -18,5 +18,12 @@
"devDependencies": { "devDependencies": {
"@types/create-torrent": "^5.0.2", "@types/create-torrent": "^5.0.2",
"@types/webtorrent": "^0.110.0" "@types/webtorrent": "^0.110.0"
},
"pnpm": {
"onlyBuiltDependencies": [
"utf-8-validate",
"node-datachannel",
"utp-native"
]
} }
} }

1567
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
packages: packages:
- packages/* - packages/*
- apps/* - apps/*
- '!packages/testing-torrents'
onlyBuiltDependencies: onlyBuiltDependencies:
- '@biomejs/biome' - '@biomejs/biome'
- bufferutil - bufferutil