diff --git a/.gitignore b/.gitignore index f6390af..cd64728 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .env node_modules -static +static_assets host_info.json *.js package-lock.json diff --git a/README.md b/README.md index ad53189..fb0d31f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ The most legalest private file (specifically for manga, anime, music) hosting ever, on TOR. Includes a manga viewer. Nothing fancy. Uses HTTP Basic Authentication with daily rotating password derived from a master password. -Most of the code is a modification of [hedgeblog](https://github.com/jetstream0/hedgeblog), albeit with some pretty significant modifications in places (templates, host.ts, build.ts, saki.ts). +Most of the code is a modification of [hedgeblog](https://github.com/jetstream0/hedgeblog), albeit with some pretty significant modifications in places (templates, host.ts, build.ts). -Since TOR is pretty slow to download large files (eg, videos), the videos are streamed so load time isn't as bad as one might expect (HTTP protocol is amazing, browsers are amazing!). +Since TOR is pretty slow to download large files (eg, videos), the videos are streamed so load time isn't as bad as one might expect (HTTP protocol is amazing, browsers are amazing). # Running After first cloning or downloading the repository: @@ -25,4 +25,23 @@ Then to actually run: bash tor_prebuilt.sh ``` +# Tips + +## Adding media +Add it to the relevant static directory (`/static_assets/anime_assets`, `/static_assets/manga_assets`, or `/static_assets/music_assets`), and create an entry for it in `host_info.json`. + +## Hosting Multiple TOR Hidden Services If you are running multiple TOR hidden services, you will need to modify the [.torrc file](https://stackoverflow.com/questions/14321214/how-to-run-multiple-tor-processes-at-once-with-different-exit-ips#18895491). + +## Master Password +To set the master password, create a `.env` file like so: + +``` +master_password=example_password_do_not_actually_use_this_as_your_password_obviously +``` + +## Username +The username doesn't matter. Only the password is important. + +# Disclaimer +blah blah blah meant for private use, I would never dream of breaking copyright law blah blah blah not the intended purpose. diff --git a/build.ts b/build.ts index 693f1e6..b8f9de1 100644 --- a/build.ts +++ b/build.ts @@ -36,17 +36,10 @@ interface MangaVars { const listings: Listing[] = _host_info.listings.filter((listing: any): listing is Listing => typeof listing.name === "string" && (listing?.type === "anime" || listing?.type === "manga" || listing?.type === "music")); let renderer: Renderer = new Renderer("templates", "components"); -let builder: Builder; -if (process.argv[2] === "--quick") { - builder = new Builder("/build", ["anime_assets", "manga_assets", "music_assets", "password"]); //password is included since it is static -} else { - builder = new Builder(); -} +let builder: Builder = new Builder("/build");; //static page -if (process.argv[2] !== "--quick") { - builder.serve_static_folder("static"); -} +builder.serve_static_folder("static"); //main page builder.serve_template(renderer, "/", "index", { @@ -71,7 +64,7 @@ let songs: string[] = []; for (let i = 0; i < listings.length; i++) { const listing: Listing = listings[i]; directory_serve_paths.push(`/${listing.type}/${listing.name}`); - const chapters: string[] = readdirSync(path.join(__dirname, `/static/${listing.type}_assets/${listing.name}`), { withFileTypes: true }).map((d) => d.name.replace(".mp4", "").replace(".mp3", "")); + const chapters: string[] = readdirSync(path.join(__dirname, `/static_assets/${listing.type}_assets/${listing.name}`), { withFileTypes: true }).map((d) => d.name.replace(".mp4", "").replace(".mp3", "")); directory_vars.push({ listing, chapters, @@ -91,7 +84,7 @@ for (let i = 0; i < listings.length; i++) { for (let j = 0; j < chapters.length; j++) { const chapter: string = chapters[j]; manga_serve_paths.push(`/${listing.type}/${listing.name}/${chapter}`); - const images: string[] = readdirSync(path.join(__dirname, `/static/${listing.type}_assets/${listing.name}/${chapter}`), { withFileTypes: true }).map((d) => d.name); + const images: string[] = readdirSync(path.join(__dirname, `/static_assets/${listing.type}_assets/${listing.name}/${chapter}`), { withFileTypes: true }).map((d) => d.name); manga_vars.push({ listing, chapter, diff --git a/host.ts b/host.ts index d6fc023..af8e3a6 100644 --- a/host.ts +++ b/host.ts @@ -28,12 +28,17 @@ createServer((req, res) => { //end response return res.end(); } + const url_obj = new URL(req.url, `http://${req.headers.host}`); if (!req.url.includes(".")) { req_path = path.join(__dirname, "build", decodeURI(req.url), "index.html"); } else { - req_path = path.join(__dirname, "build", decodeURI(req.url)); + //is file + if (url_obj.pathname.startsWith("/anime_assets") || url_obj.pathname.startsWith("/manga_assets") || url_obj.pathname.startsWith("/music_assets")) { + req_path = path.join(__dirname, "static_assets", decodeURI(req.url)); + } else { + req_path = path.join(__dirname, "build", decodeURI(req.url)); + } } - const url_obj = new URL(req.url, `http://${req.headers.host}`); //check for auth //hopefully no security vulnerabilities. please look away if (url_obj.pathname !== "/password") { @@ -64,10 +69,11 @@ createServer((req, res) => { res.write("404"); return res.end(); } + const file_ext: string = req_path.split(".")[1]; //set content type let non_utf8_content_types: string[] = ["image/png", "image/gif", "image/jpeg", "audio/mpeg", "video/mp4"]; let content_type: string; - switch (req_path.split(".")[1]) { + switch (file_ext) { case "html": content_type = "text/html; charset=utf-8"; break; diff --git a/package.json b/package.json index 05f0057..84bdb5d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "scripts": { "compile": "tsc -p .", "build": "node build.js", - "build-quick": "node build.js --quick", "host": "node host.js" }, "repository": { diff --git a/saki.ts b/saki.ts index 022d580..fdc7447 100644 --- a/saki.ts +++ b/saki.ts @@ -4,33 +4,19 @@ import type { Renderer } from './ryuji.js'; export class Builder { build_dir: string; - ignore?: string[]; //top level directories/files to not wipe - constructor(build_dir: string="/build", ignore?: string[]) { + constructor(build_dir: string="/build") { this.build_dir = path.join(__dirname, build_dir); - this.ignore = ignore; if (existsSync(this.build_dir)) { //wipe the build directory - if (!this.ignore || this.ignore?.length === 0) { - rmSync(this.build_dir, { - recursive: true, - }); - } else { - //do not wipe certain directories/files - readdirSync(this.build_dir).forEach((member: string) => { - if (!this.ignore.includes(member)) { - rmSync(path.join(this.build_dir, member), { - recursive: true, - }); - } - }); - } - } - if (!this.ignore || this.ignore?.length === 0) { - mkdirSync(this.build_dir); + rmSync(this.build_dir, { + recursive: true, + }); } + mkdirSync(this.build_dir); } + //todo: option where if path already exists, let it be (much faster build times) static copy_folder(folder_path: string, dest_path: string) { let children: string[] = readdirSync(folder_path); for (let i=0; i < children.length; i++) { @@ -42,7 +28,7 @@ export class Builder { copyFileSync(child_path, copy_path); } else { //directory, make directory and recursively copy - mkdirSync(copy_path); + if (!existsSync(copy_path)) mkdirSync(copy_path); Builder.copy_folder(child_path, path.join(dest_path, child)); } } diff --git a/static/password/index.html b/static/password/index.html new file mode 100644 index 0000000..c8ac717 --- /dev/null +++ b/static/password/index.html @@ -0,0 +1,37 @@ + + +
+ + +