fix: fix testing torrents container
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
FROM node:23-slim AS nodebt
|
||||
RUN npm install -g pnpm
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
|
||||
FROM nodebt AS deps
|
||||
|
||||
RUN mkdir -p /app/workspace
|
||||
WORKDIR /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
|
||||
|
||||
COPY main.ts /app/
|
||||
|
||||
CMD [ "npm", "start" ]
|
||||
CMD [ "npm", "start" ]
|
||||
|
||||
@@ -1,39 +1,40 @@
|
||||
import Fastify from 'fastify';
|
||||
import fastifyStatic from '@fastify/static';
|
||||
import { join } from 'node:path';
|
||||
import Fastify from 'fastify';
|
||||
|
||||
import WebTorrent, { Torrent } from 'webtorrent';
|
||||
import createTorrent from 'create-torrent'
|
||||
import fs from 'node:fs';
|
||||
import fsp from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
// @ts-ignore
|
||||
import TrackerServer from 'bittorrent-tracker/server'
|
||||
import fs, { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
||||
import TrackerServer from 'bittorrent-tracker/server';
|
||||
import createTorrent from 'create-torrent';
|
||||
import WebTorrent, { type Torrent } from 'webtorrent';
|
||||
|
||||
// Configuration
|
||||
const API_PORT = 6080;
|
||||
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 WORKSPACE_PATH = 'workspace';
|
||||
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
|
||||
const app = Fastify({ logger: true });
|
||||
|
||||
// Mount static file service, mapping ./workspace directory to /api/static route
|
||||
app.register(fastifyStatic, {
|
||||
root: join(process.cwd(), 'workspace'),
|
||||
prefix: '/api/static',
|
||||
root: path.join(process.cwd(), WORKSPACE_PATH),
|
||||
prefix: STATIC_API_PATH,
|
||||
});
|
||||
|
||||
const tracker = new TrackerServer({
|
||||
udp: true, // enable udp server? [default=true]
|
||||
http: true, // enable http server? [default=true]
|
||||
ws: true, // enable websocket server? [default=true]
|
||||
stats: true, // enable web-based statistics? [default=true]
|
||||
trustProxy: true, // enable trusting x-forwarded-for header for remote IP [default=false]
|
||||
udp: true, // enable udp server? [default=true]
|
||||
http: true, // enable http server? [default=true]
|
||||
ws: true, // enable websocket server? [default=true]
|
||||
stats: true, // enable web-based statistics? [default=true]
|
||||
trustProxy: true, // enable trusting x-forwarded-for header for remote IP [default=false]
|
||||
});
|
||||
|
||||
|
||||
// Define request and response type definitions
|
||||
interface FileItem {
|
||||
path: string;
|
||||
@@ -52,25 +53,22 @@ interface ResponseSchema {
|
||||
|
||||
// Start local Tracker
|
||||
async function startTracker(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
tracker.listen(TRACKER_PORT, "localhost", () => {
|
||||
console.log(`Tracker listening on port ${TRACKER_PORT}`);
|
||||
resolve();
|
||||
});
|
||||
tracker.on('error', (err: any) => {
|
||||
console.error(`Tracker error: ${err}`)
|
||||
reject(`Tracker error: ${err}`);
|
||||
});
|
||||
tracker.on('warning', (warn: any) => console.warn(`Tracker warning: ${warn}`));
|
||||
// Log tracked torrents
|
||||
tracker.on('update', (addr: any, params: any) => {
|
||||
console.log(`Tracker update: ${params.info_hash} from ${addr}`);
|
||||
});
|
||||
tracker.on('stop', function () {
|
||||
reject(`Tracker stopped`);
|
||||
})
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
tracker.listen(TRACKER_PORT, 'localhost', () => {
|
||||
console.log(`Tracker listening on port ${TRACKER_PORT}`);
|
||||
resolve();
|
||||
});
|
||||
tracker.on('error', (err: any) => {
|
||||
reject(`Tracker error: ${err}`);
|
||||
});
|
||||
tracker.on('warning', (warn: any) =>
|
||||
console.warn(`Tracker warning: ${warn}`)
|
||||
);
|
||||
// Log tracked torrents
|
||||
tracker.on('update', (addr: any, params: any) => {
|
||||
console.log(`Tracker update: ${params.info_hash} from ${addr}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Tracker and WebTorrent client
|
||||
@@ -78,73 +76,80 @@ const webTorrent = new WebTorrent({});
|
||||
|
||||
// Generate mock file
|
||||
async function generateMockFile(filePath: string, size: number) {
|
||||
const dir = join(filePath, '..');
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true })
|
||||
};
|
||||
const dir = path.dirname(filePath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
await fsp.mkdir(dir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(filePath, 'w');
|
||||
fs.truncateSync(filePath, size);
|
||||
await fsp.truncate(filePath, size);
|
||||
}
|
||||
|
||||
// Generate torrent file
|
||||
function generateTorrent(folderPath: string, torrentPath: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
createTorrent(
|
||||
folderPath,
|
||||
{
|
||||
announceList: [[TRACKER_URL]], // Specify tracker URL
|
||||
private: false,
|
||||
createdBy: 'WebTorrent',
|
||||
comment: 'Generated by WebTorrent server',
|
||||
urlList: [API_BASE_URL]
|
||||
},
|
||||
(err, torrent) => {
|
||||
if (err) {
|
||||
reject(new Error(`Failed to create torrent: ${err}`));
|
||||
return;
|
||||
}
|
||||
writeFileSync(torrentPath, torrent);
|
||||
if (!existsSync(torrentPath)) {
|
||||
reject(new Error(`Torrent file ${torrentPath} was not created`));
|
||||
return;
|
||||
}
|
||||
console.log(`Generated torrent with tracker: ${TRACKER_URL}`);
|
||||
resolve();
|
||||
function generateTorrent(folderPath: string, torrentPath: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
createTorrent(
|
||||
folderPath,
|
||||
{
|
||||
announceList: [[TRACKER_URL]], // Specify tracker URL
|
||||
private: false,
|
||||
createdBy: 'WebTorrent',
|
||||
comment: 'Generated by WebTorrent server',
|
||||
urlList: [API_BASE_URL],
|
||||
},
|
||||
async (err, torrent) => {
|
||||
if (err) {
|
||||
reject(new Error(`Failed to create torrent: ${err}`));
|
||||
return;
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
await fsp.writeFile(torrentPath, torrent);
|
||||
if (!fs.existsSync(torrentPath)) {
|
||||
reject(new Error(`Torrent file ${torrentPath} was not created`));
|
||||
return;
|
||||
}
|
||||
console.log(`Generated torrent with tracker: ${TRACKER_URL}`);
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Add torrent and seed
|
||||
async function seedTorrent(torrentPath: string): Promise<Torrent> {
|
||||
return new Promise((resolve) => {
|
||||
const torrent = webTorrent.seed(torrentPath, {
|
||||
announce: [TRACKER_URL],
|
||||
}, (t) => {
|
||||
resolve(t);
|
||||
});
|
||||
torrent.on('error', (err) => console.error(`Torrent error: ${err}`));
|
||||
torrent.on('wire', (wire) => console.log(`Connected to peer: ${wire.peerId}`));
|
||||
torrent.on('done', () => console.log(`Torrent ${torrent.infoHash} fully seeded`));
|
||||
})
|
||||
return new Promise((resolve) => {
|
||||
const torrent = webTorrent.seed(
|
||||
torrentPath,
|
||||
{
|
||||
announce: [TRACKER_URL],
|
||||
},
|
||||
(t) => {
|
||||
resolve(t);
|
||||
}
|
||||
);
|
||||
torrent.on('error', (err) => console.error(`Torrent error: ${err}`));
|
||||
torrent.on('wire', (wire) =>
|
||||
console.log(`Connected to peer: ${wire.peerId}`)
|
||||
);
|
||||
torrent.on('done', () =>
|
||||
console.log(`Torrent ${torrent.infoHash} fully seeded`)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle POST request to /api/torrents/mock
|
||||
app.post<{ Body: RequestSchema }>('/api/torrents/mock', async (req, _reply) => {
|
||||
const { id, fileList } = req.body;
|
||||
|
||||
const idFolder = join('./workspace', id);
|
||||
if (!existsSync(idFolder)) {
|
||||
mkdirSync(idFolder, { recursive: true })
|
||||
};
|
||||
const idFolder = path.join(WORKSPACE_PATH, id);
|
||||
if (!fs.existsSync(idFolder)) {
|
||||
await fsp.mkdir(idFolder, { recursive: true });
|
||||
}
|
||||
|
||||
for (const fileItem of fileList) {
|
||||
const filePath = join(idFolder, fileItem.path);
|
||||
const filePath = path.join(idFolder, fileItem.path);
|
||||
await generateMockFile(filePath, fileItem.size);
|
||||
}
|
||||
|
||||
const torrentPath = join('./workspace', `${id}.torrent`);
|
||||
const torrentPath = path.join(WORKSPACE_PATH, `${id}.torrent`);
|
||||
await generateTorrent(idFolder, torrentPath);
|
||||
|
||||
const torrent = await seedTorrent(torrentPath);
|
||||
@@ -160,8 +165,7 @@ app.post<{ Body: RequestSchema }>('/api/torrents/mock', async (req, _reply) => {
|
||||
async function main() {
|
||||
try {
|
||||
await startTracker();
|
||||
await app.listen({ port: API_PORT, host: '0.0.0.0' });
|
||||
console.log(`Fastify running on http://0.0.0.0:${API_PORT}`);
|
||||
await app.listen({ port: API_PORT, host: LOCAL_IP });
|
||||
} catch (err) {
|
||||
console.error('Startup error:', err);
|
||||
webTorrent.destroy();
|
||||
@@ -174,8 +178,8 @@ main();
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGINT', () => {
|
||||
console.log('Shutting down...');
|
||||
tracker.close();
|
||||
webTorrent.destroy();
|
||||
process.exit(0);
|
||||
});
|
||||
console.log('Shutting down...');
|
||||
tracker.close();
|
||||
webTorrent.destroy();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
{
|
||||
"name": "@konobangu/testing-torrents",
|
||||
"version": "0.0.1",
|
||||
"description": "Kono bangumi? Testing Torrents",
|
||||
"main": "main.ts",
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
"start": "tsx main.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/static": "^8.1.1",
|
||||
"bittorrent-tracker": "^11.2.1",
|
||||
"create-torrent": "^6.1.0",
|
||||
"fastify": "^5.2.2",
|
||||
"tsx": "^4.19.2",
|
||||
"webtorrent": "^2.5.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/create-torrent": "^5.0.2",
|
||||
"@types/webtorrent": "^0.110.0"
|
||||
}
|
||||
}
|
||||
"name": "@konobangu/testing-torrents",
|
||||
"version": "0.0.1",
|
||||
"description": "Kono bangumi? Testing Torrents",
|
||||
"main": "main.ts",
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
"start": "tsx main.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/static": "^8.1.1",
|
||||
"bittorrent-tracker": "^11.2.1",
|
||||
"create-torrent": "^6.1.0",
|
||||
"fastify": "^5.2.2",
|
||||
"tsx": "^4.19.2",
|
||||
"webtorrent": "^2.5.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/create-torrent": "^5.0.2",
|
||||
"@types/webtorrent": "^0.110.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"utf-8-validate",
|
||||
"node-datachannel",
|
||||
"utp-native"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user