diff --git a/README.md b/README.md index 379622b..c5bed15 100644 --- a/README.md +++ b/README.md @@ -23,23 +23,30 @@


+# This fork prevents files from becoming unavailable after 24 hours due to Discord's sharing restrictions. 😎 + +What's changing? Just add a user token to the .env ;) +


+ ##### **DDrive** A lightweight cloud storage system using discord as storage device written in nodejs. Supports an unlimited file size and unlimited storage, Implemented using node js streams with multi-part up & download. https://user-images.githubusercontent.com/59018146/167635903-48cdace0-c383-4e7d-a037-4a32eaa4ab69.mp4 + ### Features + - Theoretically unlimited file size, thanks to splitting the file in 24mb chunks using nodejs streams API. -- Simple yet robust HTTP front end +- Simple yet robust HTTP front end - Rest API with OpenAPI 3.1 specifications. - Tested with storing 4000 GB of data on single discord channel (With max file size of 16GB). - Supports basic auth with read only public access to panel. - Easily deployable on heroku/replit and use as private cloud storage. -## New Version 4.0 + ### Requirements + - NodeJS v16.x or Docker - Postgres Database, Discord Webhook URLs - Avg technical knowledge ## Setup Guide + 1. Clone this project 2. Create few webhook urls. For better performance and to avoid rate limit at least create 5 with 1 webhook / text channel. ([How to create webhook url](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks)) 3. Setup postgres using docker, if you already don't have it running @@ -74,17 +83,20 @@ I spent several weeks finalizing this new version. Any support is highly apprec - `docker-compose up -d` 4. Copy `config/.env_sample` to `config/.env` and make necessary changes 5. Optional - If you have lots of webhookURLs you can put those in `webhook.txt` with `\n` seperated. -6. Run - `npm run migration:up` -7. Run - `node bin/ddrive` -8. Navigate to `http://localhost:3000` in your browser. +6. Run - `npm install` +7. Run - `npm run migration:up` +8. Run - `node bin/ddrive` +9. Navigate to `http://localhost:3000` in your browser. ### How to keep it running forever + 1. Install pm2 with `npm install -g pm2` 2. Run - `pm2 start bin/ddrive` 3. Run - `pm2 list` to check status of ddrive 4. Run - `pm2 logs` to check ddrive logs ### Config variables explanation + ```shell # config/.env @@ -108,18 +120,39 @@ PUBLIC_ACCESS=READ_ONLY_FILE # If you want to give read only access to panel or # READ_ONLY_FILE - User will be only access download links of file and not panel # READ_ONLY_PANEL - User will be able to browse the panel for files/directories but won't be able to upload/delete/rename any file/folder. -UPLOAD_CONCURRENCY=3 # ddrive will upload this many chunks in parallel to discord. If you have fast internet increasing it will significantly increase performance at cost of cpu/disk usage +UPLOAD_CONCURRENCY=3 # ddrive will upload this many chunks in parallel to discord. If you have fast internet increasing it will significantly increase performance at cost of cpu/disk usage +USER_TOKEN= # Put real Discord User account Token (see below for steps) ``` +## How to get a user Discord token (need to be login to the account) + +[](https://github.com/ShufflePerson/Discord_CDN/blob/master/README.md#how-to-get-your-token) + +1. Open Discord on your Browser. +2. Open the Dev Tools ( Inspect Element ) +3. Go to the Console Tab and paste in the following command +4. `console.log((webpackChunkdiscord_app.push([[''],{},e=>{m=[];for(let c in e.c)m.push(e.c[c])}]),m).find(m=>m?.exports?.default?.getToken!==void 0).exports.default.getToken());` +5. Copy the output and set it in the `.env` file. (e.g `USER_TOKEN=PASTE_TOKEN_HERE`), _the account does not have to be on your ddrive server_ + ### Run using docker + ```shell docker run -rm -it -p 8080:8080 \ -e PORT=8080 \ -e WEBHOOKS={url1},{url2} \ -e DATABASE_URL={database url} \ +-e USER_TOKEN=PASTE_TOKEN_HERE \ --name ddrive forscht/ddrive ``` + +
+
+ +_...see the [original repo](https://github.com/forscht/ddrive/) for the full readme_ +Credit : Copyright 2024 ShufflePerson + + diff --git a/config/.env_sample b/config/.env_sample index bdaa32b..5239d94 100644 --- a/config/.env_sample +++ b/config/.env_sample @@ -1,12 +1,16 @@ # Required + DATABASE_URL=postgres://ddrive:ddrive@127.0.0.1:5429/ddrive // Postgres DB URL WEBHOOKS= // ',' seperated urls # Optional + PORT=3000 // HTTP Server port REQUEST_TIMEOUT=60000 // Request timeout - Increase if you have slow internet connection -CHUNK_SIZE=25165824 // 24MB - Can't increase. Webhooks are max allowed to 25MB +CHUNK_SIZE=10165824 // 10MB - Can't increase. Webhooks are max allowed to 10MB SECRET=myrandomsecret // Put here something if you want to encrypt your files. May cause very high cpu usage PUBLIC_ACCESS=READ_ONLY_FILE // ['READ_ONLY_FILE', 'READ_ONLY_PANEL'] Allow public user to access to download link of file or read only access of whole panel AUTH=admin:admin UPLOAD_CONCURRENCY= + +USER_TOKEN= // Put real Discord User account Token (see README.md for steps) diff --git a/package.json b/package.json index 479478c..0a5f7c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@forscht/ddrive", - "version": "4.3.0", + "version": "5.0.1", "description": "A lightweight cloud storage system using discord as storage device written in nodejs", "main": "src/index.js", "author": "Darshan Patel (https://github.com/forscht/ddrive)", @@ -40,8 +40,10 @@ "@fastify/auth": "^4.2.0", "@fastify/multipart": "^7.3.0", "@fastify/static": "^6.6.0", - "dotenv": "^16.0.3", + "axios": "^1.7.3", + "dotenv": "^16.4.5", "fastify": "^4.11.0", + "fs": "^0.0.1-security", "knex": "^2.4.0", "lodash": "^4.17.21", "mime-types": "^2.1.35", diff --git a/src/DFs/cdn-package/Discord/Discord.d.ts b/src/DFs/cdn-package/Discord/Discord.d.ts new file mode 100644 index 0000000..ae0a0e0 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Discord.d.ts @@ -0,0 +1,8 @@ +import IConfig from "../Types/IConfig"; +declare class Discord { + private config; + constructor(config: IConfig); + fetchLatestLink(oldLink: string): Promise; + private getHTTPConfig; +} +export default Discord; diff --git a/src/DFs/cdn-package/Discord/Discord.js b/src/DFs/cdn-package/Discord/Discord.js new file mode 100644 index 0000000..d037154 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Discord.js @@ -0,0 +1,58 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const ELinkIssue_1 = __importDefault(require("./Types/ELinkIssue")); +const ParseLink_1 = __importDefault(require("./Utils/ParseLink")); +const axios_1 = __importDefault(require("axios")); +class Discord { + constructor(config) { + this.config = config; + } + ; + fetchLatestLink(oldLink) { + return __awaiter(this, void 0, void 0, function* () { + if (!oldLink.includes("https://")) + oldLink = `https://cdn.discordapp.com/${oldLink}`; + const linkData = (0, ParseLink_1.default)(oldLink); + if (linkData.error != ELinkIssue_1.default.NONE) { + throw new Error(linkData.error); + } + try { + const { data } = yield axios_1.default.post("https://discord.com/api/v9/attachments/refresh-urls", { + attachment_urls: [oldLink] + }, this.getHTTPConfig()); + let response = data; + if (!response || !response.refreshed_urls || response.refreshed_urls.length == 0) { + console.log("response:", data); + throw new Error("Unexpected Discord response."); + } + let updatedLink = response.refreshed_urls[0].refreshed; + return updatedLink; + } + catch (ex) { + console.log(ex); + } + return ""; + }); + } + getHTTPConfig() { + return { + headers: { + "Authorization": this.config.TOKEN + } + }; + } +} +exports.default = Discord; +//# sourceMappingURL=Discord.js.map \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Discord.js.map b/src/DFs/cdn-package/Discord/Discord.js.map new file mode 100644 index 0000000..bd738fa --- /dev/null +++ b/src/DFs/cdn-package/Discord/Discord.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Discord.js","sourceRoot":"","sources":["../../src/Discord/Discord.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AACA,oEAA4C;AAE5C,kEAA0C;AAG1C,kDAAiD;AAEjD,MAAM,OAAO;IACT,YAAoB,MAAe;QAAf,WAAM,GAAN,MAAM,CAAS;IAAG,CAAC;IAAA,CAAC;IAU3B,eAAe,CAAC,OAAe;;YACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAC7B,OAAO,GAAG,8BAA8B,OAAO,EAAE,CAAC;YACtD,MAAM,QAAQ,GAAG,IAAA,mBAAS,EAAC,OAAO,CAAC,CAAC;YACpC,IAAI,QAAQ,CAAC,KAAK,IAAI,oBAAU,CAAC,IAAI,EAAE;gBACnC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;aACnC;YAED,IAAI;gBACA,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,eAAK,CAAC,IAAI,CAAC,qDAAqD,EAAE;oBACrF,eAAe,EAAE,CAAC,OAAO,CAAC;iBAC7B,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;gBAEzB,IAAI,QAAQ,GAAG,IAAuB,CAAC;gBACvC,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,IAAI,CAAC,EAAE;oBAC9E,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;oBAC/B,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;iBACnD;gBAED,IAAI,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACvD,OAAO,WAAW,CAAC;aAEtB;YAAC,OAAO,EAAE,EAAE;gBACT,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;aAClB;YAED,OAAO,EAAE,CAAC;QACd,CAAC;KAAA;IAEO,aAAa;QACjB,OAAO;YACH,OAAO,EAAE;gBACL,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;aACrC;SACJ,CAAA;IACL,CAAC;CACJ;AAED,kBAAe,OAAO,CAAC"} \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/ELinkIssue.d.ts b/src/DFs/cdn-package/Discord/Types/ELinkIssue.d.ts new file mode 100644 index 0000000..d464634 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/ELinkIssue.d.ts @@ -0,0 +1,8 @@ +declare enum ELinkIssue { + NONE = "", + INVALID_SLASH_AMOUNT = "Invalid link due to not the right amount of forward slashes", + CHANNEL_ID_NAN = "The Channel ID should be a valid integer.", + FILE_ID_NAN = "The File ID should be a valid integer.", + FILENAME_NO_DOT = "The File Name should include at least one dot." +} +export default ELinkIssue; diff --git a/src/DFs/cdn-package/Discord/Types/ELinkIssue.js b/src/DFs/cdn-package/Discord/Types/ELinkIssue.js new file mode 100644 index 0000000..0b1b056 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/ELinkIssue.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ELinkIssue; +(function (ELinkIssue) { + ELinkIssue["NONE"] = ""; + ELinkIssue["INVALID_SLASH_AMOUNT"] = "Invalid link due to not the right amount of forward slashes"; + ELinkIssue["CHANNEL_ID_NAN"] = "The Channel ID should be a valid integer."; + ELinkIssue["FILE_ID_NAN"] = "The File ID should be a valid integer."; + ELinkIssue["FILENAME_NO_DOT"] = "The File Name should include at least one dot."; +})(ELinkIssue || (ELinkIssue = {})); +exports.default = ELinkIssue; +//# sourceMappingURL=ELinkIssue.js.map \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/ELinkIssue.js.map b/src/DFs/cdn-package/Discord/Types/ELinkIssue.js.map new file mode 100644 index 0000000..232b8d5 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/ELinkIssue.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ELinkIssue.js","sourceRoot":"","sources":["../../../src/Discord/Types/ELinkIssue.ts"],"names":[],"mappings":";;AAAA,IAAK,UAMJ;AAND,WAAK,UAAU;IACX,uBAAS,CAAA;IACT,kGAAoF,CAAA;IACpF,0EAA4D,CAAA;IAC5D,oEAAsD,CAAA;IACtD,gFAAkE,CAAA;AACtE,CAAC,EANI,UAAU,KAAV,UAAU,QAMd;AAED,kBAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/ETokenIssue.d.ts b/src/DFs/cdn-package/Discord/Types/ETokenIssue.d.ts new file mode 100644 index 0000000..9ca8457 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/ETokenIssue.d.ts @@ -0,0 +1,8 @@ +declare enum ETokenIssue { + NONE = "", + INVALID_AMOUNT_OF_DOTS = "The Token has an invalid amount of dots.", + NON_BASE64_UTF8 = "The ID part of the token is not valid", + ID_NOT_A_NUMBER = "THe ID part of the token is not a number", + ID_TOO_SHORT = "The ID part of the token is too short" +} +export default ETokenIssue; diff --git a/src/DFs/cdn-package/Discord/Types/ETokenIssue.js b/src/DFs/cdn-package/Discord/Types/ETokenIssue.js new file mode 100644 index 0000000..b8d9fc1 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/ETokenIssue.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ETokenIssue; +(function (ETokenIssue) { + ETokenIssue["NONE"] = ""; + ETokenIssue["INVALID_AMOUNT_OF_DOTS"] = "The Token has an invalid amount of dots."; + ETokenIssue["NON_BASE64_UTF8"] = "The ID part of the token is not valid"; + ETokenIssue["ID_NOT_A_NUMBER"] = "THe ID part of the token is not a number"; + ETokenIssue["ID_TOO_SHORT"] = "The ID part of the token is too short"; +})(ETokenIssue || (ETokenIssue = {})); +exports.default = ETokenIssue; +//# sourceMappingURL=ETokenIssue.js.map \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/ETokenIssue.js.map b/src/DFs/cdn-package/Discord/Types/ETokenIssue.js.map new file mode 100644 index 0000000..c8ccab0 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/ETokenIssue.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ETokenIssue.js","sourceRoot":"","sources":["../../../src/Discord/Types/ETokenIssue.ts"],"names":[],"mappings":";;AAAA,IAAK,WAOJ;AAPD,WAAK,WAAW;IACZ,wBAAS,CAAA;IACT,kFAAmE,CAAA;IACnE,wEAAyD,CAAA;IACzD,2EAA4D,CAAA;IAC5D,qEAAsD,CAAA;AAE1D,CAAC,EAPI,WAAW,KAAX,WAAW,QAOf;AAED,kBAAe,WAAW,CAAC"} \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/ILinkData.d.ts b/src/DFs/cdn-package/Discord/Types/ILinkData.d.ts new file mode 100644 index 0000000..7d793cb --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/ILinkData.d.ts @@ -0,0 +1,6 @@ +interface ILinkData { + channelID: bigint; + fileID: bigint; + fileName: string; +} +export default ILinkData; diff --git a/src/DFs/cdn-package/Discord/Types/ILinkData.js b/src/DFs/cdn-package/Discord/Types/ILinkData.js new file mode 100644 index 0000000..f2ba6bc --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/ILinkData.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=ILinkData.js.map \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/ILinkData.js.map b/src/DFs/cdn-package/Discord/Types/ILinkData.js.map new file mode 100644 index 0000000..2c45335 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/ILinkData.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ILinkData.js","sourceRoot":"","sources":["../../../src/Discord/Types/ILinkData.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/IParsedLink.d.ts b/src/DFs/cdn-package/Discord/Types/IParsedLink.d.ts new file mode 100644 index 0000000..c766e5e --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/IParsedLink.d.ts @@ -0,0 +1,7 @@ +import ELinkIssue from "./ELinkIssue"; +import ILinkData from "./ILinkData"; +interface IParsedLink { + error: ELinkIssue; + data?: ILinkData; +} +export default IParsedLink; diff --git a/src/DFs/cdn-package/Discord/Types/IParsedLink.js b/src/DFs/cdn-package/Discord/Types/IParsedLink.js new file mode 100644 index 0000000..b28159d --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/IParsedLink.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=IParsedLink.js.map \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/IParsedLink.js.map b/src/DFs/cdn-package/Discord/Types/IParsedLink.js.map new file mode 100644 index 0000000..68f086f --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/IParsedLink.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IParsedLink.js","sourceRoot":"","sources":["../../../src/Discord/Types/IParsedLink.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/IRefreshUrlsRes.d.ts b/src/DFs/cdn-package/Discord/Types/IRefreshUrlsRes.d.ts new file mode 100644 index 0000000..5e1153f --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/IRefreshUrlsRes.d.ts @@ -0,0 +1,7 @@ +interface IRefreshUrlsRes { + refreshed_urls: Array<{ + original: string; + refreshed: string; + }>; +} +export default IRefreshUrlsRes; diff --git a/src/DFs/cdn-package/Discord/Types/IRefreshUrlsRes.js b/src/DFs/cdn-package/Discord/Types/IRefreshUrlsRes.js new file mode 100644 index 0000000..94db19d --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/IRefreshUrlsRes.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=IRefreshUrlsRes.js.map \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Types/IRefreshUrlsRes.js.map b/src/DFs/cdn-package/Discord/Types/IRefreshUrlsRes.js.map new file mode 100644 index 0000000..ade92a9 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Types/IRefreshUrlsRes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IRefreshUrlsRes.js","sourceRoot":"","sources":["../../../src/Discord/Types/IRefreshUrlsRes.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Utils/GetUTF8Base64.d.ts b/src/DFs/cdn-package/Discord/Utils/GetUTF8Base64.d.ts new file mode 100644 index 0000000..1de66be --- /dev/null +++ b/src/DFs/cdn-package/Discord/Utils/GetUTF8Base64.d.ts @@ -0,0 +1,2 @@ +declare function GetUTF8Base64(input: string): string | null; +export default GetUTF8Base64; diff --git a/src/DFs/cdn-package/Discord/Utils/GetUTF8Base64.js b/src/DFs/cdn-package/Discord/Utils/GetUTF8Base64.js new file mode 100644 index 0000000..e9f3d1d --- /dev/null +++ b/src/DFs/cdn-package/Discord/Utils/GetUTF8Base64.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function hasNonUTF8Characters(str) { + return /[^\x00-\x7F]/.test(str); +} +function GetUTF8Base64(input) { + try { + const buf = Buffer.from(input, "base64"); + const decodedBase64 = buf.toString(); + if (hasNonUTF8Characters(decodedBase64)) + throw new Error("Contains Non UTF-8 character(s)."); + return decodedBase64; + } + catch (ex) { + return null; + } +} +exports.default = GetUTF8Base64; +//# sourceMappingURL=GetUTF8Base64.js.map \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Utils/GetUTF8Base64.js.map b/src/DFs/cdn-package/Discord/Utils/GetUTF8Base64.js.map new file mode 100644 index 0000000..dda2a0a --- /dev/null +++ b/src/DFs/cdn-package/Discord/Utils/GetUTF8Base64.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUTF8Base64.js","sourceRoot":"","sources":["../../../src/Discord/Utils/GetUTF8Base64.ts"],"names":[],"mappings":";;AAAA,SAAS,oBAAoB,CAAC,GAAW;IACvC,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC;AAQD,SAAS,aAAa,CAAC,KAAa;IAChC,IAAI;QACA,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACzC,MAAM,aAAa,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;QACrC,IAAI,oBAAoB,CAAC,aAAa,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;QAEvD,OAAO,aAAa,CAAC;KACxB;IAAC,OAAO,EAAE,EAAE;QACT,OAAO,IAAI,CAAC;KACf;AACL,CAAC;AAED,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Utils/ParseLink.d.ts b/src/DFs/cdn-package/Discord/Utils/ParseLink.d.ts new file mode 100644 index 0000000..a1400a1 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Utils/ParseLink.d.ts @@ -0,0 +1,3 @@ +import IParsedLink from "../Types/IParsedLink"; +declare function ParseLink(input: string): IParsedLink; +export default ParseLink; diff --git a/src/DFs/cdn-package/Discord/Utils/ParseLink.js b/src/DFs/cdn-package/Discord/Utils/ParseLink.js new file mode 100644 index 0000000..982fd8d --- /dev/null +++ b/src/DFs/cdn-package/Discord/Utils/ParseLink.js @@ -0,0 +1,30 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const ELinkIssue_1 = __importDefault(require("../Types/ELinkIssue")); +function ParseLink(input) { + if (input.includes("?")) + input = input.split("?")[0]; + if (input.includes("attachments/")) + input = input.split("attachments/")[1]; + let slashParts = input.split("/"); + if (slashParts.length != 3) + return { error: ELinkIssue_1.default.INVALID_SLASH_AMOUNT }; + const [channelID, fileID, fileName] = slashParts; + if (isNaN(Number(channelID))) + return { error: ELinkIssue_1.default.CHANNEL_ID_NAN }; + if (isNaN(Number(fileID))) + return { error: ELinkIssue_1.default.FILE_ID_NAN }; + return { + error: ELinkIssue_1.default.NONE, + data: { + channelID: BigInt(channelID), + fileID: BigInt(fileID), + fileName, + }, + }; +} +exports.default = ParseLink; +//# sourceMappingURL=ParseLink.js.map \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Utils/ParseLink.js.map b/src/DFs/cdn-package/Discord/Utils/ParseLink.js.map new file mode 100644 index 0000000..481fb65 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Utils/ParseLink.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ParseLink.js","sourceRoot":"","sources":["../../../src/Discord/Utils/ParseLink.ts"],"names":[],"mappings":";;;;;AAAA,qEAA6C;AAS7C,SAAS,SAAS,CAAC,KAAa;IAC9B,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAErD,IAAI,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IAE3E,IAAI,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,UAAU,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,oBAAU,CAAC,oBAAoB,EAAE,CAAC;IAE9E,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC;IAEjD,IAAI,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,oBAAU,CAAC,cAAc,EAAE,CAAC;IAE1E,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,oBAAU,CAAC,WAAW,EAAE,CAAC;IAOpE,OAAO;QACL,KAAK,EAAE,oBAAU,CAAC,IAAI;QACtB,IAAI,EAAE;YACJ,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC;YAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;YACtB,QAAQ;SACT;KACF,CAAC;AACJ,CAAC;AAED,kBAAe,SAAS,CAAC"} \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Utils/VerifyToken.d.ts b/src/DFs/cdn-package/Discord/Utils/VerifyToken.d.ts new file mode 100644 index 0000000..9a3b991 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Utils/VerifyToken.d.ts @@ -0,0 +1,2 @@ +declare function VerifyToken(token: string, performStatic?: boolean): Promise; +export default VerifyToken; diff --git a/src/DFs/cdn-package/Discord/Utils/VerifyToken.js b/src/DFs/cdn-package/Discord/Utils/VerifyToken.js new file mode 100644 index 0000000..cc1d0c3 --- /dev/null +++ b/src/DFs/cdn-package/Discord/Utils/VerifyToken.js @@ -0,0 +1,47 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const ETokenIssue_1 = __importDefault(require("../Types/ETokenIssue")); +const GetUTF8Base64_1 = __importDefault(require("./GetUTF8Base64")); +function staticVerifyToken(token) { + let tokenSplitByDots = token.split("."); + if (tokenSplitByDots.length != 3) + return ETokenIssue_1.default.INVALID_AMOUNT_OF_DOTS; + const decodedID = (0, GetUTF8Base64_1.default)(tokenSplitByDots[0]); + if (!decodedID) + return ETokenIssue_1.default.NON_BASE64_UTF8; + if (isNaN(Number(decodedID))) + return ETokenIssue_1.default.ID_NOT_A_NUMBER; + if (decodedID.length < 17) + return ETokenIssue_1.default.ID_TOO_SHORT; + return ETokenIssue_1.default.NONE; +} +function VerifyToken(token, performStatic = true) { + return __awaiter(this, void 0, void 0, function* () { + let isValidToken = true; + if (performStatic) { + const tokenIssue = staticVerifyToken(token); + isValidToken = tokenIssue == ETokenIssue_1.default.NONE; + if (!isValidToken) { + console.log(`[ERROR] VerifyToken(): ${tokenIssue}`); + } + } + else { + throw new Error("Non Static check is not yet implemented."); + } + return isValidToken; + }); +} +exports.default = VerifyToken; +//# sourceMappingURL=VerifyToken.js.map \ No newline at end of file diff --git a/src/DFs/cdn-package/Discord/Utils/VerifyToken.js.map b/src/DFs/cdn-package/Discord/Utils/VerifyToken.js.map new file mode 100644 index 0000000..992e7ef --- /dev/null +++ b/src/DFs/cdn-package/Discord/Utils/VerifyToken.js.map @@ -0,0 +1 @@ +{"version":3,"file":"VerifyToken.js","sourceRoot":"","sources":["../../../src/Discord/Utils/VerifyToken.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,uEAA+C;AAC/C,oEAA4C;AAE5C,SAAS,iBAAiB,CAAC,KAAa;IAEpC,IAAI,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,gBAAgB,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,qBAAW,CAAC,sBAAsB,CAAC;IAG5E,MAAM,SAAS,GAAG,IAAA,uBAAa,EAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,CAAC,SAAS;QAAE,OAAO,qBAAW,CAAC,eAAe,CAAC;IAInD,IAAI,KAAK,CAAE,MAAM,CAAC,SAAS,CAAC,CAAE;QAAE,OAAO,qBAAW,CAAC,eAAe,CAAC;IAGnE,IAAI,SAAS,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,qBAAW,CAAC,YAAY,CAAC;IAE3D,OAAO,qBAAW,CAAC,IAAI,CAAC;AAC5B,CAAC;AAWD,SAAe,WAAW,CAAC,KAAa,EAAE,gBAAyB,IAAI;;QACnE,IAAI,YAAY,GAAY,IAAI,CAAC;QAEjC,IAAI,aAAa,EAAE;YACf,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC5C,YAAY,GAAG,UAAU,IAAI,qBAAW,CAAC,IAAI,CAAC;YAC9C,IAAI,CAAC,YAAY,EAAE;gBACf,OAAO,CAAC,GAAG,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAA;aACtD;SACJ;aAAM;YACH,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;SAC/D;QAED,OAAO,YAAY,CAAC;IACxB,CAAC;CAAA;AAED,kBAAe,WAAW,CAAC"} \ No newline at end of file diff --git a/src/DFs/index.js b/src/DFs/index.js index a80083c..15876ee 100644 --- a/src/DFs/index.js +++ b/src/DFs/index.js @@ -1,9 +1,11 @@ /* eslint-disable no-restricted-syntax,no-await-in-loop */ +require('dotenv').config({ path: './config/.env' }) const https = require('https') const crypto = require('crypto') const { REST } = require('@discordjs/rest') const _ = require('lodash') const uuid = require('uuid').v4 +const fs = require('fs'); const AsyncStreamProcessorWithConcurrency = require('./lib/AsyncStreamProcessorWithConcurrency') const AsyncStreamProcessor = require('./lib/AsyncStreamProcessor') const StreamChunker = require('./lib/StreamChunker') @@ -13,6 +15,25 @@ const DEFAULT_ENCRYPTION = 'aes-256-ctr' const DEFAULT_REST_OPTS = { version: 10, timeout: 60000 } const DEFAULT_MAX_UPLOAD_CONCURRENCY = 3 +const DiscordCDN = require("./cdn-package/Discord/Discord").default + + + +let config = { TOKEN: process.env.USER_TOKEN } +let discord = new DiscordCDN(config); + +async function refreshLink(linkToRefresh) { + try { + let link = await discord.fetchLatestLink(linkToRefresh) + return link; + } catch (ex) { + console.error(ex.message); + return false + } +} + + + class DiscordFileSystem { constructor(opts) { this.webhooks = opts.webhooks @@ -108,8 +129,11 @@ class DiscordFileSystem { for (const part of parts) { let headers = {} if (part.start || part.end) headers = { Range: `bytes=${part.start || 0}-${part.end || ''}` } - await new Promise((resolve, reject) => { - https.get(part.url, { headers }, (res) => { + await new Promise(async (resolve, reject) => { + + let attachment_fixURL = await refreshLink(part.url) + if (!attachment_fixURL) return reject("Can't generate new url for discord-files attachments"); + https.get(attachment_fixURL, { headers }, (res) => { // Handle incoming data chunks from discord server const handleData = async (data) => { // https://nodejs.org/docs/latest-v16.x/api/stream.html#writablewritechunk-encoding-callback @@ -149,7 +173,7 @@ class DiscordFileSystem { // Encrypt the data if secret is provided let iv let encrypted - if (this.secret)({ iv, encrypted } = this._encrypt(this.secret, data)) + if (this.secret) ({ iv, encrypted } = this._encrypt(this.secret, data)) // Upload file to discord const part = { name: uuid(), data: encrypted || data } const { attachments: [attachment] } = await this._uploadFile(part) diff --git a/src/http/html/index.html b/src/http/html/index.html index 328a1a7..36c3344 100644 --- a/src/http/html/index.html +++ b/src/http/html/index.html @@ -66,6 +66,7 @@ DDrive File Manager +
Total storage:
@@ -246,7 +247,14 @@ // // Create dynamic TR for files // + let totalSize = 0 + function prepareFileTR(data) { + totalSize = totalSize + data.size + // Calculate the total size + const totalSizeElement = document.getElementById('totalsize'); + totalSizeElement.innerText = humanReadableSize(totalSize); + const tr = document.createElement('tr') tr.innerHTML = ` diff --git a/src/http/html/index.js b/src/http/html/index.js index 19991a3..d1a7aad 100644 --- a/src/http/html/index.js +++ b/src/http/html/index.js @@ -33,12 +33,15 @@ function makeid(length) { } function sort(arr, key) { - return arr.sort((a, b) => { - if (a[key] < b[key]) return -1 - if (a[key] > b[key]) return 1 - - return 0 - }) + if (key === "createdAt") { + return arr.sort((b, a) => new Date(a[key]) - new Date(b[key])); + } else { + return arr.sort((a, b) => { + if (a[key] < b[key]) return -1 + if (a[key] > b[key]) return 1 + return 0 + }) + } } function download(url) { const a = document.createElement('a') @@ -136,7 +139,7 @@ async function refreshTable() { for (const directory of sort(body.child.directories, 'name')) { tbody.appendChild(prepareFolderTR(directory)) } - for (const file of sort(body.child.files, 'name')) { + for (const file of sort(body.child.files, 'name')) { //replace "name" by "createdAt" to filter by date tbody.appendChild(prepareFileTR(file)) } }