Merge pull request #35 from subnub/mydrive-3

Mydrive 3
This commit is contained in:
subnub
2020-12-29 17:45:09 -05:00
committed by GitHub
2061 changed files with 156982 additions and 3634 deletions
Vendored
BIN
View File
Binary file not shown.
Executable → Regular
+1
View File
@@ -1,3 +1,4 @@
{
"presets": ["@babel/env", "@babel/react"],
"plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-object-rest-spread"]
+180 -130
View File
@@ -3,16 +3,28 @@ import FileService from "../services/FileService";
import MongoService from "../services/ChunkService/MongoService";
import FileSystemService from "../services/ChunkService/FileSystemService";
import S3Service from "../services/ChunkService/S3Service";
import {UserInterface} from "../models/user";
import uuid from "uuid";
import tempStorage from "../tempStorage/tempStorage";
import User, {UserInterface} from "../models/user";
import sendShareEmail from "../utils/sendShareEmail";
import { createStreamVideoCookie, removeStreamVideoCookie } from "../cookies/createCookies";
const fileService = new FileService()
interface RequestType extends Request {
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
s3Enabled: boolean,
}
interface RequestTypeFullUser extends Request {
user?: UserInterface,
auth?: boolean,
busboy: any,
encryptedToken?: string,
accessTokenStreamVideo?: string
}
interface RequestType extends Request {
user?: userAccessType,
encryptedToken?: string
}
type ChunkServiceType = MongoService | FileSystemService | S3Service;
@@ -26,8 +38,7 @@ class FileController {
this.chunkService = chunkService;
}
getThumbnail = async(req: RequestType, res: Response) => {
getThumbnail = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
@@ -47,15 +58,14 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
console.log("\nGet Thumbnail Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getFullThumbnail = async(req: RequestType, res: Response) => {
getFullThumbnail = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
@@ -69,13 +79,14 @@ class FileController {
await this.chunkService.getFullThumbnail(user, fileID, res);
} catch (e) {
const code = e.code || 500;
console.log(e.message, e.exception)
return res.status(code).send();
console.log("\nGet Thumbnail Full Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
uploadFile = async(req: RequestType, res: Response) => {
uploadFile = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
@@ -93,12 +104,12 @@ class FileController {
res.send(file);
console.log("file uploaded");
} catch (e) {
const code = e.code || 500;
console.log(e.message, e.exception)
return res.status(code).send();
console.log("\nUploading File Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.writeHead(code, {'Connection': 'close'})
res.end();
}
}
@@ -113,10 +124,8 @@ class FileController {
} catch (e) {
const code = e.code || 500;
const message = e.message || e;
console.log(message, e);
console.log("\nGet Public Download Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -138,9 +147,8 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
console.log("\nRemove Public Link Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
@@ -155,17 +163,16 @@ class FileController {
try {
const fileID = req.params.id;
const user = req.user;
const userID = req.user._id;
const token = await fileService.makePublic(user, fileID);
const token = await fileService.makePublic(userID, fileID);
res.send(token);
} catch (e) {
const code = e.code || 500;
console.log(e);
console.log("\nMake Public Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -183,10 +190,9 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nGet Public Info Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -207,10 +213,9 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nMake One Time Public Link Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -232,10 +237,9 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nGet File Info Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -247,18 +251,17 @@ class FileController {
try {
const userID = req.user._id;
const user = req.user;
const quickList = await fileService.getQuickList(userID);
const quickList = await fileService.getQuickList(user);
res.send(quickList);
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nGet Quick List Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -270,23 +273,22 @@ class FileController {
try {
const user = req.user;
const query = req.query;
const userID = req.user._id;
const fileList = await fileService.getList(userID, query);
const fileList = await fileService.getList(user, query);
res.send(fileList);
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nGet File List Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getDownloadToken = async(req: RequestType, res: Response) => {
getDownloadToken = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return
@@ -302,38 +304,88 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nGet Download Token Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getDownloadTokenVideo = async(req: RequestType, res: Response) => {
getAccessTokenStreamVideo = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) return;
if (!req.user) {
return
}
try {
const user = req.user;
const cookie = req.headers.uuid as string;
const tempToken = await fileService.getDownloadTokenVideo(user, cookie);
res.send({tempToken});
const currentUUID = req.headers.uuid as string;
const streamVideoAccessToken = await user.generateAuthTokenStreamVideo(currentUUID);
createStreamVideoCookie(res, streamVideoAccessToken);
res.send();
} catch (e) {
const code = e.code || 500;
console.log("\nGet Access Token Stream Video Fle Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
console.log(e);
res.status(code).send()
}
removeStreamVideoAccessToken = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) return;
try {
const userID = req.user._id;
const accessTokenStreamVideo = req.accessTokenStreamVideo!;
await User.updateOne({_id: userID}, {$pull: {tempTokens: {token: accessTokenStreamVideo}}});
removeStreamVideoCookie(res);
res.send();
} catch (e) {
console.log("\Remove Video Token File Router Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
removeTempToken = async(req: RequestType, res: Response) => {
// No longer needed, left for reference
// getDownloadTokenVideo = async(req: RequestTypeFullUser, res: Response) => {
// if (!req.user) {
// return
// }
// try {
// const user = req.user;
// const cookie = req.headers.uuid as string;
// const tempToken = await fileService.getDownloadTokenVideo(user, cookie);
// res.send({tempToken});
// } catch (e) {
// const code = e.code || 500;
// console.log(e);
// res.status(code).send()
// }
// }
removeTempToken = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return
@@ -351,16 +403,15 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nRemove Temp Token Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
streamVideo = async(req: RequestType, res: Response) => {
streamVideo = async(req: RequestTypeFullUser, res: Response) => {
if (!req.auth || !req.user) {
if (!req.user) {
return;
}
@@ -370,43 +421,25 @@ class FileController {
const fileID = req.params.id;
const headers = req.headers;
//tempStorage[req.params.uuid] = uuid.v4();
console.log("stream request 2", tempStorage);
// req.on("close", () => {
// console.log("req closed stream");
// })
// req.on("abort", () => {
// console.log("Aborted");
// })
await this.chunkService.streamVideo(user, fileID, headers, res, req);
//console.log("stream finished");
} catch (e) {
const code = e.code || 500;
const message = e.message || e;
console.log(message, e);
console.log("\nStream Video Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
downloadFile = async(req: RequestType, res: Response) => {
downloadFile = async(req: RequestTypeFullUser, res: Response) => {
if (!req.auth || !req.user) {
if (!req.user) {
return;
}
try {
console.log("download request")
const user = req.user;
const fileID = req.params.id;
@@ -414,10 +447,8 @@ class FileController {
} catch (e) {
const code = e.code || 500;
const message = e.message || e;
console.log(message, e);
console.log("\nDownload File Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -440,10 +471,9 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nGet Suggested List Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -465,17 +495,41 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nRename File Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
moveFile = async(req: RequestType, res: Response) => {
sendEmailShare = async(req: RequestType, res: Response) => {
console.log("move request");
if (!req.user) {
return;
}
try {
const user = req.user!;
const fileID = req.body.file._id;
const respient = req.body.file.resp;
const file = await fileService.getFileInfo(user._id, fileID);
await sendShareEmail(file, respient)
res.send()
} catch (e) {
console.log("\nSend Share Email Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
moveFile = async(req: RequestType, res: Response) => {
if (!req.user) {
return;
@@ -486,8 +540,6 @@ class FileController {
const fileID = req.body.id;
const userID = req.user._id;
const parentID = req.body.parent;
console.log(fileID, userID, parentID);
await fileService.moveFile(userID, fileID, parentID);
@@ -495,10 +547,9 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nMove File Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -520,10 +571,9 @@ class FileController {
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nDelete File Error File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
}
+85 -37
View File
@@ -1,14 +1,21 @@
import FolderService from "../services/FolderService";
import { Request, Response } from "express";
import {UserInterface} from "../models/user";
import MongoService from "../services/ChunkService/MongoService";
import FileSystemService from "../services/ChunkService/FileSystemService";
import S3Service from "../services/ChunkService/S3Service";
const folderService = new FolderService();
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
s3Enabled: boolean,
}
interface RequestType extends Request {
user?: UserInterface,
user?: userAccessType,
encryptedToken?: string
}
type ChunkServiceType = MongoService | FileSystemService | S3Service;
@@ -39,10 +46,9 @@ class FolderController {
} catch (e) {
const code = e.code || 500
console.log(e);
res.status(code).send(e);
console.log("\nUpload Folder Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -65,10 +71,60 @@ class FolderController {
} catch (e) {
const code = e.code || 500
console.log("\nDelete Folder Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
console.log(e);
res.status(code).send(e);
getSubfolderFullList = async(req: RequestType, res: Response) => {
if (!req.user) {
return
}
try {
const user = req.user;
const id: any = req.query.id;
const subfolderList = await folderService.getSubfolderFullList(user, id);
res.send(subfolderList);
} catch (e) {
console.log("\nGet Subfolder List Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
deletePersonalFolder = async(req: RequestType, res: Response) => {
if (!req.user) {
return
}
try {
const userID = req.user._id;
const folderID = req.body.id;
const parentList = req.body.parentList
const s3Service = new S3Service();
await s3Service.deleteFolder(userID, folderID, parentList);
res.send();
} catch (e) {
console.log("\nDelete Personal Folder Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -89,10 +145,9 @@ class FolderController {
} catch (e) {
const code = e.code || 500
console.log(e);
res.status(code).send(e);
console.log("\nDelete All Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -113,10 +168,9 @@ class FolderController {
} catch (e) {
const code = e.code || 500
console.log(e);
res.status(code).send(e);
console.log("\nGet Info Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -138,10 +192,9 @@ class FolderController {
} catch (e) {
const code = e.code || 500
console.log(e);
res.status(code).send(e);
console.log("\nGet Subfolder Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -154,19 +207,18 @@ class FolderController {
try {
const userID = req.user._id;
const user = req.user;
const query = req.query;
const folderList = await folderService.getFolderList(userID, query);
const folderList = await folderService.getFolderList(user, query);
res.send(folderList);
} catch (e) {
const code = e.code || 500
console.log(e);
res.status(code).send(e);
console.log("\nGet Folder List Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -188,11 +240,9 @@ class FolderController {
} catch (e) {
const code = e.code || 500
console.log(e);
res.status(code).send(e);
console.log("\nMove Folder Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -214,11 +264,9 @@ class FolderController {
} catch (e) {
const code = e.code || 500
console.log(e);
res.status(code).send(e);
console.log("\nRename Folder Error Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
}
+384
View File
@@ -0,0 +1,384 @@
import { Request, Response } from "express";
import {UserInterface} from "../models/user";
import GoogleFileService from "../services/GoogleFileService";
import { googleQueryType } from "../utils/createQueryGoogle";
const googleFileService = new GoogleFileService();
interface RequestTypeFullUser extends Request {
user?: UserInterface,
encryptedToken?: string
}
class GoogleFileController {
constructor() {
}
getList = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const query = req.query as unknown as googleQueryType;
const googleFiles = await googleFileService.getList(user, query);
res.send(googleFiles)
} catch (e) {
console.log("\nGet Google List Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getMongoGoogleList = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const query = req.query as unknown as googleQueryType;
const mongoGoogleFiles = await googleFileService.getMongoGoogleList(user, query);
res.send(mongoGoogleFiles)
} catch (e) {
console.log("\nGet Google/Mongo List Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getFileInfo = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const id: any = req.params.id;
const user = req.user;
const fileInfo = await googleFileService.getFileInfo(user, id);
res.send(fileInfo);
} catch (e) {
console.log("\nGet Info Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getGoogleMongoQuickList = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const googleMongoQuickList = await googleFileService.getGoogleMongoQuickList(user);
res.send(googleMongoQuickList);
} catch (e) {
console.log("\nGet Google/Mongo Quicklist Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getGoogleMongoSuggestedList = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const searchQuery: any = req.query.search || "";
const fileAndFolderList = await googleFileService.getGoogleMongoSuggestedList(user, searchQuery);
res.send(fileAndFolderList);
} catch (e) {
console.log("\nGet Google/Mongo Suggested List Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
renameFile = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const fileID = req.body.id;
const title = req.body.title
await googleFileService.renameFile(user, fileID, title);
res.send();
} catch (e) {
console.log("\nRename File Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
removeFile = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const fileID = req.body.id;
const user = req.user!;
await googleFileService.removeFile(user, fileID);
res.send();
} catch (e) {
console.log("\nRemove File Error Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
downloadFile = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user!;
const fileID = req.params.id;
await googleFileService.downloadFile(user, fileID, res);
} catch (e) {
console.log("\nDownload File Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
downloadDoc = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const fileID = req.params.id;
await googleFileService.downloadDoc(user, fileID, res);
} catch (e) {
console.log("\nDownload Doc Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getThumbnail = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const fileID = req.params.id;
await googleFileService.getThumbnail(user, fileID, res);
} catch(e) {
console.log("\nGet Thumbnail Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getFulllThumbnail = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const fileID = req.params.id;
await googleFileService.getFullThumbnail(user, fileID, res);
} catch (e) {
console.log("\nGet Full Thumbnail Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
streamVideo = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user!;
const fileID = req.params.id;
const tempUUID = req.params.uuid;
await googleFileService.streamVideo(user, fileID, tempUUID, req, res);
} catch (e) {
console.log("\nStream Video Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
uploadFile = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const busboy = req.busboy;
req.pipe(busboy);
await googleFileService.uploadFile(user, busboy, req, res);
} catch (e) {
console.log("\nUpload File Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
moveFile = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user!;
const fileID = req.body.id;
const parentID = req.body.parent;
await googleFileService.moveFile(user, fileID, parentID);
res.send();
} catch (e) {
console.log("\nMove File Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
makeFilePublic = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const fileID = req.params.id;
const publicURL = await googleFileService.makeFilePublic(user, fileID);
res.send(publicURL);
} catch (e) {
console.log("\nMake Public Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
removePublicLink = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user!;
const fileID = req.params.id;
await googleFileService.removePublicLink(user, fileID);
res.send();
} catch (e) {
console.log("\nRemove Public Error Google File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
}
export default GoogleFileController;
+230
View File
@@ -0,0 +1,230 @@
import { Request, Response } from "express";
import {UserInterface} from "../models/user";
import GoogleFolderService from "../services/GoogleFolderService";
import { googleQueryType } from "../utils/createQueryGoogle";
const googleFolderService = new GoogleFolderService();
interface RequestTypeFullUser extends Request {
user?: UserInterface,
encryptedToken?: string
}
class GoogleFolderController {
constructor() {
}
getList = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const query = req.query as unknown as googleQueryType;
const folderList = await googleFolderService.getList(user, query);
res.send(folderList);
} catch (e) {
console.log("\nGet Google List Error Google Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getGoogleMongoList = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const query = req.query;
const folderList = await googleFolderService.getGoogleMongoList(user, query);
res.send(folderList);
} catch (e) {
console.log("\nGet Google/Mongo List Error Google Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getInfo = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const id = req.params.id;
const folderInfo = await googleFolderService.getInfo(user, id);
res.send(folderInfo);
} catch (e) {
console.log("\nGet Info Error Google Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getSubFolderList = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const id: any = req.query.id;
const nameAndIDList = await googleFolderService.getSubFolderList(user, id);
res.send(nameAndIDList);
} catch (e) {
console.log("\nGet Subfolder List Error Google Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getSubfolderFullList = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const id: any = req.query.id;
const fullSubfolderList = await googleFolderService.getSubFolderFullList(user, id);
res.send(fullSubfolderList);
} catch (e) {
console.log("\nGet Full Subfolder List Error Google Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
renameFolder = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const folderID = req.body.id;
const title = req.body.title
await googleFolderService.renameFolder(user, folderID, title);
res.send();
} catch (e) {
console.log("\nRename Folder Error Google Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
removeFolder = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const folderID = req.body.id;
await googleFolderService.removeFolder(user, folderID)
res.send();
} catch (e) {
console.log("\nRemove Folder Error Google Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
upload = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
let {name, parent} = req.body;
const createdFolder = await googleFolderService.upload(user, name, parent);
res.send(createdFolder);
} catch (e) {
console.log("\nUpload Folder Error Google Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
moveFolder = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const folderID = req.body.id;
const parentID = req.body.parent;
await googleFolderService.moveFolder(user, folderID, parentID);
res.send();
} catch (e) {
console.log("\nMove Folder Error Google Folder Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
}
export default GoogleFolderController;
+202
View File
@@ -0,0 +1,202 @@
import S3Service from "../services/ChunkService/S3Service";
import { Request, Response } from "express";
import {UserInterface} from "../models/user";
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
s3Enabled: boolean,
}
interface RequestTypeFullUser extends Request {
user?: UserInterface,
encryptedToken?: string
}
interface RequestType extends Request {
user?: userAccessType,
encryptedToken?: string
}
class PersonalFileController {
constructor() {
}
getPersonalThumbnail = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const s3Service = new S3Service();
const user = req.user;
const id = req.params.id;
const decryptedThumbnail = await s3Service.getThumbnail(user, id);
res.send(decryptedThumbnail);
} catch (e) {
console.log("\nGet Thumbnail Error Personal File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getFullPersonalThumbnail = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const s3Service = new S3Service();
const user = req.user;
const fileID = req.params.id;
await s3Service.getFullThumbnail(user, fileID, res);
} catch (e) {
console.log("\nGet Full Thumbnail Error Personal File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
uploadPersonalFile = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return
}
try {
const s3Service = new S3Service();
const user = req.user;
const busboy = req.busboy;
req.pipe(busboy);
const file = await s3Service.uploadFile(user, busboy, req);
res.send(file);
} catch (e) {
console.log("\nUpload File Error Personal File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getPublicPersonalDownload = async(req: RequestType, res: Response) => {
try {
const ID = req.params.id;
const tempToken = req.params.tempToken;
const s3Service = new S3Service();
await s3Service.getPublicDownload(ID, tempToken, res);
} catch (e) {
console.log("\nGet Public Download Error Personal File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
streamPersonalVideo = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const fileID = req.params.id;
const headers = req.headers;
const s3Service = new S3Service();
await s3Service.streamVideo(user, fileID, headers, res, req);
} catch (e) {
console.log("\nStream Video Error Personal File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
downloadPersonalFile = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const s3Service = new S3Service();
const user = req.user;
const fileID = req.params.id;
await s3Service.downloadFile(user, fileID, res);
} catch (e) {
console.log("\nDownload File Error Personal File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
deletePersonalFile = async(req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const s3Service = new S3Service();
const userID = req.user._id;
const fileID = req.body.id;
await s3Service.deleteFile(userID, fileID);
res.send()
} catch (e) {
console.log("\nDelete File Error Personal File Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
}
export default PersonalFileController;
+12 -3
View File
@@ -3,8 +3,16 @@ import env from "../enviroment/env";
import { Request, Response } from "express";
import { UserInterface } from "../models/user";
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
s3Enabled: boolean,
}
interface RequestType extends Request {
user?: UserInterface,
user?: userAccessType,
encryptedToken?: string
}
class StorageController {
@@ -32,8 +40,9 @@ class StorageController {
} catch (e) {
console.log(e);
res.status(500).send(e);
console.log("\nGet Storage Error Storage Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
}
+244 -44
View File
@@ -3,13 +3,31 @@ import env from "../enviroment/env";
import UserService from "../services/UserService";
import { Request, Response } from "express";
import { UserInterface } from "../models/user";
import { createLoginCookie, createLogoutCookie } from "../cookies/createCookies";
import NotFoundError from "../utils/NotFoundError";
import InternalServerError from "../utils/InternalServerError";
const UserProvider = new UserService();
interface RequestType extends Request {
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
admin: boolean,
botChecked: boolean,
username: string,
}
interface RequestTypeRefresh extends Request {
user?: UserInterface,
encryptedToken?: string
}
interface RequestType extends Request {
user?: userAccessType,
encryptedToken?: string
}
class UserController {
constructor() {
@@ -21,17 +39,14 @@ class UserController {
try {
const user = req.user!;
user.tokens = [];
user.tempTokens = [];
user.password = '';
user.privateKey = undefined;
user.publicKey = undefined;
res.send(user)
} catch (e) {
res.status(500).send(e);
console.log("\nGet User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -40,23 +55,46 @@ class UserController {
try {
const body = req.body;
const currentUUID = req.headers.uuid as string;
const {user, token} = await UserProvider.login(body);
const {user, accessToken, refreshToken} = await UserProvider.login(body, currentUUID);
user.tokens = [];
user.tempTokens = [];
user.password = '';
user.privateKey = undefined;
user.publicKey = undefined;
res.status(200).send({user, token});
createLoginCookie(res, accessToken, refreshToken);
res.status(200).send({user});
} catch (e) {
const code = e.code || 500;
console.log("\nLogin User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
console.log(e);
res.status(code).send(e);
getToken = async(req: RequestTypeRefresh, res: Response) => {
try {
const user = req.user;
if (!user) throw new NotFoundError("User Not Found");
const currentUUID = req.headers.uuid as string;
const {accessToken, refreshToken} = await user.generateAuthToken(currentUUID);
if (!accessToken || !refreshToken) throw new InternalServerError("User/Access/Refresh Token Missing");
createLoginCookie(res, accessToken, refreshToken);
res.status(201).send();
} catch (e) {
console.log("\nGet Refresh Token User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -69,32 +107,43 @@ class UserController {
try {
const user = req.user;
const token = req.encryptedToken;
const userID = req.user._id;
const refreshToken = req.cookies["refresh-token"];
await UserProvider.logout(user, token);
await UserProvider.logout(userID, refreshToken);
createLogoutCookie(res);
res.send();
} catch (e) {
res.status(500).send(e);
console.log("\nLogout User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
createLogoutCookie(res);
res.status(code).send();
}
}
logoutAll = async(req: RequestType, res: Response) => {
if (!req.user) return;
try {
const user = req.user;
const userID = req.user._id;
await UserProvider.logoutAll(user!);
await UserProvider.logoutAll(userID);
createLogoutCookie(res);
res.send();
} catch (e) {
res.status(500).send(e);
console.log("\nLogout All User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -107,22 +156,19 @@ class UserController {
try {
const {user, token} = await UserProvider.create(req.body);
const currentUUID = req.headers.uuid as string;
user.tokens = [];
user.tempTokens = []
user.password = '';
user.privateKey = undefined;
user.publicKey = undefined;
const {user, accessToken, refreshToken} = await UserProvider.create(req.body, currentUUID);
createLoginCookie(res, accessToken, refreshToken);
res.status(201).send({user, token})
res.status(201).send({user})
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send(e);
console.log("\nCreate User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
@@ -134,21 +180,175 @@ class UserController {
}
try {
const user = req.user;
const userID = req.user._id;
const oldPassword = req.body.oldPassword;
const newPassword = req.body.newPassword;
const newToken = await UserProvider.changePassword(user, oldPassword, newPassword);
const oldRefreshToken = req.cookies["refresh-token"];
res.send(newToken);
const currentUUID = req.headers.uuid as string;
const {accessToken, refreshToken} = await UserProvider.changePassword(userID, oldPassword, newPassword, oldRefreshToken, currentUUID);
createLoginCookie(res, accessToken, refreshToken);
res.send();
} catch (e) {
const code = e.code || 500;
console.log("\nChange Password User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
console.log(e);
res.status(code).send(e);
refreshStorageSize = async(req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const userID = req.user._id;
await UserProvider.refreshStorageSize(userID);
res.send();
} catch (e) {
console.log("\nRefresh Storage Size User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
getUserDetailed = async(req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const userID = req.user._id;
const userDetailed = await UserProvider.getUserDetailed(userID);
res.send(userDetailed);
} catch (e) {
console.log("\nGet User Detailed User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
verifyEmail = async(req: RequestType, res: Response) => {
try {
const verifyToken = req.body.emailToken;
const currentUUID = req.headers.uuid as string;
const user = await UserProvider.verifyEmail(verifyToken);
const {accessToken, refreshToken} = await user.generateAuthToken(currentUUID);
createLoginCookie(res, accessToken, refreshToken);
res.send();
} catch (e) {
console.log("\nVerify Email User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
resendVerifyEmail = async(req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const userID = req.user._id;
await UserProvider.resendVerifyEmail(userID);
res.send();
} catch (e) {
console.log("\nResend Email User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
sendPasswordReset = async(req: RequestType, res: Response) => {
try {
const email = req.body.email;
await UserProvider.sendPasswordReset(email);
res.send();
} catch (e) {
console.log("\nSend Password Reset Email User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
resetPassword = async(req: RequestType, res: Response) => {
try {
const verifyToken = req.body.passwordToken;
const newPassword = req.body.password;
await UserProvider.resetPassword(newPassword, verifyToken);
res.send();
} catch (e) {
console.log("\nReset Password User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
addName = async(req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const userID = req.user._id;
const name = req.body.name;
await UserProvider.addName(userID, name);
res.send();
} catch (e) {
console.log("\nAdd Name User Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
}
+96
View File
@@ -0,0 +1,96 @@
import { Request, Response } from "express";
import { UserInterface } from "../models/user";
import UserServiceGoogle from "../services/UserGoogle";
import { createLoginCookie } from "../cookies/createCookies";
const UserProviderGoogle = new UserServiceGoogle();
interface RequestTypeFullUser extends Request {
user?: UserInterface,
encryptedToken?: string
}
class UserGoogleController {
constructor() {
}
createGoogleStorageURL = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const googleData = req.body;
const url = await UserProviderGoogle.createGoogleStorageURL(user, googleData);
res.send(url);
} catch (e) {
console.log("\nCreate Storage URL Error Google User Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
addGoogleStorage = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const code = req.body.code;
const currentUUID = req.headers.uuid as string;
const {accessToken, refreshToken} = await UserProviderGoogle.addGoogleStorage(user, code, currentUUID);
createLoginCookie(res, accessToken, refreshToken);
res.send();
} catch (e) {
console.log("\nAdd Google Storage Error Google User Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
removeGoogleStorage = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user
const currentUUID = req.headers.uuid as string;
const {accessToken, refreshToken} = await UserProviderGoogle.removeGoogleStorage(user, currentUUID);
createLoginCookie(res, accessToken, refreshToken);
res.send();
} catch (e) {
console.log("\nRemove Google Storage Error Google User Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
}
export default UserGoogleController;
+156
View File
@@ -0,0 +1,156 @@
import { Request, Response } from "express";
import { UserInterface } from "../models/user";
import UserServicePersonal from "../services/UserPersonalService";
import { createLoginCookie } from "../cookies/createCookies";
const UserProviderPersonal = new UserServicePersonal()
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
s3Enabled: boolean,
}
interface RequestTypeFullUser extends Request {
user?: UserInterface,
encryptedToken?: string
}
interface RequestType extends Request {
user?: userAccessType,
encryptedToken?: string
}
class UserPersonalController {
constructor() {
}
addS3Storage = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const s3Data = req.body;
const currentUUID = req.headers.uuid as string;
const {accessToken, refreshToken} = await UserProviderPersonal.addS3Storage(user, s3Data, currentUUID);
createLoginCookie(res, accessToken, refreshToken);
res.send();
} catch (e) {
console.log("\nAdd S3 Storage Error Personal User Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
removeS3Storage = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const currentUUID = req.headers.uuid as string;
const {accessToken, refreshToken} = await UserProviderPersonal.removeS3Storage(user, currentUUID);
createLoginCookie(res, accessToken, refreshToken);
res.send();
} catch (e) {
console.log("\nRemove S3 Storage Error Personal User Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
removeS3Metadata = async(req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
await UserProviderPersonal.removeS3Metadata(user);
res.send();
} catch (e) {
console.log("\nRemove S3 Metadata Error Personal User Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
downloadPersonalFileList = async(req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
res.set('Content-Type', 'application/json');
res.setHeader('Content-disposition', 'attachment; filename= personal-data-list.json');
const personalFileList = await UserProviderPersonal.downloadPersonalFileList(user);
res.send(personalFileList);
} catch (e) {
console.log("\nDownload S3 Metadata Error Personal User Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
uploadPersonalFileList = async(req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const user = req.user;
const fileList = req.body;
await UserProviderPersonal.uploadPersonalFileList(user, fileList);
res.send();
} catch (e) {
console.log("\nUpload S3 Metadata Error Personal User Route:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
}
export default UserPersonalController;
+64
View File
@@ -0,0 +1,64 @@
import {Response } from "express";
import env from "../enviroment/env";
const maxAgeAccess = 60 * 1000 * 20;
//const maxAgeAccess = 1000;
const maxAgeRefresh = 60 * 1000 * 60 * 24 * 30;
//const maxAgeRefresh = 1000;
const maxAgeStreamVideo = 60 * 1000 * 60 * 24;
const secureCookies = env.secureCookies ? env.secureCookies === "true" ? true : false : false;
export const createLoginCookie = (res: Response, accessToken: string, refreshToken: string) => {
res.cookie("access-token",accessToken, {
httpOnly: true,
maxAge: maxAgeAccess,
sameSite: "strict",
secure: secureCookies
})
res.cookie("refresh-token",refreshToken, {
httpOnly: true,
maxAge: maxAgeRefresh,
sameSite: "strict",
secure: secureCookies
})
}
export const createLogoutCookie = (res: Response) => {
res.cookie("access-token", {}, {
httpOnly: true,
maxAge: 0,
sameSite: "strict",
secure: secureCookies
})
res.cookie("refresh-token", {}, {
httpOnly: true,
maxAge: 0,
sameSite: "strict",
secure: secureCookies
})
}
export const createStreamVideoCookie = (res: Response, streamVideoAccessToken: string) => {
res.cookie("video-access-token", streamVideoAccessToken, {
httpOnly: true,
maxAge: maxAgeStreamVideo,
sameSite: "strict",
secure: secureCookies
})
}
export const removeStreamVideoCookie = (res: Response) => {
res.cookie("video-access-token", {}, {
httpOnly: true,
maxAge: 0,
sameSite: "strict",
secure: secureCookies
})
}
+16
View File
@@ -0,0 +1,16 @@
import AWS from "aws-sdk";
const s3Auth = (id:string, key:string) => {
AWS.config.update({
accessKeyId: id,
secretAccessKey: key
});
const s3 = new AWS.S3();
return s3;
}
export default s3Auth;
+35
View File
@@ -0,0 +1,35 @@
import { UserInterface } from "../models/user";
import {google} from "googleapis";
import env from "../enviroment/env";
const getGoogleAuth = async (user: UserInterface) => {
const googleIDandToken = await user?.decryptDriveIDandKey();
const clientID = googleIDandToken?.clientID;
const clientKey = googleIDandToken?.clientKey;
const token = await user?.decryptDriveTokenData();
const refreshToken = token.refresh_token;
const date = new Date();
const time = date.getTime();
if (time >= token.expiry_date) {
// console.log("TOKEN EXPIRED!")
}
const redirectURL = env.remoteURL + "/add-google-account";
const oauth2Client = new google.auth.OAuth2(
clientID,
clientKey,
redirectURL,
);
oauth2Client.setCredentials(token);
return oauth2Client
}
export default getGoogleAuth;
+2 -1
View File
@@ -8,4 +8,5 @@ AWS.config.update({
const s3 = new AWS.S3();
export default s3;
export default s3;
module.exports = s3;
+21 -12
View File
@@ -32,7 +32,7 @@ class DbUtil {
const file = await conn.db.collection("fs.files")
.findOneAndUpdate({"_id": new ObjectID(fileID),
"metadata.owner": userID},
"metadata.owner": new ObjectID(userID)},
{"$unset": {"metadata.linkType": "", "metadata.link": ""}}) as FileInterface;
return file;
@@ -42,7 +42,7 @@ class DbUtil {
const file = await conn.db.collection("fs.files")
.findOneAndUpdate({"_id": new ObjectID(fileID),
"metadata.owner": userID},
"metadata.owner": new ObjectID(userID)},
{"$set": {"metadata.linkType": "public", "metadata.link": token}}) as FileInterface
return file;
@@ -60,7 +60,7 @@ class DbUtil {
const file = await conn.db.collection("fs.files")
.findOneAndUpdate({"_id": new ObjectID(fileID),
"metadata.owner": userID},
"metadata.owner": new ObjectID(userID)},
{"$set": {"metadata.linkType": "one", "metadata.link": token}}) as FileInterface;
return file;
@@ -69,15 +69,21 @@ class DbUtil {
getFileInfo = async(fileID: string, userID: string) => {
const file = await conn.db.collection("fs.files")
.findOne({"metadata.owner": userID, "_id": new ObjectID(fileID)}) as FileInterface;
.findOne({"metadata.owner": new ObjectID(userID), "_id": new ObjectID(fileID)}) as FileInterface;
return file;
}
getQuickList = async(userID: string) => {
getQuickList = async(userID: string, s3Enabled: boolean) => {
let query: any = {"metadata.owner": new ObjectID(userID)}
if (!s3Enabled){
query = {...query, "metadata.personalFile": null}
}
const fileList = await conn.db.collection("fs.files")
.find({"metadata.owner": userID})
.find(query)
.sort({uploadDate: -1})
.limit(10)
.toArray() as FileInterface[];
@@ -109,8 +115,10 @@ class DbUtil {
getFileSearchList = async(userID: string, searchQuery: RegExp) => {
let query:any = {"metadata.owner": new ObjectID(userID), "filename": searchQuery};
const fileList = await conn.db.collection("fs.files")
.find({"metadata.owner": userID, "filename": searchQuery})
.find(query)
.limit(10)
.toArray() as FileInterface[];
@@ -121,7 +129,7 @@ class DbUtil {
const file = await conn.db.collection("fs.files")
.findOneAndUpdate({"_id": new ObjectID(fileID),
"metadata.owner": userID}, {"$set": {"filename": title}}) as FileInterface;
"metadata.owner": new ObjectID(userID)}, {"$set": {"filename": title}}) as FileInterface;
return file;
}
@@ -130,7 +138,7 @@ class DbUtil {
const file = await conn.db.collection("fs.files")
.findOneAndUpdate({"_id": new ObjectID(fileID),
"metadata.owner": userID}, {"$set": {"metadata.parent": parent, "metadata.parentList": parentList}})
"metadata.owner": new ObjectID(userID)}, {"$set": {"metadata.parent": parent, "metadata.parentList": parentList}})
return file;
}
@@ -138,7 +146,7 @@ class DbUtil {
getFileListByParent = async(userID: string, parentListString: string) => {
const fileList = await conn.db.collection("fs.files")
.find({"metadata.owner": userID,
.find({"metadata.owner": new ObjectID(userID),
"metadata.parentList": {$regex : `.*${parentListString}.*`}}).toArray() as FileInterface[];
return fileList;
@@ -147,7 +155,7 @@ class DbUtil {
getFileListByOwner = async(userID: string) => {
const fileList = await conn.db.collection("fs.files")
.find({"metadata.owner": userID}).toArray() as FileInterface[];
.find({"metadata.owner": new ObjectID(userID)}).toArray() as FileInterface[];
return fileList;
}
@@ -158,4 +166,5 @@ class DbUtil {
}
}
export default DbUtil;
export default DbUtil;
module.exports = DbUtil;
+54 -5
View File
@@ -9,7 +9,9 @@ class DbUtil {
getFolderSearchList = async(userID: string, searchQuery: RegExp) => {
const folderList = await Folder.find({"owner": userID, "name": searchQuery}).limit(10) as FolderInterface[];
let query:any = {"owner": userID, "name": searchQuery};
const folderList = await Folder.find(query).limit(10) as FolderInterface[];
return folderList;
}
@@ -21,17 +23,64 @@ class DbUtil {
return folder;
}
getFolderListByParent = async(userID: string, parent: string, sortBy: string) => {
getFolderListByParent = async(userID: string, parent: string, sortBy: string, s3Enabled: boolean, type: string, storageType: string, itemType: string) => {
const folderList = await Folder.find({"owner": userID, "parent": parent})
let query: any = {"owner": userID, "parent": parent};
if (!s3Enabled) {
query = {...query, "personalFolder": null}
}
if (type) {
if (type === "mongo") {
query = {...query, "personalFolder": null}
} else if (type === "s3") {
query = {...query, "personalFolder": true}
}
}
if (itemType) {
if (itemType === "personal") query = {...query, "personalFolder": true}
if (itemType === "nonpersonal") query = {...query, "personalFolder": null}
}
const folderList = await Folder.find(query)
.sort(sortBy) as FolderInterface[];
return folderList;
}
getFolderListBySearch = async(userID: string, searchQuery: string, sortBy: string) => {
getFolderListBySearch = async(userID: string, searchQuery: string, sortBy: string, type: string, parent: string, storageType: string, folderSearch: boolean, itemType: string, s3Enabled: boolean) => {
const folderList = await Folder.find({"name": searchQuery,"owner": userID})
let query: any = {"name": searchQuery,"owner": userID};
if (type) {
if (type === "mongo") {
query = {...query, "personalFolder": null}
} else {
query = {...query, "personalFolder": true}
}
}
if (storageType === "s3") {
query = {...query, "personalFolder": true}
}
if (parent && (parent !== "/" || folderSearch)) {
query = {...query, parent}
}
if (!s3Enabled) {
query = {...query, "personalFolder": null}
}
if (itemType) {
if (itemType === "personal") query = {...query, "personalFolder": true}
if (itemType === "nonpersonal") query = {...query, "personalFolder": null}
}
const folderList = await Folder.find(query)
.sort(sortBy) as FolderInterface[];
return folderList;
+163
View File
@@ -0,0 +1,163 @@
import {UserInterface} from "../../../models/user";
import { google } from "googleapis";
import getGoogleAuth from "../../../db/googleAuth";
import createQueryGoogle, {googleQueryType} from "../../../utils/createQueryGoogle";
const fields = 'id, name, size, modifiedTime, hasThumbnail, parents, mimeType, thumbnailLink, webViewLink, shared';
class GoogleDbUtil {
constructor() {
}
getList = async(query: googleQueryType, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const limit = query.limit;
let parent = query.parent === "/" ? "root" : query.parent;
const {queryBuilder, orderBy} = createQueryGoogle(query, parent)
const previosPageToken = query.pageToken;
const drive = google.drive({version:"v3", auth: oauth2Client});
const files = await drive.files.list({pageSize: limit, fields: `nextPageToken, files(${fields})`, q: queryBuilder, orderBy, pageToken: previosPageToken});
return files
}
getFileInfo = async(id: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const file = await drive.files.get({fileId: id, fields: fields});
return file;
}
getQuickList = async(user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
let query = 'mimeType != "application/vnd.google-apps.folder" and trashed=false';
const files = await drive.files.list({pageSize: 10, fields: `nextPageToken, files(${fields})`, q: query});
return files;
}
getSuggestedList = async(searchQuery: string, user: UserInterface) => {
const driveQuery = `name contains "${searchQuery}" and mimeType != "application/vnd.google-apps.folder" and trashed=false`
const driveQueryFolder = `name contains "${searchQuery}" and mimeType = "application/vnd.google-apps.folder" and trashed=false`
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const files = await drive.files.list({pageSize: 10, fields: `nextPageToken, files(${fields})`, q: driveQuery});
const folders = await drive.files.list({pageSize: 10, fields: `nextPageToken, files(${fields})`, q: driveQueryFolder});
return {
files,
folders
}
}
renameFile = async(fileID: string, title: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
await drive.files.update({fileId: fileID, requestBody: {name:title}})
}
removeFile = async(fileID: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
await drive.files.delete({fileId:fileID});
}
getDownloadFileMetadata = async(fileID: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const fileMetadata = await drive.files.get({fileId: fileID, fields: "*"});
return {fileMetadata, drive};
}
moveFile = async(fileID: string, parentID: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const previousFile = await drive.files.get({
fileId: fileID,
fields: "*"
})
const previousParent = previousFile.data.parents![0];
await drive.files.update({
fileId: fileID,
addParents: parentID,
removeParents: previousParent,
fields: fields
})
}
makeFilePublic = async(fileID: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
await drive.permissions.create({
requestBody: {
type: "anyone",
role: "reader"
},
fileId: fileID,
fields: "*"
})
const fileDetails = await drive.files.get({
fileId: fileID,
fields: fields
})
const publicURL = fileDetails.data.webViewLink!;
return publicURL;
}
removePublicLink = async(fileID: string, user: UserInterface) => {
const {fileMetadata, drive} = await this.getDownloadFileMetadata(fileID, user);
await drive.permissions.get({
fileId: fileID,
permissionId: fileMetadata.data.permissionIds![0]
})
await drive.permissions.delete({
fileId: fileID,
permissionId: fileMetadata.data.permissionIds![0]
})
}
}
export default GoogleDbUtil;
+100
View File
@@ -0,0 +1,100 @@
import {UserInterface} from "../../../models/user";
import { google } from "googleapis";
import getGoogleAuth from "../../../db/googleAuth";
import {googleQueryType} from "../../../utils/createQueryGoogle";
import createQueryGoogleFolder from "../../../utils/createQueryGoogleFolder";
const fields = 'id, name, createdTime, parents, mimeType'
class GoogleFolderUtils {
constructor() {
}
getList = async(query: googleQueryType, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const limit = query.limit;
let parent = query.parent === "/" ? "root" : query.parent;
const {orderBy, queryBuilder} = createQueryGoogleFolder(query, parent);
const folders = await drive.files.list({pageSize: limit, fields: `files(${fields})`, q: queryBuilder, orderBy});
return folders;
}
getInfo = async(id: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const folder = await drive.files.get({fileId: id, fields: fields});
return folder;
}
renameFolder = async(folderID: string, title: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
await drive.files.update({fileId: folderID, requestBody: {name:title}});
}
removeFolder = async(folderID: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
await drive.files.delete({fileId:folderID});
}
uploadFolder = async(name: string, parent: string, user: UserInterface) => {
parent = parent === "/" ? "root" : parent;
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const folderMetadata = {
name,
mimeType: 'application/vnd.google-apps.folder',
parents: [parent]
}
const createdFolder = await drive.files.create({
requestBody: folderMetadata,
fields: fields
})
return createdFolder;
}
moveFolder = async(fileID: string, parentID: string, user: UserInterface) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const previousFile = await drive.files.get({
fileId: fileID,
fields: fields
})
const previousParent = previousFile.data.parents![0];
await drive.files.update({
fileId: fileID,
addParents: parentID,
removeParents: previousParent,
fields: fields
})
}
}
export default GoogleFolderUtils;
+33 -2
View File
@@ -1,7 +1,9 @@
export default {
key: process.env.KEY,
newKey: process.env.NEW_KEY,
password: process.env.PASSWORD,
passwordAccess: process.env.PASSWORD_ACCESS,
passwordRefresh: process.env.PASSWORD_REFRESH,
passwordCookie: process.env.PASSWORD_COOKIE,
createAcctBlocked: process.env.BLOCK_CREATE_ACCOUNT,
root: process.env.ROOT,
url: process.env.URL,
@@ -12,5 +14,34 @@ export default {
s3Key: process.env.S3_KEY,
s3Bucket: process.env.S3_BUCKET,
useDocumentDB: process.env.USE_DOCUMENT_DB,
documentDBBundle: process.env.DOCUMENT_DB_BUNDLE
documentDBBundle: process.env.DOCUMENT_DB_BUNDLE,
sendgridKey: process.env.SENDGRID_KEY,
sendgridEmail: process.env.SENDGRID_EMAIL,
remoteURL: process.env.REMOTE_URL,
disableEmailVerification: process.env.DISABLE_EMAIL_VERIFICATION,
secureCookies: process.env.SECURE_COOKIES
}
module.exports = {
key: process.env.KEY,
newKey: process.env.NEW_KEY,
passwordAccess: process.env.PASSWORD_ACCESS,
passwordRefresh: process.env.PASSWORD_REFRESH,
passwordCookie: process.env.PASSWORD_COOKIE,
createAcctBlocked: process.env.BLOCK_CREATE_ACCOUNT,
root: process.env.ROOT,
url: process.env.URL,
mongoURL: process.env.MONGODB_URL,
dbType: process.env.DB_TYPE,
fsDirectory: process.env.FS_DIRECTORY,
s3ID: process.env.S3_ID,
s3Key: process.env.S3_KEY,
s3Bucket: process.env.S3_BUCKET,
useDocumentDB: process.env.USE_DOCUMENT_DB,
documentDBBundle: process.env.DOCUMENT_DB_BUNDLE,
sendgridKey: process.env.SENDGRID_KEY,
sendgridEmail: process.env.SENDGRID_EMAIL,
remoteURL: process.env.REMOTE_URL,
disableEmailVerification: process.env.DISABLE_EMAIL_VERIFICATION,
secureCookies: process.env.SECURE_COOKIES
}
+2 -1
View File
@@ -20,4 +20,5 @@ const getEnvVariables = () => {
}
}
export default getEnvVariables;
export default getEnvVariables;
module.exports = getEnvVariables
+27 -12
View File
@@ -1,38 +1,43 @@
import {Router} from "express";
import auth from "../middleware/auth";
import tempAuth from "../middleware/tempAuth";
import tempAuthVideo from "../middleware/tempAuthVideo";
import FileController from "../controllers/file";
import env from "../enviroment/env";
import MongoService from "../services/ChunkService/MongoService";
import FileSystemService from "../services/ChunkService/FileSystemService";
import S3Service from "../services/ChunkService/S3Service";
import ChunkInterface from "../services/ChunkService/utils/ChunkInterface";
import authFullUser from "../middleware/authFullUser";
import authStreamVideo from "../middleware/authStreamVideo";
let fileController: FileController;
let chunkService: ChunkInterface;
if (env.dbType === "mongo") {
const mongoService = new MongoService();
chunkService = mongoService;
fileController = new FileController(mongoService);
} else if (env.dbType === "fs") {
const fileSystemService = new FileSystemService();
chunkService = fileSystemService
fileController = new FileController(fileSystemService);
} else {
const s3Service = new S3Service();
chunkService = s3Service;
fileController = new FileController(s3Service);
}
const router = Router();
router.post("/file-service/upload", auth, fileController.uploadFile);
router.post("/file-service/upload", authFullUser, fileController.uploadFile);
router.get("/file-service/thumbnail/:id", auth, fileController.getThumbnail);
router.get("/file-service/thumbnail/:id", authFullUser, fileController.getThumbnail);
router.get("/file-service/full-thumbnail/:id", auth, fileController.getFullThumbnail);
router.get("/file-service/full-thumbnail/:id", authFullUser, fileController.getFullThumbnail);
router.get("/file-service/public/download/:id/:tempToken", fileController.getPublicDownload);
@@ -44,17 +49,17 @@ router.get("/file-service/quick-list", auth, fileController.getQuickList);
router.get("/file-service/list", auth, fileController.getList);
router.get("/file-service/download/get-token", auth, fileController.getDownloadToken);
router.get("/file-service/download/access-token-stream-video", authFullUser, fileController.getAccessTokenStreamVideo)
router.get("/file-service/download/get-token-video", auth, fileController.getDownloadTokenVideo);
router.get("/file-service/stream-video/:id", authStreamVideo, fileController.streamVideo);
router.get("/file-service/stream-video/:id/:tempToken/:uuid", tempAuthVideo, fileController.streamVideo);
router.delete("/file-service/remove-stream-video-token", authStreamVideo, fileController.removeStreamVideoAccessToken);
router.get("/file-service/download/:id/:tempToken", tempAuth, fileController.downloadFile);
router.get("/file-service/download/:id", authFullUser, fileController.downloadFile);
router.get("/file-service/suggested-list", auth, fileController.getSuggestedList);
router.patch("/file-service/make-public/:id", auth, fileController.makePublic);
router.patch("/file-service/make-public/:id", authFullUser, fileController.makePublic);
router.patch("/file-service/make-one/:id", auth, fileController.makeOneTimePublic);
@@ -64,8 +69,18 @@ router.patch("/file-service/move", auth, fileController.moveFile);
router.delete("/file-service/remove-link/:id", auth, fileController.removeLink);
router.delete("/file-service/remove/token-video/:tempToken/:uuid", auth, fileController.removeTempToken);
router.delete("/file-service/remove/token-video/:id", auth, fileController.removeTempToken);
router.delete("/file-service/remove", auth, fileController.deleteFile);
export default router;
router.post("/file-service/send-share-email", auth, fileController.sendEmailShare);
export default router;
// NO longer needed left for reference
//router.delete("/file-service/remove/token-video/:tempToken/:uuid", auth, fileController.removeTempToken);
//router.get("/file-service/stream-video/:id/:tempToken/:uuid", auth, fileController.streamVideo);
//router.get("/file-service/stream-video/:id", auth, fileController.streamVideo);
//router.get("/file-service/download/get-token", authFullUser, fileController.getDownloadToken);
//router.get("/file-service/download/get-token-video", auth, fileController.getDownloadTokenVideo);
+6
View File
@@ -42,4 +42,10 @@ router.patch("/folder-service/rename", auth, folderController.renameFolder);
router.patch("/folder-service/move", auth, folderController.moveFolder);
router.get("/folder-service/subfolder-list-full", auth, folderController.getSubfolderFullList);
// Personal Folder
router.delete("/folder-service-personal/remove", auth, folderController.deletePersonalFolder);
export default router;
+42
View File
@@ -0,0 +1,42 @@
import {Router} from "express";
import GoogleFileController from "../controllers/googleFile";
import authFullUser from "../middleware/authFullUser";
import authStreamVideo from "../middleware/authStreamVideo";
const googleController = new GoogleFileController();
const router = Router();
router.get("/file-service-google/list", authFullUser, googleController.getList);
router.get("/file-service-google-mongo/list", authFullUser, googleController.getMongoGoogleList);
router.get("/file-service-google/info/:id", authFullUser, googleController.getFileInfo);
router.get("/file-service-google-mongo/quick-list", authFullUser, googleController.getGoogleMongoQuickList);
router.get("/file-service-google-mongo/suggested-list", authFullUser, googleController.getGoogleMongoSuggestedList);
router.patch("/file-service-google/rename", authFullUser, googleController.renameFile);
router.delete("/file-service-google/remove", authFullUser, googleController.removeFile);
router.get("/file-service-google/download/:id", authFullUser, googleController.downloadFile);
router.get("/file-service-google-doc/download/:id", authFullUser, googleController.downloadDoc);
router.get("/file-service-google/thumbnail/:id", authFullUser, googleController.getThumbnail);
router.get("/file-service-google/full-thumbnail/:id", authFullUser, googleController.getFulllThumbnail);
router.get("/file-service-google/stream-video/:id", authStreamVideo, googleController.streamVideo);
router.post("/file-service-google/upload", authFullUser, googleController.uploadFile);
router.patch("/file-service-google/move", authFullUser, googleController.moveFile);
router.patch("/file-service-google/make-public/:id", authFullUser, googleController.makeFilePublic);
router.delete("/file-service-google/remove-link/:id", authFullUser, googleController.removePublicLink);
export default router;
+27
View File
@@ -0,0 +1,27 @@
import {Router} from "express";
import GoogleFolderController from "../controllers/googleFolder";
import authFullUser from "../middleware/authFullUser";
const googleFolderController = new GoogleFolderController();
const router = Router();
router.get("/folder-service-google/list", authFullUser, googleFolderController.getList);
router.get("/folder-service-google-mongo/list", authFullUser, googleFolderController.getGoogleMongoList);
router.get("/folder-service-google/info/:id", authFullUser, googleFolderController.getInfo);
router.get("/folder-service-google/subfolder-list", authFullUser, googleFolderController.getSubFolderList);
router.get("/folder-service-google/subfolder-list-full", authFullUser, googleFolderController.getSubfolderFullList);
router.patch("/folder-service-google/rename", authFullUser, googleFolderController.renameFolder);
router.delete("/folder-service-google/remove", authFullUser,googleFolderController.removeFolder);
router.post("/folder-service-google/upload", authFullUser, googleFolderController.upload);
router.patch("/folder-service-google/move", authFullUser, googleFolderController.moveFolder);
export default router;
+26
View File
@@ -0,0 +1,26 @@
import {Router} from "express";
import auth from "../middleware/auth";
import PersonalFileController from "../controllers/personalFile";
import authFullUser from "../middleware/authFullUser";
import authStreamVideo from "../middleware/authStreamVideo";
const personalFileController = new PersonalFileController();
const router = Router();
router.post("/file-service-personal/upload", authFullUser, personalFileController.uploadPersonalFile);
router.get("/file-service-personal/download/:id", authFullUser, personalFileController.downloadPersonalFile);
router.get("/file-service-personal/thumbnail/:id", authFullUser, personalFileController.getPersonalThumbnail);
router.get("/file-service-personal/full-thumbnail/:id", authFullUser, personalFileController.getFullPersonalThumbnail);
router.get("/file-service-personal/stream-video/:id", authStreamVideo, personalFileController.streamPersonalVideo);
router.get("/file-service-personal/public/download/:id/:tempToken", personalFileController.getPublicPersonalDownload);
router.delete("/file-service-personal/remove", auth, personalFileController.deletePersonalFile);
export default router;
+3 -1
View File
@@ -6,6 +6,8 @@ const storageController = new StorageController();
const router = Router();
router.get("/storage-service/info", auth, storageController.getStorageInfo);
// No longer needed left for reference
// router.get("/storage-service/info", auth, storageController.getStorageInfo);
export default router;
+22 -3
View File
@@ -1,21 +1,40 @@
import { Router } from "express";
import auth from "../middleware/auth";
import authNoEmailVerification from "../middleware/authNoEmailVerificication"
import UserController from "../controllers/user";
import authRefresh from "../middleware/authRefresh";
import authLogout from "../middleware/authLogout";
const userController = new UserController();
const router = Router();
router.get("/user-service/user", auth, userController.getUser);
router.get("/user-service/user", authNoEmailVerification, userController.getUser);
router.patch("/user-service/refresh-storage-size", auth, userController.refreshStorageSize);
router.get("/user-service/user-detailed", auth, userController.getUserDetailed);
router.post("/user-service/login", userController.login);
router.post("/user-service/logout", auth, userController.logout);
router.post("/user-service/logout", authLogout, userController.logout);
router.post("/user-service/logout-all", auth, userController.logoutAll);
router.post("/user-service/logout-all", authLogout, userController.logoutAll);
router.post("/user-service/create", userController.createUser);
router.post("/user-service/change-password", auth, userController.changePassword);
router.post("/user-service/verify-email", userController.verifyEmail);
router.post("/user-service/resend-verify-email", authNoEmailVerification, userController.resendVerifyEmail);
router.post("/user-service/send-password-reset", userController.sendPasswordReset);
router.post("/user-service/reset-password", userController.resetPassword);
router.patch("/user-service/add-name", auth, userController.addName);
router.post("/user-service/get-token", authRefresh, userController.getToken);
export default router;
+15
View File
@@ -0,0 +1,15 @@
import { Router } from "express";
import UserGoogleController from "../controllers/userGoogle";
import authFullUser from "../middleware/authFullUser";
const userGoogleController = new UserGoogleController();
const router = Router();
router.post("/user-service/create-google-storage-url", authFullUser, userGoogleController.createGoogleStorageURL);
router.post("/user-service/add-google-storage", authFullUser, userGoogleController.addGoogleStorage);
router.delete("/user-service/remove-google-storage", authFullUser, userGoogleController.removeGoogleStorage);
export default router;
+20
View File
@@ -0,0 +1,20 @@
import { Router } from "express";
import auth from "../middleware/auth";
import UserPersonalController from "../controllers/userPersonal";
import authFullUser from "../middleware/authFullUser";
const userPersonalController = new UserPersonalController();
const router = Router();
router.get("/user-service/download-personal-file-list", auth, userPersonalController.downloadPersonalFileList);
router.post("/user-service/upload-personal-file-list", auth, userPersonalController.uploadPersonalFileList);
router.post("/user-service/add-s3-storage", authFullUser, userPersonalController.addS3Storage);
router.delete("/user-service/remove-s3-storage", authFullUser, userPersonalController.removeS3Storage);
router.delete("/user-service/remove-s3-metadata", auth, userPersonalController.removeS3Metadata);
export default router;
-1
View File
@@ -1,4 +1,3 @@
const prompt = require('password-prompt')
import env from "../enviroment/env";
import crypto from "crypto";
import getWebUIKey from "./getWebUIKey";
+1 -1
View File
@@ -40,7 +40,7 @@ const getWebUIKey = () => {
const server = http.createServer(app) as any;
server.listen(port, url, () => {
server.listen(port, () => {
console.log(`\nPlease navigate to http://localhost:${port} to enter encryption key\n`)
+25 -32
View File
@@ -1,61 +1,54 @@
import jwt from "jsonwebtoken";
import User, {UserInterface} from "../models/user";
import env from "../enviroment/env";
import {Request, Response, NextFunction} from "express";
interface RequestType extends Request {
user?: UserInterface,
user?: userAccessType,
token?: string,
encryptedToken?: string,
}
type jwtType = {
iv: Buffer,
user: userAccessType
}
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
admin: boolean,
botChecked: boolean,
username: string,
}
const auth = async(req: RequestType, res: Response, next: NextFunction) => {
try {
const token = req.header("Authorization")!.replace("Bearer ", "");
const accessToken = req.cookies["access-token"];
const decoded = jwt.verify(token, env.password!) as jwtType;
if (!accessToken) throw new Error("No Access Token");
const iv = decoded.iv;
const user = await User.findOne({_id: decoded._id}) as UserInterface;
const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(token, encrpytionKey, iv);
const decoded = jwt.verify(accessToken, env.passwordAccess!) as jwtType;
let tokenFound = false;
for (let i = 0; i < user.tokens.length; i++) {
const user = decoded.user;
const currentToken = user.tokens[i].token;
if (!user) throw new Error("No User");
if (!user.emailVerified && !env.disableEmailVerification) throw new Error("Email Not Verified")
if (currentToken === encryptedToken) {
tokenFound = true;
break;
}
}
req.user = user;
if (!user || !tokenFound) {
throw new Error("User not found")
} else {
req.token = token;
req.encryptedToken = encryptedToken
req.user = user;
next();
}
next();
} catch (e) {
console.log(e);
res.status(401).send({error: "Error Authenticating"})
if (e.message !== "No Access Token" &&
e.message !== "No User" &&
e.message !== "Email Not Verified") console.log("\nAuthorization Middleware Error:", e.message);
res.status(401).send("Error Authenticating");
}
}
export default auth;
export default auth;
+59
View File
@@ -0,0 +1,59 @@
import jwt from "jsonwebtoken";
import User, {UserInterface} from "../models/user";
import env from "../enviroment/env";
import {Request, Response, NextFunction} from "express";
interface RequestType extends Request {
user?: UserInterface,
token?: string,
encryptedToken?: string,
}
type jwtType = {
iv: Buffer,
user: userAccessType
}
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
admin: boolean,
botChecked: boolean,
username: string,
}
const authFullUser = async(req: RequestType, res: Response, next: NextFunction) => {
try {
const accessToken = req.cookies["access-token"];
if (!accessToken) throw new Error("No Access Token");
const decoded = jwt.verify(accessToken, env.passwordAccess!) as jwtType;
const user = decoded.user;
if (!user) throw new Error("No User");
if (!user.emailVerified && !env.disableEmailVerification) throw new Error("Email Not Verified")
const fullUser = await User.findById(user._id);
if (!fullUser) throw new Error("No User");
req.user = fullUser;
next();
} catch (e) {
if (e.message !== "No Access Token" &&
e.message !== "No User" &&
e.message !== "Email Not Verified") console.log("\nAuthorization Full User Middleware Error:", e.message);
res.status(401).send("Error Authenticating");
}
}
export default authFullUser;
+52
View File
@@ -0,0 +1,52 @@
import jwt from "jsonwebtoken";
import env from "../enviroment/env";
import {Request, Response, NextFunction} from "express";
import { createLogoutCookie } from "../cookies/createCookies";
interface RequestType extends Request {
user?: userAccessType,
token?: string,
encryptedToken?: string,
}
type jwtType = {
iv: Buffer,
user: userAccessType
}
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
botChecked: boolean,
}
const auth = async(req: RequestType, res: Response, next: NextFunction) => {
try {
const accessToken = req.cookies["access-token"];
if (!accessToken) throw new Error("No Access Token");
const decoded = jwt.verify(accessToken, env.passwordAccess!) as jwtType;
const user = decoded.user;
if (!user) throw new Error("No User");
req.user = user;
next();
} catch (e) {
if (e.message !== "No Access Token" &&
e.message !== "No User") console.log("\nAuthorization Logout Middleware Error:", e.message);
createLogoutCookie(res);
return res.status(401).send("Error Authenticating");
}
}
export default auth;
@@ -0,0 +1,57 @@
import jwt from "jsonwebtoken";
import User, {UserInterface} from "../models/user";
import env from "../enviroment/env";
import {Request, Response, NextFunction} from "express";
import userUpdateCheck from "../utils/userUpdateCheck";
interface RequestType extends Request {
user?: userAccessType,
token?: string,
encryptedToken?: string,
}
type jwtType = {
iv: Buffer,
user: userAccessType
}
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
botChecked: boolean,
}
const auth = async(req: RequestType, res: Response, next: NextFunction) => {
try {
const accessToken = req.cookies["access-token"];
if (!accessToken) throw new Error("No Access Token");
const decoded = jwt.verify(accessToken, env.passwordAccess!) as jwtType;
let user = decoded.user;
if (!user) throw new Error("No User");
if (!user.emailVerified && !env.disableEmailVerification) {
const currentUUID = req.headers.uuid as string;
user = await userUpdateCheck(res, user._id, currentUUID);
}
req.user = user;
next();
} catch (e) {
if (e.message !== "No Access Token" &&
e.message !== "No User") console.log("\nAuthorization No Email Verification Middleware Error:", e.message);
return res.status(401).send("Error Authenticating");
}
}
export default auth;
+92
View File
@@ -0,0 +1,92 @@
import jwt from "jsonwebtoken";
import User, {UserInterface} from "../models/user";
import env from "../enviroment/env";
import {Request, Response, NextFunction} from "express";
import { ObjectID } from "mongodb";
interface RequestType extends Request {
user?: UserInterface,
token?: string,
encryptedToken?: string,
}
type jwtType = {
iv: Buffer,
_id: string,
time: number
}
const removeOldTokens = async(userID: string, uuid: string | undefined, oldTime: number) => {
try {
const minusTime = oldTime - (1000 * 60 * 60);
//const minusTime = oldTime - (1000);
uuid = uuid ? uuid : "unknown";
if (uuid === "unknown") return;
await User.updateOne({_id: userID}, {$pull: {tokens: {uuid, time: {$lt: minusTime}}}})
} catch (e) {
console.log("cannot remove old tokens", e);
}
}
const authRefresh = async(req: RequestType, res: Response, next: NextFunction) => {
try {
const refreshToken = req.cookies["refresh-token"];
const currentUUID = req.headers.uuid as string;
if (!refreshToken) throw new Error("No Refresh Token");
const decoded = jwt.verify(refreshToken, env.passwordRefresh!) as jwtType;
const time = decoded.time;
const user = await User.findById(new ObjectID(decoded._id));
if (!user) throw new Error("No User");
const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(refreshToken, encrpytionKey, decoded.iv);
let tokenFound = false;
for (let i = 0; i < user.tokens.length; i++) {
const currentEncryptedToken = user.tokens[i].token;
if (currentEncryptedToken === encryptedToken) {
tokenFound = true;
removeOldTokens(user._id, currentUUID, time);
break;
}
}
if (!tokenFound) {
console.log("token not found", encryptedToken);
console.log("token list", user.tokens);
}
if (!tokenFound) throw new Error("Refresh Token Not Found");
req.user = user;
next();
} catch (e) {
if (e.message !== "No Refresh Token" &&
e.message !== "No User" &&
e.message !== "Refresh Token Not Found") console.log("\nAuthorization Refresh Middleware Error:", e.message);
res.status(401).send("Error Refreshing Token");
}
}
export default authRefresh;
+89
View File
@@ -0,0 +1,89 @@
import jwt from "jsonwebtoken";
import User, {UserInterface} from "../models/user";
import env from "../enviroment/env";
import {Request, Response, NextFunction} from "express";
import { ObjectID } from "mongodb";
interface RequestType extends Request {
user?: UserInterface,
token?: string,
encryptedToken?: string,
accessTokenStreamVideo?: string
}
type jwtType = {
iv: Buffer,
_id: string,
time: number
}
const removeOldTokens = async(userID: string, uuid: string | undefined, oldTime: number) => {
try {
const minusTime = oldTime - (60 * 1000 * 60 * 24);
uuid = uuid ? uuid : "unknown";
if (uuid === "unknown") return;
await User.updateOne({_id: userID}, {$pull: {tempTokens: {uuid, time: {$lt: minusTime}}}})
} catch (e) {
console.log("cannot remove old tokens", e);
}
}
const authStreamVideo = async(req: RequestType, res: Response, next: NextFunction) => {
try {
const accessTokenStreamVideo = req.cookies["video-access-token"];
const currentUUID = req.headers.uuid as string;
if (!accessTokenStreamVideo) throw new Error("No Access Token");
const decoded = jwt.verify(accessTokenStreamVideo, env.passwordAccess!) as jwtType;
const time = decoded.time;
const user = await User.findById(new ObjectID(decoded._id));
if (!user) throw new Error("No User");
const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(accessTokenStreamVideo, encrpytionKey, decoded.iv);
let tokenFound = false;
for (let i = 0; i < user.tempTokens.length; i++) {
const currentEncryptedToken = user.tempTokens[i].token;
if (currentEncryptedToken === encryptedToken) {
tokenFound = true;
removeOldTokens(user._id, currentUUID, time);
break;
}
}
if (!tokenFound) throw new Error("Access Token Not Found");
req.user = user;
req.accessTokenStreamVideo = encryptedToken;
next();
} catch (e) {
if (e.message !== "No Access Token" &&
e.message !== "No User" &&
e.message !== "Access Token Not Found") console.log("\nAuthorization Stream Video Middleware Error:", e.message);
res.status(401).send("Error Authenticating");
}
}
export default authStreamVideo;
View File
+47 -47
View File
@@ -1,70 +1,70 @@
import jwt from "jsonwebtoken";
import User, {UserInterface} from "../models/user";
import env from "../enviroment/env";
import {Request, Response, NextFunction} from "express";
// import jwt from "jsonwebtoken";
// import User, {UserInterface} from "../models/user";
// import env from "../enviroment/env";
// import {Request, Response, NextFunction} from "express";
interface RequestType extends Request {
user?: UserInterface,
auth?: boolean,
encryptedTempToken?: string,
}
// interface RequestType extends Request {
// user?: UserInterface,
// auth?: boolean,
// encryptedTempToken?: string,
// }
type jwtType = {
iv: Buffer,
_id: string,
}
// type jwtType = {
// iv: Buffer,
// _id: string,
// }
const tempAuth = async(req: RequestType, res: Response, next: NextFunction) => {
// const tempAuth = async(req: RequestType, res: Response, next: NextFunction) => {
try {
// try {
const token = req.params.tempToken;
// const token = req.params.tempToken;
const decoded = jwt.verify(token, env.password!) as jwtType;
// const decoded = jwt.verify(token, env.passwordAccess!) as jwtType;
const iv = decoded.iv;
// const iv = decoded.iv;
const user = await User.findOne({_id: decoded._id}) as UserInterface;
const encrpytionKey = user.getEncryptionKey();
// const user = await User.findOne({_id: decoded._id}) as UserInterface;
// const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(token, encrpytionKey, iv);
// const encryptedToken = user.encryptToken(token, encrpytionKey, iv);
let tokenFound = false;
for (let i = 0; i < user.tempTokens.length; i++) {
// let tokenFound = false;
// for (let i = 0; i < user.tempTokens.length; i++) {
const currentToken = user.tempTokens[i].token;
// const currentToken = user.tempTokens[i].token;
if (currentToken === encryptedToken) {
tokenFound = true;
break;
}
}
// if (currentToken === encryptedToken) {
// tokenFound = true;
// break;
// }
// }
if (!user || !tokenFound) {
// if (!user || !tokenFound) {
throw new Error("User Not Found")
// throw new Error("User Not Found")
} else {
// } else {
user.tempTokens = user.tempTokens.filter((filterToken) => {
// user.tempTokens = user.tempTokens.filter((filterToken) => {
return filterToken.token !== encryptedToken
})
// return filterToken.token !== encryptedToken
// })
await user.save();
// await user.save();
req.user = user;
req.auth = true;
req.encryptedTempToken = encryptedToken;
// req.user = user;
// req.auth = true;
// req.encryptedTempToken = encryptedToken;
next();
}
// next();
// }
} catch (e) {
console.log(e);
res.status(401).send();
}
}
// } catch (e) {
// console.log(e);
// res.status(401).send();
// }
// }
export default tempAuth;
// export default tempAuth;
+48 -48
View File
@@ -1,70 +1,70 @@
import jwt from "jsonwebtoken";
import User, {UserInterface} from "../models/user";
import env from "../enviroment/env";
import {Request, Response, NextFunction} from "express";
// import jwt from "jsonwebtoken";
// import User, {UserInterface} from "../models/user";
// import env from "../enviroment/env";
// import {Request, Response, NextFunction} from "express";
interface RequestType extends Request {
user?: UserInterface,
auth?: boolean,
encryptedTempToken?: string,
}
// interface RequestType extends Request {
// user?: UserInterface,
// auth?: boolean,
// encryptedTempToken?: string,
// }
type jwtType = {
iv: Buffer,
_id: string,
cookie: string
}
// type jwtType = {
// iv: Buffer,
// _id: string,
// cookie: string
// }
const tempAuthVideo = async(req: RequestType, res: Response, next: NextFunction) => {
// const tempAuthVideo = async(req: RequestType, res: Response, next: NextFunction) => {
try {
// try {
const token = req.params.tempToken;
// const token = req.params.tempToken;
const decoded = jwt.verify(token, env.password!) as jwtType;
// const decoded = jwt.verify(token, env.passwordAccess!) as jwtType;
const iv = decoded.iv;
// const iv = decoded.iv;
if (req.params.uuid !== decoded.cookie) {
// if (req.params.uuid !== decoded.cookie) {
throw new Error("Cookie mismatch")
}
// throw new Error("Cookie mismatch")
// }
const user = await User.findOne({_id: decoded._id}) as UserInterface;
const encrpytionKey = user.getEncryptionKey();
// const user = await User.findOne({_id: decoded._id}) as UserInterface;
// const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(token, encrpytionKey, iv);
// const encryptedToken = user.encryptToken(token, encrpytionKey, iv);
let tokenFound = false;
for (let i = 0; i < user.tempTokens.length; i++) {
// let tokenFound = false;
// for (let i = 0; i < user.tempTokens.length; i++) {
const currentToken = user.tempTokens[i].token;
// const currentToken = user.tempTokens[i].token;
if (currentToken === encryptedToken) {
tokenFound = true;
break;
}
}
// if (currentToken === encryptedToken) {
// tokenFound = true;
// break;
// }
// }
if (!user || !tokenFound) {
// if (!user || !tokenFound) {
throw new Error("User not found");
// throw new Error("User not found");
} else {
// } else {
await user.save();
// await user.save();
req.user = user;
req.auth = true;
req.encryptedTempToken = encryptedToken;
next();
}
// req.user = user;
// req.auth = true;
// req.encryptedTempToken = encryptedToken;
// next();
// }
} catch (e) {
console.log(e);
res.status(401).send();
}
}
// } catch (e) {
// console.log(e);
// res.status(401).send();
// }
// }
export default tempAuthVideo;
// export default tempAuthVideo;
+6 -3
View File
@@ -1,5 +1,5 @@
import mongoose, {Document} from "mongoose";
import { Binary } from "mongodb";
import { Binary, ObjectID } from "mongodb";
const fileSchema = new mongoose.Schema({
@@ -53,6 +53,7 @@ const fileSchema = new mongoose.Schema({
link: String,
filePath: String,
s3ID: String,
personalFile: Boolean,
},
required: true
@@ -67,7 +68,7 @@ export interface FileInterface extends Document {
filename: string,
lastErrorObject: {updatedExisting: any}
metadata: {
owner: string,
owner: string | ObjectID,
parent: string,
parentList: string,
hasThumbnail: boolean,
@@ -79,9 +80,11 @@ export interface FileInterface extends Document {
link?: string,
filePath?: string,
s3ID?: string,
personalFile? : boolean
}
}
const File = mongoose.model<FileInterface>("fs.files", fileSchema);
export default File;
export default File;
module.exports = File;
+6 -3
View File
@@ -20,8 +20,8 @@ const folderSchema = new mongoose.Schema({
parentList: {
type: Array,
required:true
}
},
personalFolder: Boolean
}, {
timestamps: true
@@ -31,8 +31,11 @@ export interface FolderInterface extends Document {
name: string,
parent: string,
owner: string,
createdAt: Date,
updatedAt: Date,
parentList: string[],
_doc?: any
_doc?: any,
personalFolder?: boolean
}
const Folder = mongoose.model<FolderInterface>("Folder", folderSchema);
+8 -3
View File
@@ -21,21 +21,26 @@ const thumbnailSchema = new mongoose.Schema({
IV: {
type: Buffer,
},
s3ID: String
s3ID: String,
personalFile: String,
}, {
timestamps: true
})
export interface ThumbnailInterface extends Document {
_id: string,
_id: any,
name: string,
owner: string,
data?: any,
path?: string,
IV?: Buffer,
s3ID? : string,
personalFile?: boolean,
createdAt: Date,
updatedAt: Date
}
const Thumbnail = mongoose.model<ThumbnailInterface>("Thumbnail", thumbnailSchema);
export default Thumbnail;
export default Thumbnail;
module.exports = Thumbnail;
+353 -18
View File
@@ -10,7 +10,6 @@ const userSchema = new mongoose.Schema({
name:
{
type:String,
trim: true
},
email: {
type: String,
@@ -38,12 +37,28 @@ const userSchema = new mongoose.Schema({
token: {
type: String,
required: true
},
uuid: {
type: String,
required: true,
},
time: {
type: Number,
required: true
}
}],
tempTokens: [{
token: {
type: String,
required: true
},
uuid: {
type: String,
required: true,
},
time: {
type: Number,
required: true
}
}],
privateKey: {
@@ -51,8 +66,71 @@ const userSchema = new mongoose.Schema({
},
publicKey: {
type: String,
}
},
emailVerified: {
type: Boolean
},
emailToken: {
type: String,
},
passwordResetToken: {
type: String
},
googleDriveEnabled: {
type: Boolean
},
googleDriveData: {
id: {
type: String
},
key: {
type: String
},
iv: {
type: Buffer
},
token: {
type: String
}
},
s3Enabled: {
type: Boolean
},
s3Data: {
id: {
type: String
},
key: {
type: String
},
bucket: {
type: String
},
iv: {
type: Buffer
}
},
personalStorageCanceledDate: Number,
storageData: {
storageSize: Number,
storageLimit: Number,
failed: Boolean,
},
storageDataPersonal: {
storageSize: Number,
failed: Boolean,
},
storageDataGoogle: {
storageSize: Number,
storageLimit: Number,
failed: Boolean,
},
activeSubscription: Boolean,
planID: String,
passwordLastModified: Number,
lastSubscriptionCheckTime: Number,
lastSubscriptionStatus: Boolean
}, {
timestamps: true
})
@@ -67,17 +145,68 @@ export interface UserInterface extends Document {
privateKey?: string,
publicKey?: string,
token?: string,
emailVerified?: boolean,
emailToken?: string,
passwordResetToken?: string,
googleDriveEnabled?: boolean,
googleDriveData?: {
id?: string,
key?: string,
iv?: Buffer,
token?: string,
},
s3Enabled?: boolean,
s3Data?: {
id?: string,
key?: string,
bucket?: string,
iv?: Buffer,
},
storageData?: {
storageSize?: number,
storageLimit?: number,
failed?: boolean,
},
storageDataPersonal?: {
storageSize?: number,
failed?: boolean,
},
storageDataGoogle?: {
storageSize?: number,
storageLimit?: number,
failed?: boolean,
},
activeSubscription?: boolean,
passwordLastModified?: number,
personalStorageCanceledDate?: number,
planID?: string,
lastSubscriptionCheckTime?: number,
lastSubscriptionStatus?: boolean,
getEncryptionKey: () => Buffer | undefined;
generateTempAuthToken: () => any;
generateTempAuthTokenVideo: (cookie: string) => any;
generateTempAuthToken: () => Promise<any>;
encryptToken: (tempToken: any, key: any, publicKey: any) => any;
findByCreds: (email: string, password: string) => UserInterface;
generateAuthToken: () => any;
decryptToken: (encryptedToken: any, key: any, publicKey: any) => any;
findByCreds: (email: string, password: string) => Promise<UserInterface>;
generateAuthToken: (uuid: string | undefined) => Promise<{accessToken: string, refreshToken: string}>
generateAuthTokenStreamVideo: (uuid: string | undefined) => Promise<string>
generateEncryptionKeys: () => Promise<void>;
changeEncryptionKey: (randomKey: Buffer) => void;
changeEncryptionKey: (randomKey: Buffer) => Promise<void>;
generateEmailVerifyToken: () => Promise<string>;
generatePasswordResetToken: () => Promise<string>;
encryptDriveIDandKey: (ID:string, key: string) => Promise<void>;
decryptDriveIDandKey: () => Promise<{clientID: string, clientKey: string}>;
encryptDriveTokenData: (token: Object) => Promise<void>;
decryptDriveTokenData: () => Promise<any>;
encryptS3Data: (id: string, key: string, bucket: string) => Promise<void>;
decryptS3Data: () => Promise<{id: string, key: string, bucket: string}>
}
const maxAgeAccess = 60 * 1000 * 20 + (1000 * 60);
const maxAgeRefresh = 60 * 1000 * 60 * 24 * 30 + (1000 * 60);
const maxAgeAccessStreamVideo = 60 * 1000 * 60 * 24;
userSchema.pre("save", async function(this: any, next: any) {
const user = this;
@@ -101,7 +230,6 @@ userSchema.statics.findByCreds = async(email: string, password: string) => {
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
console.log("incorrect password")
throw new Error("Incorrect password");
}
@@ -117,25 +245,75 @@ userSchema.methods.toJSON = function() {
delete userObject.password;
delete userObject.tokens;
delete userObject.tempTokens;
delete userObject.privateKey;
delete userObject.publicKey;
return userObject;
}
userSchema.methods.generateAuthToken = async function() {
userSchema.methods.generateAuthTokenStreamVideo = async function(uuid: string | undefined) {
const iv = crypto.randomBytes(16);
const user = this;
let token = jwt.sign({_id:user._id.toString(), iv}, env.password!);
const user = this;
const date = new Date();
const time = date.getTime();
let accessTokenStreamVideo = jwt.sign({_id:user._id.toString(), iv, time}, env.passwordAccess!, {expiresIn: maxAgeAccessStreamVideo.toString()});
const encryptionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(token, encryptionKey, iv);
const encryptedToken = user.encryptToken(accessTokenStreamVideo, encryptionKey, iv);
user.tokens = user.tokens.concat({token: encryptedToken});
uuid = uuid ? uuid : "unknown";
await user.save();
return token;
await User.updateOne({_id: user._id}, {$push: {"tempTokens": {token: encryptedToken, uuid, time}}});
return accessTokenStreamVideo;
}
userSchema.methods.generateAuthToken = async function(uuid: string | undefined) {
const iv = crypto.randomBytes(16);
const user = this;
const date = new Date();
const time = date.getTime();
const userObj = {_id: user._id, emailVerified: user.emailVerified || env.disableEmailVerification, email: user.email, s3Enabled: user.s3Enabled, googleDriveEnabled: user.googleDriveEnabled}
let accessToken = jwt.sign({user: userObj, iv}, env.passwordAccess!, {expiresIn: maxAgeAccess.toString()});
let refreshToken = jwt.sign({_id:user._id.toString(), iv, time}, env.passwordRefresh!, {expiresIn: maxAgeRefresh.toString()});
const encryptionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(refreshToken, encryptionKey, iv);
//user.tokens = user.tokens.concat({token: encryptedToken});
uuid = uuid ? uuid : "unknown";
await User.updateOne({_id: user._id}, {$push: {"tokens": {token: encryptedToken, uuid, time}}})
// console.log("saving user")
// console.log("user saved")
return {accessToken, refreshToken};
// const iv = crypto.randomBytes(16);
// const user = this;
// let token = jwt.sign({_id:user._id.toString(), iv}, env.passwordAccess!);
// const encryptionKey = user.getEncryptionKey();
// const encryptedToken = user.encryptToken(token, encryptionKey, iv);
// user.tokens = user.tokens.concat({token: encryptedToken});
// await user.save();
// return token;
}
userSchema.methods.encryptToken = function(token: string, key: string, iv: any) {
@@ -243,7 +421,7 @@ userSchema.methods.generateTempAuthToken = async function() {
const iv = crypto.randomBytes(16);
const user = this as UserInterface;
const token = jwt.sign({_id:user._id.toString(), iv}, env.password!, {expiresIn: "3000ms"});
const token = jwt.sign({_id:user._id.toString(), iv}, env.passwordAccess!, {expiresIn: "3000ms"});
const encryptionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(token, encryptionKey, iv);
@@ -254,12 +432,168 @@ userSchema.methods.generateTempAuthToken = async function() {
return token;
}
userSchema.methods.generateEmailVerifyToken = async function() {
const iv = crypto.randomBytes(16);
const user = this as UserInterface;
const token = jwt.sign({_id:user._id.toString(), iv}, env.passwordAccess!, {expiresIn: "1d"});
const encryptionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(token, encryptionKey, iv);
user.emailToken = encryptedToken;
await user.save();
return token;
}
userSchema.methods.encryptDriveIDandKey = async function(ID: string, key: string) {
const iv = crypto.randomBytes(16);
const user = this as UserInterface;
const encryptedKey = user.getEncryptionKey();
const encryptedDriveID = user.encryptToken(ID, encryptedKey, iv);
const encryptedDriveKey = user.encryptToken(key, encryptedKey, iv);
if (!user.googleDriveData) user.googleDriveData = {};
user.googleDriveData!.id = encryptedDriveID;
user.googleDriveData!.key = encryptedDriveKey;
user.googleDriveData!.iv = iv;
await user.save();
}
userSchema.methods.decryptDriveIDandKey = async function() {
const user = this as UserInterface;
const iv = user.googleDriveData!.iv;
const encryptedKey = user.getEncryptionKey();
const encryptedDriveID = user.googleDriveData?.id;
const encryptedDriveKey = user.googleDriveData?.key;
const decryptedDriveID = user.decryptToken(encryptedDriveID, encryptedKey, iv)
const decryptedDriveKey = user.decryptToken(encryptedDriveKey, encryptedKey, iv);
return {
clientID: decryptedDriveID,
clientKey: decryptedDriveKey
}
}
userSchema.methods.encryptDriveTokenData = async function(token: Object) {
const user = this as UserInterface;
const iv = user.googleDriveData?.iv;
const tokenToString = JSON.stringify(token);
const encryptedKey = user.getEncryptionKey();
const encryptedDriveToken = user.encryptToken(tokenToString, encryptedKey, iv);;
if (!user.googleDriveData) user.googleDriveData = {};
user.googleDriveData.token = encryptedDriveToken;
user.googleDriveEnabled = true;
await user.save();
}
userSchema.methods.decryptDriveTokenData = async function() {
const user = this as UserInterface;
const iv = user.googleDriveData!.iv;
const encryptedKey = user.getEncryptionKey();
const encryptedToken = user.googleDriveData?.token;
const decryptedToken = user.decryptToken(encryptedToken, encryptedKey, iv);
const tokenToObj = JSON.parse(decryptedToken);
return tokenToObj;
}
userSchema.methods.encryptS3Data = async function(ID: string, key: string, bucket: string) {
const iv = crypto.randomBytes(16);
const user = this as UserInterface;
const encryptedKey = user.getEncryptionKey();
const encryptedS3ID = user.encryptToken(ID, encryptedKey, iv);
const encryptedS3Key = user.encryptToken(key, encryptedKey, iv);
const encryptedS3Bucket = user.encryptToken(bucket, encryptedKey, iv);
if (!user.s3Data) user.s3Data = {};
user.s3Data!.id = encryptedS3ID;
user.s3Data!.key = encryptedS3Key;
user.s3Data!.bucket = encryptedS3Bucket;
user.s3Data!.iv = iv;
user.s3Enabled = true;
await user.save();
}
userSchema.methods.decryptS3Data = async function() {
const user = this as UserInterface;
const iv = user.s3Data?.iv;
const encryptedKey = user.getEncryptionKey();
const encrytpedS3ID = user.s3Data?.id;
const encryptedS3Key = user.s3Data?.key;
const encryptedS3Bucket = user.s3Data?.bucket;
const decrytpedS3ID = user.decryptToken(encrytpedS3ID, encryptedKey, iv);
const decryptedS3Key = user.decryptToken(encryptedS3Key, encryptedKey, iv);
const decryptedS3Bucket = user.decryptToken(encryptedS3Bucket, encryptedKey, iv);
//console.log("decrypted keys", decrytpedS3ID, decryptedS3Key, decryptedS3Bucket);
return {
id: decrytpedS3ID,
key: decryptedS3Key,
bucket: decryptedS3Bucket,
}
}
userSchema.methods.generatePasswordResetToken = async function() {
const iv = crypto.randomBytes(16);
const user = this as UserInterface;
const token = jwt.sign({_id:user._id.toString(), iv}, env.passwordAccess!, {expiresIn: "1h"});
const encryptionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(token, encryptionKey, iv);
user.passwordResetToken = encryptedToken;
await user.save();
return token;
}
userSchema.methods.generateTempAuthTokenVideo = async function(cookie: string) {
const iv = crypto.randomBytes(16);
const user = this;
const token = jwt.sign({_id:user._id.toString(), cookie, iv}, env.password!, {expiresIn:"5h"});
const token = jwt.sign({_id:user._id.toString(), cookie, iv}, env.passwordAccess!, {expiresIn:"5h"});
const encryptionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(token, encryptionKey, iv);
@@ -272,4 +606,5 @@ userSchema.methods.generateTempAuthTokenVideo = async function(cookie: string) {
const User = mongoose.model<UserInterface>("User", userSchema);
export default User;
export default User;
module.exports = User;
+18 -8
View File
@@ -4,6 +4,11 @@ import userRouter from "../express-routers/user";
import fileRouter from "../express-routers/file";
import folderRouter from "../express-routers/folder";
import storageRouter from "../express-routers/storage";
import googleFileRouter from "../express-routers/googleFile";
import personalFileRouter from "../express-routers/personalFile";
import googleFolderRouter from "../express-routers/googleFolder";
import userGoogleRouter from "../express-routers/userGoogle";
import userPersonalRouter from "../express-routers/userPersonal";
import bodyParser from "body-parser";
import https from "https";
import fs from "fs";
@@ -11,8 +16,9 @@ import helmet from "helmet";
import busboy from "connect-busboy";
import compression from "compression";
import http from "http";
import cookieParser from "cookie-parser";
import env from "../enviroment/env";
// import requestIp from "request-ip";
const app = express();
const publicPath = path.join(__dirname, "..", "..", "public");
@@ -21,7 +27,7 @@ let server: any;
let serverHttps: any;
if (process.env.SSL === 'true') {
const cert = fs.readFileSync("certificate.crt")
const ca = fs.readFileSync("certificate.ca-bundle");
const key = fs.readFileSync("certificate.key");
@@ -40,23 +46,26 @@ server = http.createServer(app);
require("../db/mongoose");
app.use(cookieParser(env.passwordCookie));
app.use(helmet())
app.use(compression());
app.use(express.json());
app.use(express.static(publicPath));
app.use(bodyParser.json({limit: "50mb"}));
app.use(bodyParser.urlencoded({limit: "50mb", extended: true, parameterLimit:50000}))
// app.use(requestIp.mw());
app.use(busboy({
highWaterMark: 2 * 1024 * 1024,
highWaterMark: 2 * 1024 * 1024,
}));
app.use(userRouter, fileRouter, folderRouter, storageRouter);
app.use(userRouter, fileRouter, folderRouter, storageRouter, googleFileRouter, personalFileRouter, googleFolderRouter, userPersonalRouter, userGoogleRouter);
const nodeMode = process.env.NODE_ENV ? "Production" : "Development/Testing";
//const nodeMode = process.env.NODE_ENV ? "Production" : "Development/Testing";
console.log("Node Enviroment Mode:", nodeMode);
//console.log("Node Enviroment Mode:", nodeMode);
app.get("*", (_: Request, res: Response) => {
@@ -64,4 +73,5 @@ app.get("*", (_: Request, res: Response) => {
res.sendFile(path.join(publicPath, "index.html"))
})
export default {server, serverHttps};
export default {server, serverHttps};
@@ -13,7 +13,7 @@ import File, { FileInterface } from "../../models/file";
import getFileSize from "./utils/getFileSize";
import DbUtilFile from "../../db/utils/fileUtils/index";
import awaitStream from "./utils/awaitStream";
import createThumbnailFS from "./utils/createThumbnailFS";
import createThumbnailAny from "./utils/createThumbailAny";
import imageChecker from "../../utils/imageChecker";
import Thumbnail, {ThumbnailInterface} from "../../models/thumbnail";
import streamToBuffer from "../../utils/streamToBuffer";
@@ -23,7 +23,11 @@ import removeChunksFS from "./utils/removeChunksFS";
import getPrevIVFS from "./utils/getPrevIVFS";
import awaitStreamVideo from "./utils/awaitStreamVideo";
import fixStartChunkLength from "./utils/fixStartChunkLength";
import Folder from "../../models/folder";
import Folder, { FolderInterface } from "../../models/folder";
import addToStoageSize from "./utils/addToStorageSize";
import subtractFromStorageSize from "./utils/subtractFromStorageSize";
import ForbiddenError from "../../utils/ForbiddenError";
import { ObjectID } from "mongodb";
const dbUtilsFile = new DbUtilFile();
@@ -38,8 +42,7 @@ class FileSystemService implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const initVect = crypto.randomBytes(16);
@@ -52,10 +55,11 @@ class FileSystemService implements ChunkInterface {
const parent = formData.get("parent") || "/"
const parentList = formData.get("parentList") || "/";
const size = formData.get("size") || ""
const personalFile = formData.get("personal-file") ? true : false;
let hasThumbnail = false;
let thumbnailID = ""
const isVideo = videoChecker(filename)
const systemFileName = uuid.v4();
const metadata = {
@@ -88,13 +92,13 @@ class FileSystemService implements ChunkInterface {
await currentFile.save();
console.log(currentFile);
await addToStoageSize(user, size, personalFile);
const imageCheck = imageChecker(currentFile.filename);
if (currentFile.length < 15728640 && imageCheck) {
const updatedFile = await createThumbnailFS(currentFile, filename, user);
const updatedFile = await createThumbnailAny(currentFile, filename, user);
return updatedFile;
@@ -105,6 +109,47 @@ class FileSystemService implements ChunkInterface {
}
getFileWriteStream = async(user: UserInterface, file: FileInterface, parentFolder: FolderInterface) => {
const password = user.getEncryptionKey();
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const initVect = crypto.randomBytes(16);
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
const cipher = crypto.createCipheriv('aes256', CIPHER_KEY, initVect);
const filename = file.filename
const parent = parentFolder._id
const parentList = [...parentFolder.parentList, parentFolder._id]
const size = file.metadata.size
const personalFile = file.metadata.personalFile ? true : false
let hasThumbnail = file.metadata.hasThumbnail;
let thumbnailID = file.metadata.thumbnailID
const isVideo = file.metadata.isVideo
const systemFileName = uuid.v4();
const metadata = {
owner: user._id,
parent,
parentList,
hasThumbnail,
thumbnailID,
isVideo,
size,
IV: file.metadata.IV,
filePath: env.fsDirectory + systemFileName
}
const fileWriteStream = fs.createWriteStream(metadata.filePath);
return fileWriteStream;
}
downloadFile = async(user: UserInterface, fileID: string, res: Response) => {
const currentFile: FileInterface = await dbUtilsFile.getFileInfo(fileID, user._id);
@@ -113,7 +158,7 @@ class FileSystemService implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const filePath = currentFile.metadata.filePath!;
@@ -134,17 +179,40 @@ class FileSystemService implements ChunkInterface {
await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch);
}
getFileReadStream = async(user: UserInterface, fileID: string) => {
const currentFile: FileInterface = await dbUtilsFile.getFileInfo(fileID, user._id);
if (!currentFile) throw new NotFoundError("Download File Not Found");
const password = user.getEncryptionKey();
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const filePath = currentFile.metadata.filePath!;
const IV = currentFile.metadata.IV.buffer as Buffer;
const readStream = fs.createReadStream(filePath);
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
return readStream
}
getThumbnail = async(user: UserInterface, id: string) => {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const thumbnail = await Thumbnail.findById(id) as ThumbnailInterface;
const thumbnail = await Thumbnail.findById(new ObjectID(id)) as ThumbnailInterface;
if (thumbnail.owner !== user._id.toString()) {
throw new NotAuthorizedError('Thumbnail Unauthorized Error');
throw new ForbiddenError('Thumbnail Unauthorized Error');
}
const iv = thumbnail.IV!;
@@ -174,7 +242,7 @@ class FileSystemService implements ChunkInterface {
const password = user.getEncryptionKey();
const IV = file.metadata.IV.buffer as Buffer;
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const readStream = fs.createReadStream(file.metadata.filePath!);
@@ -188,13 +256,21 @@ class FileSystemService implements ChunkInterface {
const allStreamsToErrorCatch = [readStream, decipher];
console.log("Sending Full Thumbnail...")
await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch);
console.log("Full thumbnail sent");
}
streamVideo = async(user: UserInterface, fileID: string, headers: any, res: Response, req: Request) => {
// To get this all working correctly with encryption and across
// All browsers took many days, tears, and some of my sanity.
// Shoutout to Tyzoid for helping me with the decryption
// And and helping me understand how the IVs work.
// P.S I hate safari >:(
// Why do yall have to be weird with video streaming
// 90% of the issues with this are only in Safari
// Is safari going to be the next internet explorer?
const userID = user._id;
const currentFile: FileInterface = await dbUtilsFile.getFileInfo(fileID, userID);
@@ -202,7 +278,7 @@ class FileSystemService implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const fileSize = currentFile.metadata.size;
@@ -223,22 +299,56 @@ class FileSystemService implements ChunkInterface {
let currentIV = IV;
let fixedStart = start % 16 === 0 ? start : fixStartChunkLength(start);
let fixedStart = 0;
let fixedEnd = currentFile.length;
if (start === 0 && end === 1) {
// This is for Safari/iOS, Safari will request the first
// Byte before actually playing the video. Needs to be
// 16 bytes.
fixedStart = 0;
fixedEnd = 15;
} else {
// If you're a normal browser, or this isn't Safari's first request
// We need to make it so start is divisible by 16, since AES256
// Has a block size of 16 bytes.
fixedStart = start % 16 === 0 ? start : fixStartChunkLength(start);
}
if (+start === 0) {
// This math will not work if the start is 0
// So if it is we just change fixed start back
// To 0.
fixedStart = 0;
}
const fixedEnd = currentFile.length;
// We also need to calculate the difference between the start and the
// Fixed start position. Since there will be an offset if the original
// Request is not divisible by 16, it will not return the right part
// Of the file, you will see how we do this in the awaitStreamVideo
// code.
const differenceStart = start - fixedStart;
if (fixedStart !== 0 && start !== 0) {
// If this isn't the first request, the way AES256 works is when you try to
// Decrypt a certain part of the file that isn't the start, the IV will
// Actually be the 16 bytes ahead of where you are trying to
// Start the decryption.
currentIV = await getPrevIVFS(fixedStart - 16, currentFile.metadata.filePath!) as Buffer;
}
const readStream = fs.createReadStream(currentFile.metadata.filePath!, {
start: fixedStart,
end: fixedEnd,
@@ -256,15 +366,8 @@ class FileSystemService implements ChunkInterface {
readStream.pipe(decipher);
// req.on("close", () => {
// // console.log("req closed");
// readStream.destroy();
// })
await awaitStreamVideo(start, end, differenceStart, decipher, res, req, allStreamsToErrorCatch, readStream);
const tempUUID = req.params.uuid;
await awaitStreamVideo(start, end, differenceStart, decipher, res, req, tempUUID, allStreamsToErrorCatch);
console.log("Video Stream Finished");
readStream.destroy();
}
@@ -280,7 +383,7 @@ class FileSystemService implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key");
if (!password) throw new ForbiddenError("Invalid Encryption Key");
const IV = file.metadata.IV.buffer as Buffer;
@@ -299,7 +402,6 @@ class FileSystemService implements ChunkInterface {
await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch);
if (file.metadata.linkType === "one") {
console.log("removing public link");
await dbUtilsFile.removeOneTimePublicLink(fileID);
}
}
@@ -321,6 +423,7 @@ class FileSystemService implements ChunkInterface {
await removeChunksFS(file.metadata.filePath!);
await File.deleteOne({_id: file._id});
await subtractFromStorageSize(userID, file.length, file.metadata.personalFile!)
}
deleteFolder = async(userID: string, folderID: string, parentList: string[]) => {
@@ -362,8 +465,6 @@ class FileSystemService implements ChunkInterface {
deleteAll = async(userID: string) => {
console.log("remove all request")
await Folder.deleteMany({"owner": userID});
const fileList = await dbUtilsFile.getFileListByOwner(userID);
+139 -35
View File
@@ -1,12 +1,11 @@
import mongoose from "../../db/mongoose";
import { Response, Request } from "express";
import DbUtilFolder from "../../db/utils/folderUtils";
import { Response, Request } from "express";;
import DbUtilFile from "../../db/utils/fileUtils";
import crypto from "crypto";
import videoChecker from "../../utils/videoChecker"
import imageChecker from "../../utils/imageChecker"
import { ObjectID } from "mongodb";
import createThumbnail from "./utils/createThumbnail";
import createThumbnailAny from "./utils/createThumbailAny";
import Thumbnail, {ThumbnailInterface} from "../../models/thumbnail";
import NotAuthorizedError from "../../utils/NotAuthorizedError";
import NotFoundError from "../../utils/NotFoundError";
@@ -14,13 +13,16 @@ import {GridFSBucketWriteStream} from "mongodb";
import awaitStream from "./utils/awaitStream";
import awaitUploadStream from "./utils/awaitUploadStream";
import User, { UserInterface } from "../../models/user";
import Folder from "../../models/folder";
import Folder, { FolderInterface } from "../../models/folder";
import { FileInterface } from "../../models/file";
import getBusboyData from "./utils/getBusboyData";
import ChunkInterface from "./utils/ChunkInterface";
import fixStartChunkLength from "./utils/fixStartChunkLength";
import getPrevIVMongo from "./utils/getPrevIVMongo";
import awaitStreamVideo from "./utils/awaitStreamVideo";
import addToStoageSize from "./utils/addToStorageSize";
import subtractFromStorageSize from "./utils/subtractFromStorageSize";
import ForbiddenError from "../../utils/ForbiddenError";
const conn = mongoose.connection;
@@ -36,7 +38,7 @@ class MongoService implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
let bucketStream: GridFSBucketWriteStream;
@@ -51,10 +53,11 @@ class MongoService implements ChunkInterface {
const parent = formData.get("parent") || "/"
const parentList = formData.get("parentList") || "/";
const size = formData.get("size") || ""
const personalFile = formData.get("personal-file") ? true : false;
let hasThumbnail = false;
let thumbnailID = ""
const isVideo = videoChecker(filename)
const metadata = {
owner: user._id,
parent,
@@ -66,7 +69,6 @@ class MongoService implements ChunkInterface {
IV: initVect
}
let bucket = new mongoose.mongo.GridFSBucket(conn.db);
bucketStream = bucket.openUploadStream(filename, {metadata});
@@ -75,11 +77,13 @@ class MongoService implements ChunkInterface {
const finishedFile = await awaitUploadStream(file.pipe(cipher), bucketStream, req, allStreamsToErrorCatch) as FileInterface;
await addToStoageSize(user, size, personalFile);
const imageCheck = imageChecker(filename);
if (finishedFile.length < 15728640 && imageCheck) {
const updatedFile = await createThumbnail(finishedFile, filename, user);
const updatedFile = await createThumbnailAny(finishedFile, filename, user);
return updatedFile;
@@ -89,6 +93,46 @@ class MongoService implements ChunkInterface {
}
}
getFileWriteStream = async(user: UserInterface, file: FileInterface, parentFolder: FolderInterface) => {
const password = user.getEncryptionKey();
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const initVect = crypto.randomBytes(16);
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
const cipher = crypto.createCipheriv('aes256', CIPHER_KEY, initVect);
const filename = file.filename
const parent = parentFolder._id
const parentList = [...parentFolder.parentList, parentFolder._id]
const size = file.metadata.size
const personalFile = file.metadata.personalFile ? true : false
let hasThumbnail = file.metadata.hasThumbnail;
let thumbnailID = file.metadata.thumbnailID
const isVideo = file.metadata.isVideo;
const metadata = {
owner: user._id,
parent,
parentList,
hasThumbnail,
thumbnailID,
isVideo,
size,
IV: file.metadata.IV
}
let bucket = new mongoose.mongo.GridFSBucket(conn.db);
const bucketStream = bucket.openUploadStream(filename, {metadata});
return bucketStream;
}
downloadFile = async(user: UserInterface, fileID: string, res: Response) => {
const currentFile = await dbUtilsFile.getFileInfo(fileID, user._id);
@@ -97,7 +141,7 @@ class MongoService implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const bucket = new mongoose.mongo.GridFSBucket(conn.db);
@@ -117,17 +161,39 @@ class MongoService implements ChunkInterface {
await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch);
}
getFileReadStream = async(user: UserInterface, fileID: string) => {
const currentFile = await dbUtilsFile.getFileInfo(fileID, user._id);
if (!currentFile) throw new NotFoundError("Download File Not Found");
const password = user.getEncryptionKey();
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const bucket = new mongoose.mongo.GridFSBucket(conn.db);
const IV = currentFile.metadata.IV.buffer as Buffer;
const readStream = bucket.openDownloadStream(new ObjectID(fileID));
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
return readStream
}
getThumbnail = async(user: UserInterface, id: string) => {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const thumbnail = await Thumbnail.findById(id) as ThumbnailInterface;
const thumbnail = await Thumbnail.findById(new ObjectID(id)) as ThumbnailInterface;
if (thumbnail.owner !== user._id.toString()) {
throw new NotAuthorizedError('Thumbnail Unauthorized Error');
throw new ForbiddenError('Thumbnail Unauthorized Error');
}
const iv = thumbnail.data.slice(0, 16);
@@ -155,7 +221,7 @@ class MongoService implements ChunkInterface {
const password = user.getEncryptionKey();
const IV = file.metadata.IV.buffer as Buffer;
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const readStream = bucket.openDownloadStream(new ObjectID(fileID))
@@ -167,12 +233,9 @@ class MongoService implements ChunkInterface {
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
res.set('Content-Length', file.metadata.size.toString());
console.log("Sending Full Thumbnail...")
const allStreamsToErrorCatch = [readStream, decipher];
await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch);
console.log("Full thumbnail sent");
}
getPublicDownload = async(fileID: string, tempToken: any, res: Response) => {
@@ -187,7 +250,7 @@ class MongoService implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key");
if (!password) throw new ForbiddenError("Invalid Encryption Key");
const bucket = new mongoose.mongo.GridFSBucket(conn.db);
@@ -208,13 +271,26 @@ class MongoService implements ChunkInterface {
await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch);
if (file.metadata.linkType === "one") {
console.log("removing public link");
await dbUtilsFile.removeOneTimePublicLink(fileID);
}
}
streamVideo = async(user: UserInterface, fileID: string, headers: any, res: Response, req: Request) => {
// THIS ISN'T WORKING FULLY WHEN USING MONGODB AND SAFARI,
// OTHER DATABASES SHOULD WORK, BUT I AM NOT SURE WHY
// IT WILL NOT WORK ON SAFARI SOMETIMES
// To get this all working correctly with encryption and across
// All browsers took many days, tears, and some of my sanity.
// Shoutout to Tyzoid for helping me with the decryption
// And and helping me understand how the IVs work.
// P.S I hate safari >:(
// Why do yall have to be weird with video streaming
// 90% of the issues with this are only in Safari
// Is safari going to be the next internet explorer?
const userID = user._id;
const currentFile = await dbUtilsFile.getFileInfo(fileID, userID);
@@ -222,7 +298,7 @@ class MongoService implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const fileSize = currentFile.metadata.size;
@@ -243,24 +319,60 @@ class MongoService implements ChunkInterface {
let currentIV = IV;
let fixedStart = start % 16 === 0 ? start : fixStartChunkLength(start);
let fixedStart = 0;
let fixedEnd = currentFile.length;
if (start === 0 && end === 1) {
// This is for Safari/iOS, Safari will request the first
// Byte before actually playing the video. Needs to be
// 16 bytes.
fixedStart = 0;
fixedEnd = 16;
// I am not sure why this needs to be 16 for mongoDB, on the other routes 15 works
// Fine, and I thought the start and end were inclusive, but I am really not sure
// At this point
} else {
// If you're a normal browser, or this isn't Safari's first request
// We need to make it so start is divisible by 16, since AES256
// Has a block size of 16 bytes.
fixedStart = start % 16 === 0 ? start : fixStartChunkLength(start);
}
if (+start === 0) {
// This math will not work if the start is 0
// So if it is we just change fixed start back
// To 0.
fixedStart = 0;
}
const fixedEnd = currentFile.length;
// We also need to calculate the difference between the start and the
// Fixed start position. Since there will be an offset if the original
// Request is not divisible by 16, it will not return the right part
// Of the file, you will see how we do this in the awaitStreamVideo
// code.
const differenceStart = start - fixedStart;
if (fixedStart !== 0 && start !== 0) {
// If this isn't the first request, the way AES256 works is when you try to
// Decrypt a certain part of the file that isn't the start, the IV will
// Actually be the 16 bytes ahead of where you are trying to
// Start the decryption.
currentIV = await getPrevIVMongo(fixedStart - 16, fileID) as Buffer;
}
const bucket = new mongoose.mongo.GridFSBucket(conn.db, {
chunkSizeBytes: 1024
});
const readStream = bucket.openDownloadStream(new ObjectID(fileID), {
@@ -268,26 +380,19 @@ class MongoService implements ChunkInterface {
end: fixedEnd,
});
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
res.writeHead(206, head);
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, currentIV);
decipher.setAutoPadding(false);
res.writeHead(206, head);
const allStreamsToErrorCatch = [readStream, decipher];
readStream.pipe(decipher);
// req.on("close", () => {
// // console.log("req closed");
// readStream.destroy();
// })
await awaitStreamVideo(start, end, differenceStart, decipher, res, req, allStreamsToErrorCatch, readStream);
const tempUUID = req.params.uuid;
await awaitStreamVideo(start, end, differenceStart, decipher, res, req, tempUUID, allStreamsToErrorCatch);
readStream.destroy();
}
@@ -307,6 +412,7 @@ class MongoService implements ChunkInterface {
}
await bucket.delete(new ObjectID(fileID));
await subtractFromStorageSize(userID, file.length, file.metadata.personalFile!);
}
deleteFolder = async(userID: string, folderID: string, parentList: string[]) => {
@@ -347,8 +453,6 @@ class MongoService implements ChunkInterface {
deleteAll = async(userID: string) => {
console.log("remove all request")
let bucket = new mongoose.mongo.GridFSBucket(conn.db, {
chunkSizeBytes: 1024 * 255
});
+236 -92
View File
@@ -12,7 +12,7 @@ import videoChecker from "../../utils/videoChecker";
import uuid from "uuid";
import awaitUploadStreamS3 from "./utils/awaitUploadStreamS3";
import awaitStream from "./utils/awaitStream";
import createThumbnailS3 from "./utils/createThumbnailS3";
import createThumbnailAny from "./utils/createThumbailAny";
import imageChecker from "../../utils/imageChecker";
import Thumbnail, {ThumbnailInterface} from "../../models/thumbnail";
import streamToBuffer from "../../utils/streamToBuffer";
@@ -21,9 +21,14 @@ import fixStartChunkLength from "./utils/fixStartChunkLength";
import fixEndChunkLength from "./utils/fixEndChunkLength";
import getPrevIVS3 from "./utils/getPrevIVS3";
import awaitStreamVideo from "./utils/awaitStreamVideo";
import Folder from "../../models/folder";
import Folder, { FolderInterface } from "../../models/folder";
import DbUtilFile from "../../db/utils/fileUtils/index";
import s3Auth from "../../db/S3Personal";
import addToStoageSize from "./utils/addToStorageSize";
import subtractFromStorageSize from "./utils/subtractFromStorageSize";
import ForbiddenError from "../../utils/ForbiddenError";
import { ObjectID } from "mongodb";
const dbUtilsFile = new DbUtilFile();
class S3Service implements ChunkInterface {
@@ -36,7 +41,7 @@ class S3Service implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const initVect = crypto.randomBytes(16);
@@ -49,13 +54,17 @@ class S3Service implements ChunkInterface {
const parent = formData.get("parent") || "/"
const parentList = formData.get("parentList") || "/";
const size = formData.get("size") || ""
const personalFile = formData.get("personal-file") ? true : false;
let hasThumbnail = false;
let thumbnailID = ""
const isVideo = videoChecker(filename)
const randomS3ID = uuid.v4();
const metadata = {
const s3Data: any = personalFile ? await user.decryptS3Data() : {};
const bucketName = personalFile ? s3Data.bucket : env.s3Bucket;
let metadata: any = {
owner: user._id,
parent,
parentList,
@@ -64,16 +73,18 @@ class S3Service implements ChunkInterface {
isVideo,
size,
IV: initVect,
s3ID: randomS3ID
s3ID: randomS3ID,
}
if (personalFile) metadata = {...metadata, personalFile: true}
const params = {
Bucket: env.s3Bucket,
Bucket: bucketName,
Body : file.pipe(cipher),
Key : randomS3ID
};
await awaitUploadStreamS3(params);
await awaitUploadStreamS3(params, personalFile, s3Data);
const date = new Date();
const encryptedFileSize = size;
@@ -87,12 +98,13 @@ class S3Service implements ChunkInterface {
await currentFile.save();
await addToStoageSize(user, size, personalFile);
const imageCheck = imageChecker(currentFile.filename);
if (currentFile.length < 15728640 && imageCheck) {
console.log("Creating thumbnail...")
const updatedFile = await createThumbnailS3(currentFile, filename, user);
const updatedFile = await createThumbnailAny(currentFile, filename, user);
return updatedFile;
@@ -102,6 +114,77 @@ class S3Service implements ChunkInterface {
}
}
getFileWriteStream = async(user: UserInterface, file: FileInterface, parentFolder: FolderInterface, readStream: any) => {
const password = user.getEncryptionKey();
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const initVect = crypto.randomBytes(16);
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
const cipher = crypto.createCipheriv('aes256', CIPHER_KEY, initVect);
const filename = file.filename
const parent = parentFolder._id
const parentList = [...parentFolder.parentList, parentFolder._id]
const size = file.metadata.size
const personalFile = file.metadata.personalFile ? true : false
let hasThumbnail = file.metadata.hasThumbnail;
let thumbnailID = file.metadata.thumbnailID
const isVideo = file.metadata.isVideo
const metadata = {
owner: user._id,
parent,
parentList,
hasThumbnail,
thumbnailID,
isVideo,
size,
IV: file.metadata.IV,
s3ID: file.metadata.s3ID,
personalFile
}
const s3Data: any = personalFile ? await user.decryptS3Data() : {};
const bucketName = personalFile ? s3Data.bucket : env.s3Bucket;
const params = {
Bucket: bucketName,
Body : readStream,
Key : file.metadata.s3ID
};
}
getS3AuthThumbnail = async (thumbnail: ThumbnailInterface, user: UserInterface) => {
if (thumbnail.personalFile) {
const s3Data = await user.decryptS3Data();
//console.log("s3 data", s3Data)
return {s3Storage: s3Auth(s3Data.id, s3Data.key), bucket: s3Data.bucket};
} else {
return {s3Storage: s3, bucket: env.s3Bucket};
}
}
getS3Auth = async (file: FileInterface, user: UserInterface) => {
if (file.metadata.personalFile) {
const s3Data = await user.decryptS3Data();
//console.log("s3 data", s3Data)
return {s3Storage: s3Auth(s3Data.id, s3Data.key), bucket: s3Data.bucket};
} else {
return {s3Storage: s3, bucket: env.s3Bucket};
}
}
downloadFile = async(user: UserInterface, fileID: string, res: Response) => {
const currentFile: FileInterface = await dbUtilsFile.getFileInfo(fileID, user._id);
@@ -110,7 +193,9 @@ class S3Service implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key");
const {s3Storage, bucket} = await this.getS3Auth(currentFile, user);
const IV = currentFile.metadata.IV.buffer as Buffer;
@@ -122,18 +207,52 @@ class S3Service implements ChunkInterface {
res.set('Content-Disposition', 'attachment; filename="' + currentFile.filename + '"');
res.set('Content-Length', currentFile.metadata.size.toString());
const params: any = {Bucket: env.s3Bucket, Key: currentFile.metadata.s3ID!};
const params: any = {Bucket: bucket, Key: currentFile.metadata.s3ID!};
const s3ReadStream = s3.getObject(params).createReadStream();
const s3ReadStream = s3Storage.getObject(params).createReadStream();
const allStreamsToErrorCatch = [s3ReadStream, decipher];
await awaitStream(s3ReadStream.pipe(decipher), res, allStreamsToErrorCatch);
}
getFileReadStream = async(user: UserInterface, fileID: string) => {
const currentFile: FileInterface = await dbUtilsFile.getFileInfo(fileID, user._id);
if (!currentFile) throw new NotFoundError("Download File Not Found");
const password = user.getEncryptionKey();
if (!password) throw new ForbiddenError("Invalid Encryption Key");
const {s3Storage, bucket} = await this.getS3Auth(currentFile, user);
const IV = currentFile.metadata.IV.buffer as Buffer;
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);
const params: any = {Bucket: bucket, Key: currentFile.metadata.s3ID!};
const s3ReadStream = s3Storage.getObject(params).createReadStream();
return s3ReadStream
}
streamVideo = async(user: UserInterface, fileID: string, headers: any, res: Response, req: Request) => {
// To get this all working correctly with encryption and across
// All browsers took many days, tears, and some of my sanity.
// Shoutout to Tyzoid for helping me with the decryption
// And and helping me understand how the IVs work.
// P.S I hate safari >:(
// Why do yall have to be weird with video streaming
// 90% of the issues with this are only in Safari
// Is safari going to be the next internet explorer?
const userID = user._id;
const currentFile: FileInterface = await dbUtilsFile.getFileInfo(fileID, userID);
@@ -141,9 +260,11 @@ class S3Service implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const fileSize = currentFile.metadata.size;
const isPersonal = currentFile.metadata.personalFile!;
const range = headers.range
const parts = range.replace(/bytes=/, "").split("-")
@@ -162,76 +283,85 @@ class S3Service implements ChunkInterface {
let currentIV = IV;
let fixedStart = start % 16 === 0 ? start : fixStartChunkLength(start);
let fixedStart = 0;
let fixedEnd = currentFile.length;
if (start === 0 && end === 1) {
// This is for Safari/iOS, Safari will request the first
// Byte before actually playing the video. Needs to be
// 16 bytes.
fixedStart = 0;
fixedEnd = 15;
} else {
// If you're a normal browser, or this isn't Safari's first request
// We need to make it so start is divisible by 16, since AES256
// Has a block size of 16 bytes.
fixedStart = start % 16 === 0 ? start : fixStartChunkLength(start);
// I goofed up and forgot to add the encrypted file size to S3 data,
// It just used the normal file size :(
// So you'll notice only on the S3 route do i need to fix the end length
// To, this is because the other 2 routes use the encrypted file size
// Which is always a multiple of 16. I cannot change this now
// Since previous versions will still have the issue, but
// This is a simple fix luckily.
fixedEnd = fixedEnd % 16 === 0 ? fixedEnd : fixEndChunkLength(fixedEnd)
}
if (+start === 0) {
// This math will not work if the start is 0
// So if it is we just change fixed start back
// To 0.
fixedStart = 0;
}
const fixedEnd = fileSize % 16 === 0 ? fileSize : fixEndChunkLength(fileSize); //end % 16 === 0 ? end + 15: (fixEndChunkLength(end) - 1) + 16;
// We also need to calculate the difference between the start and the
// Fixed start position. Since there will be an offset if the original
// Request is not divisible by 16, it will not return the right part
// Of the file, you will see how we do this in the awaitStreamVideo
// code.
const differenceStart = start - fixedStart;
if (fixedStart !== 0 && start !== 0) {
// If this isn't the first request, the way AES256 works is when you try to
// Decrypt a certain part of the file that isn't the start, the IV will
// Actually be the 16 bytes ahead of where you are trying to
// Start the decryption.
currentIV = await getPrevIVS3(fixedStart - 16, currentFile.metadata.s3ID!) as Buffer;
currentIV = await getPrevIVS3(fixedStart - 16, currentFile.metadata.s3ID!, isPersonal, user) as Buffer;
}
const params: any = {Bucket: env.s3Bucket, Key: currentFile.metadata.s3ID!, Range: `bytes=${fixedStart}-${fixedEnd}`};
const {s3Storage, bucket} = await this.getS3Auth(currentFile, user);
const s3ReadStream = s3.getObject(params).createReadStream();
const params: any = {Bucket: bucket, Key: currentFile.metadata.s3ID!, Range: `bytes=${fixedStart}-${fixedEnd}`};
const s3ReadStream = s3Storage.getObject(params).createReadStream();
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, currentIV);
decipher.setAutoPadding(false);
res.writeHead(206, head);
const allStreamsToErrorCatch = [s3ReadStream, decipher];
s3ReadStream.pipe(decipher);
const tempUUID = req.params.uuid;
await awaitStreamVideo(start, end, differenceStart, decipher, res, req, allStreamsToErrorCatch, s3ReadStream);
// s3ReadStream.on("data", () => {
// console.log("data", tempUUID);
// })
// req.on("close", () => {
// // console.log("Destoying read stream");
// // s3ReadStream.destroy();
// // console.log("Read Stream Destroyed");
// })
// req.on("end", () => {
// console.log("ending stream");
// s3ReadStream.destroy();
// console.log("ended stream")
// })
// req.on("error", () => {
// console.log("req error");
// })
// req.on("pause", () => {
// console.log("req pause")
// })
// req.on("close", () => {
// // console.log("req closed");
// s3ReadStream.destroy();
// })
//req.on("")
// req.on("end", () => {
// console.log("req end");
// })
await awaitStreamVideo(start, end, differenceStart, decipher, res, req, tempUUID, allStreamsToErrorCatch);
console.log("Video stream finished");
s3ReadStream.destroy();
}
@@ -239,24 +369,26 @@ class S3Service implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const thumbnail = await Thumbnail.findById(id) as ThumbnailInterface;
const thumbnail = await Thumbnail.findById(new ObjectID(id)) as ThumbnailInterface;
if (thumbnail.owner !== user._id.toString()) {
throw new NotAuthorizedError('Thumbnail Unauthorized Error');
throw new ForbiddenError('Thumbnail Unauthorized Error');
}
const iv = thumbnail.IV!;
const {s3Storage, bucket} = await this.getS3AuthThumbnail(thumbnail, user);
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
const decipher = crypto.createDecipheriv("aes256", CIPHER_KEY, iv);
const params: any = {Bucket: env.s3Bucket, Key: thumbnail.s3ID!};
const params: any = {Bucket: bucket, Key: thumbnail.s3ID!};
const readStream = s3.getObject(params).createReadStream();
const readStream = s3Storage.getObject(params).createReadStream();
const allStreamsToErrorCatch = [readStream, decipher];
@@ -276,11 +408,13 @@ class S3Service implements ChunkInterface {
const password = user.getEncryptionKey();
const IV = file.metadata.IV.buffer as Buffer;
if (!password) throw new NotAuthorizedError("Invalid Encryption Key")
if (!password) throw new ForbiddenError("Invalid Encryption Key")
const params: any = {Bucket: env.s3Bucket, Key: file.metadata.s3ID!};
const {s3Storage, bucket} = await this.getS3Auth(file, user);
const readStream = s3.getObject(params).createReadStream();
const params: any = {Bucket: bucket, Key: file.metadata.s3ID!};
const readStream = s3Storage.getObject(params).createReadStream();
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
@@ -292,9 +426,7 @@ class S3Service implements ChunkInterface {
const allStreamsToErrorCatch = [readStream, decipher];
console.log("Sending Full Thumbnail...")
await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch);
console.log("Full thumbnail sent");
}
getPublicDownload = async(fileID: string, tempToken: any, res: Response) => {
@@ -309,13 +441,15 @@ class S3Service implements ChunkInterface {
const password = user.getEncryptionKey();
if (!password) throw new NotAuthorizedError("Invalid Encryption Key");
if (!password) throw new ForbiddenError("Invalid Encryption Key");
const IV = file.metadata.IV.buffer as Buffer;
const params: any = {Bucket: env.s3Bucket, Key: file.metadata.s3ID!};
const readStream = s3.getObject(params).createReadStream();
const {s3Storage, bucket} = await this.getS3Auth(file, user);
const params: any = {Bucket: bucket, Key: file.metadata.s3ID!};
const readStream = s3Storage.getObject(params).createReadStream();
const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()
@@ -330,7 +464,6 @@ class S3Service implements ChunkInterface {
await awaitStream(readStream.pipe(decipher), res, allStreamsToErrorCatch);
if (file.metadata.linkType === "one") {
console.log("removing public link");
await dbUtilsFile.removeOneTimePublicLink(fileID);
}
}
@@ -340,18 +473,23 @@ class S3Service implements ChunkInterface {
const file: FileInterface = await dbUtilsFile.getFileInfo(fileID, userID);
if (!file) throw new NotFoundError("Delete File Not Found Error");
const user = await User.findById(userID) as UserInterface;
const {s3Storage, bucket} = await this.getS3Auth(file, user);
if (file.metadata.thumbnailID) {
const thumbnail = await Thumbnail.findById(file.metadata.thumbnailID) as ThumbnailInterface;
const paramsThumbnail: any = {Bucket: env.s3Bucket, Key: thumbnail.s3ID!};
await removeChunksS3(paramsThumbnail);
const paramsThumbnail: any = {Bucket: bucket, Key: thumbnail.s3ID!};
await removeChunksS3(s3Storage, paramsThumbnail);
await Thumbnail.deleteOne({_id: file.metadata.thumbnailID});
}
const params: any = {Bucket: env.s3Bucket, Key: file.metadata.s3ID!};
await removeChunksS3(params);
const params: any = {Bucket: bucket, Key: file.metadata.s3ID!};
await removeChunksS3(s3Storage, params);
await File.deleteOne({_id: file._id});
await subtractFromStorageSize(userID, file.length, file.metadata.personalFile!);
}
deleteFolder = async(userID: string, folderID: string, parentList: string[]) => {
@@ -364,23 +502,27 @@ class S3Service implements ChunkInterface {
const fileList = await dbUtilsFile.getFileListByParent(userID, parentListString);
if (!fileList) throw new NotFoundError("Delete File List Not Found");
const user = await User.findById(userID) as UserInterface;
for (let i = 0; i < fileList.length; i++) {
const currentFile = fileList[i];
const {s3Storage, bucket} = await this.getS3Auth(currentFile, user);
try {
if (currentFile.metadata.thumbnailID) {
const thumbnail = await Thumbnail.findById(currentFile.metadata.thumbnailID) as ThumbnailInterface;
const paramsThumbnail: any = {Bucket: env.s3Bucket, Key: thumbnail.s3ID!};
await removeChunksS3(paramsThumbnail);
const paramsThumbnail: any = {Bucket: bucket, Key: thumbnail.s3ID!};
await removeChunksS3(s3Storage, paramsThumbnail);
await Thumbnail.deleteOne({_id: currentFile.metadata.thumbnailID});
}
const params: any = {Bucket: env.s3Bucket, Key: currentFile.metadata.s3ID!};
await removeChunksS3(params);
const params: any = {Bucket: bucket, Key: currentFile.metadata.s3ID!};
await removeChunksS3(s3Storage, params);
await File.deleteOne({_id: currentFile._id});
} catch (e) {
@@ -393,29 +535,31 @@ class S3Service implements ChunkInterface {
deleteAll = async(userID: string) => {
console.log("remove all request")
await Folder.deleteMany({"owner": userID});
const fileList = await dbUtilsFile.getFileListByOwner(userID);
if (!fileList) throw new NotFoundError("Delete All File List Not Found Error");
const user = await User.findById(userID) as UserInterface;
for (let i = 0; i < fileList.length; i++) {
const currentFile = fileList[i];
const {s3Storage, bucket} = await this.getS3Auth(currentFile, user);
try {
if (currentFile.metadata.thumbnailID) {
const thumbnail = await Thumbnail.findById(currentFile.metadata.thumbnailID) as ThumbnailInterface;
const paramsThumbnail: any = {Bucket: env.s3Bucket, Key: thumbnail.s3ID!};
await removeChunksS3(paramsThumbnail);
const paramsThumbnail: any = {Bucket: bucket, Key: thumbnail.s3ID!};
await removeChunksS3(s3Storage, paramsThumbnail);
await Thumbnail.deleteOne({_id: currentFile.metadata.thumbnailID});
}
const params: any = {Bucket: env.s3Bucket, Key: currentFile.metadata.s3ID!};
await removeChunksS3(params);
const params: any = {Bucket: bucket, Key: currentFile.metadata.s3ID!};
await removeChunksS3(s3Storage, params);
await File.deleteOne({_id: currentFile._id});
} catch (e) {
@@ -9,6 +9,7 @@ interface ChunkInterface {
getFullThumbnail: (user: UserInterface, fileID: string, res: Response) => void;
getPublicDownload: (fileID: string, tempToken: any, res: Response) => void;
streamVideo: (user: UserInterface, fileID: string, headers: any, res: Response, req: Request) => void;
getFileReadStream: (user: UserInterface, fileID: string) => any
}
export default ChunkInterface;
@@ -0,0 +1,20 @@
import {UserInterface} from "../../../models/user";
import env from "../../../enviroment/env";
const addToStoageSize = async(user: UserInterface, size: number, isPersonalFile: boolean) => {
if (isPersonalFile) {
//console.log("user storage")
user.storageDataPersonal!.storageSize = +user.storageDataPersonal!.storageSize! + +size;
await user.save()
return;
}
if (!user.storageData || (!user.storageData.storageSize && !user.storageData.storageLimit)) user.storageData = {storageSize: 0, storageLimit: 0};
user.storageData!.storageSize = +user.storageData!.storageSize! + +size;
await user.save();
}
export default addToStoageSize;
@@ -15,7 +15,6 @@ const awaitStream = <T>(inputSteam: any, outputStream: any, allStreamsToErrorCat
})
inputSteam.pipe(outputStream).on("finish", (data: T) => {
console.log("await stream finished")
resolve(data);
})
})
@@ -1,107 +1,21 @@
import {Response, Request} from "express"
import tempStorage from "../../../tempStorage/tempStorage";
import uuid from "uuid";
const StreamSkip = require("stream-skip");
const awaitStreamVideo = (start: number, end:number, differenceStart: number,
decipher: any, res: Response, req: Request, tempUUID: string, streamsToErrorCatch: any[]) => {
const currentUUID = uuid.v4();
tempStorage[tempUUID] = currentUUID;
decipher: any, res: Response, req: Request, streamsToErrorCatch: any[], readStream: any) => {
return new Promise((resolve, reject) => {
let firstBytesRemoved = false;
//let sizeCounter = 0;
req.on("close", () => {
//console.log("close resolved");
streamsToErrorCatch.forEach((stream) => {
stream.destroy();
})
// readStream.close();
decipher.destroy();
resolve();
})
req.on("end", () => {
//console.log("End resolved");
resolve();
})
decipher.on("data", (data: Buffer | string) => {
//console.log("check", tempStorage[tempUUID]);
//console.log("check b", tempStorage[tempUUID] !== currentUUID && tempStorage[tempUUID] !== undefined);
//console.log(tempStorage[tempUUID] !== currentUUID, tempStorage[tempUUID])
if (tempStorage[tempUUID] !== currentUUID) {
//console.log("New Stream Requested, Desroying old stream");
streamsToErrorCatch.forEach((stream) => {
stream.destroy();
})
//console.log("Old Stream Desroyed");
//console.log("Old Stream Resolved");
resolve();
//delete tempStorage[tempUUID];
}
//console.log("data", currentUUID);
//console.log("stream passed", currentUUID);
// if (tempStorage[tempUUID] !== undefined && tempStorage[tempUUID] !== currentUUID) {
// console.log("New Stream Requested, Desroying old stream");
// streamsToErrorCatch[0].destroy();
// console.log("Old Stream Desroyed");
// delete tempStorage[tempUUID];
// } else {
// tempStorage[tempUUID] = currentUUID;
// }
if (+start === 0 && +end === 1) {
const dataCoverted = data.toString("hex");
let neededData = dataCoverted.substring(0, 4);
const dataBack = Buffer.from(neededData, "hex");
res.write(dataBack);
//res.flush();
return;
}
if (!firstBytesRemoved) {
const dataCoverted = data.toString("hex");
let neededData = dataCoverted.substring(differenceStart * 2);
const dataBack = Buffer.from(neededData, "hex");
firstBytesRemoved = true;
//sizeCounter += dataBack.length;
res.write(dataBack);
//res.flush();
return;
}
res.write(data);
//res.flush();
//sizeCounter += data.length;
})
decipher.on("end", () => {
//console.log("decipher resolved");
res.end();
streamsToErrorCatch.forEach((stream) => {
stream.destroy();
})
resolve();
})
@@ -118,6 +32,46 @@ const awaitStreamVideo = (start: number, end:number, differenceStart: number,
})
})
readStream.on("close", () => {
decipher.destroy();
})
if (+start === 0 && +end === 1) {
// This is for Safari/iOS, for whatever reason they ask for the
// First byte, but if I actually try to return the first byte
// It will not work ???, but if I return the first 4 bytes it seems
// To work fine.
decipher.on("data", (data: Buffer | string) => {
const dataCoverted = data.toString("hex");
let neededData = dataCoverted.substring(0, 8);
const dataBack = Buffer.from(neededData, "hex");
res.write(dataBack);
resolve();
})
} else {
// This is where the differnce start comes into play, as I said before
// There will be an offset caused by the 16 block size of AES256.
// So if there was an offset we need to skip over those bytes
// To make sure it returns the exact position the browser is requesting.
// Most browsers will still work fine without this, such as Chrome and Firefox.
// But one browser needs to be pampered and fed the exact right bytes in
// Order to function correctly, can you guess what browser? If you
// Guessed Safari you would be correct.
const streamSkip = new StreamSkip({skip: differenceStart});
decipher.pipe(streamSkip).pipe(res);
}
})
}
@@ -0,0 +1,66 @@
import request from "request";
import axios from "axios";
import { Response, Request } from "express";
import { UserInterface } from "../../../models/user";
import convertDriveToMongo from "../../../utils/convertDriveToMongo";
interface RequestType extends Request {
user?: UserInterface,
auth?: boolean,
busboy: any,
}
const awaitUploadGoogle = (file: any, size: number, axiosBody: any, axiosConfigObj: any, drive: any, req: RequestType, res: Response, allStreamsToErrorCatch: any[]) => {
return new Promise((resolve, reject) => {
allStreamsToErrorCatch.forEach((currentStream: any) => {
currentStream.on("error", (err: Error) => {
console.log("req errror", err)
reject({
message: "Await Stream Input Error",
code: 500,
error: err
})
})
})
axios.post("https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable", axiosBody, axiosConfigObj).then((result) => {
const URI: any = result.headers.location
request.post({
url: URI,
headers: {
"Content-Length": size,
},
body: file
}, async function (e, results, b) {
if (e) {
console.log(e)
return;
}
const jsonResults = JSON.parse(results.body)
const retrievedFile = await drive.files.get({
fileId: jsonResults.id,
fields: "*"
})
const reqAny: any = req;
const uploadedFile = convertDriveToMongo(retrievedFile.data, reqAny.user._id);
res.send(uploadedFile)
resolve()
})
}).catch((err) => {
console.log("axios err", err)
})
})
}
export default awaitUploadGoogle;
@@ -21,8 +21,6 @@ const awaitUploadStream = <T>(inputSteam: any, outputStream: any, req: Request,
req.on("aborted", () => {
console.log("Upload Request Cancelling...");
removeChunks(outputStream);
})
@@ -21,8 +21,6 @@ const awaitUploadStream = <T>(inputSteam: any, outputStream: any, req: Request,
req.on("aborted", () => {
console.log("Upload Request Cancelling...");
removeChunksFS(path);
})
@@ -1,18 +1,36 @@
import s3 from "../../../db/s3";
import s3Personal from "../../../db/S3Personal";
const awaitUploadStreamS3 = (params: any) => {
const awaitUploadStreamS3 = (params: any, personalFile: boolean, s3Data: {id: string, key: string, bucket: string}) => {
return new Promise((resolve, reject) => {
s3.upload(params, (err: any, data: any) => {
if (personalFile) {
if (err) {
reject("Amazon upload error");
}
const s3PersonalAuth = s3Personal(s3Data.id, s3Data.key);
resolve();
})
s3PersonalAuth.upload(params, (err: any, data: any) => {
if (err) {
console.log("Amazon upload personal err", err)
reject("Amazon upload error");
}
resolve();
})
} else {
s3.upload(params, (err: any, data: any) => {
if (err) {
console.log("Amazon upload err", err)
reject("Amazon upload error");
}
resolve();
})
}
})
}
@@ -0,0 +1,57 @@
// class CachedSubsriptions {
// cachedSubscriptionStatuses: any;
// constructor() {
// this.cachedSubscriptionStatuses = {}
// }
// addToCachedSubscriptionStatus = (userID: string) => {
// this.cachedSubscriptionStatuses[userID] = true;
// console.log("new cached", this.cachedSubscriptionStatuses)
// }
// checkCachedSubscriptionStatus = (userID: string) => {
// console.log("cache check", this.cachedSubscriptionStatuses)
// return this.cachedSubscriptionStatuses[userID];
// }
// }
// export default CachedSubsriptions;
// const cachedSubscriptionStatuses: any = {};
// export default cachedSubscriptionStatuses;
// import redis from "redis";
// const client = redis.createClient();
// export const setCachedValue = (userID: string) => {
// const date = new Date();
// return new Promise((resolve, reject) => {
// client.set(userID, date.getTime().toString(), (err, res) => {
// if (err) {
// console.log("Redis key err", err);
// reject()
// }
// resolve();
// });
// })
// }
// export const checkCachedValue = (userID: string) => {
// return new Promise((resolve, reject) => {
// client.get(userID, (err) => {
// if (err) {
// console.log("Redis key err", err);
// reject()
// } else {
// resolve();
// }
// })
// })
// }
@@ -0,0 +1,27 @@
import s3Auth from "../../../db/S3Personal";
const calculateS3Size = async(id:string, key:string, bucket:string) => {
const s3Storage = s3Auth(id, key);
const params = {
Bucket: bucket
}
const objectList = await s3Storage.listObjects(params).promise();
if (!objectList.Contents) return 0;
let size = 0
for (let i = 0; i < objectList.Contents!.length; i++) {
const currentObject = objectList.Contents[i];
if (!currentObject.Size) continue;
size += +currentObject.Size!;
}
return size;
}
export default calculateS3Size;
@@ -0,0 +1,25 @@
import {FileInterface} from "../../../models/file";
import {UserInterface} from "../../../models/user";
import createThumbnailS3 from "./createThumbnailS3";
import createThumbnailMongo from "./createThumbnail";
import createThumbnailFilesystem from "./createThumbnailFS";
import env from "../../../enviroment/env";
const createThumnailAny = async(currentFile: FileInterface, filename: string, user: UserInterface) => {
if (currentFile.metadata.personalFile || env.dbType === "s3") {
return await createThumbnailS3(currentFile, filename, user);
} else if (env.dbType === "mongo") {
return await createThumbnailMongo(currentFile, filename, user);
} else {
return await createThumbnailFilesystem(currentFile, filename, user);
}
}
export default createThumnailAny;
@@ -50,7 +50,7 @@ const createThumbnail = (file: FileInterface, filename: string, user: UserInterf
await thumbnailModel.save();
const getUpdatedFile = await conn.db.collection("fs.files")
.findOneAndUpdate({"_id": file._id}, {"$set": {"metadata.hasThumbnail": true, "metadata.thumbnailID": thumbnailModel._id}})
.findOneAndUpdate({"_id": new ObjectID(file._id)}, {"$set": {"metadata.hasThumbnail": true, "metadata.thumbnailID": thumbnailModel._id}})
let updatedFile: FileInterface = getUpdatedFile.value;
@@ -7,22 +7,40 @@ import { UserInterface } from "../../../models/user";
import uuid from "uuid";
import env from "../../../enviroment/env";
import s3 from "../../../db/s3";
import s3Auth from "../../../db/S3Personal"
const conn = mongoose.connection;
const getS3Auth = async (file: FileInterface, user: UserInterface) => {
if (file.metadata.personalFile) {
const s3Data = await user.decryptS3Data();
//console.log("s3 data", s3Data)
return {s3Storage: s3Auth(s3Data.id, s3Data.key), bucket: s3Data.bucket};
} else {
return {s3Storage: s3, bucket: env.s3Bucket};
}
}
const createThumbnailS3 = (file: FileInterface, filename: string, user: UserInterface) => {
return new Promise<FileInterface>((resolve, reject) => {
return new Promise<FileInterface>( async(resolve, reject) => {
const password = user.getEncryptionKey();
let CIPHER_KEY = crypto.createHash('sha256').update(password!).digest()
const thumbnailFilename = uuid.v4();
const params: any = {Bucket: env.s3Bucket, Key: file.metadata.s3ID!};
const readStream = s3.getObject(params).createReadStream();
const {s3Storage, bucket} = await getS3Auth(file, user);
const isPersonalFile = file.metadata.personalFile;
const params: any = {Bucket: bucket, Key: file.metadata.s3ID!};
const readStream = s3Storage.getObject(params).createReadStream();
const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, file.metadata.IV);
@@ -48,19 +66,19 @@ const createThumbnailS3 = (file: FileInterface, filename: string, user: UserInte
})
const paramsWrite: any = {
Bucket: env.s3Bucket,
Bucket: bucket,
Body : readStream.pipe(decipher).pipe(imageResize).pipe(thumbnailCipher),
Key : thumbnailFilename
};
s3.upload(paramsWrite, async(err: any, data: any) => {
s3Storage.upload(paramsWrite, async(err: any, data: any) => {
if (err) {
console.log("Amazon Upload Error", err);
reject("Amazon upload error");
}
const thumbnailModel = new Thumbnail({name: thumbnailFilename, owner: user._id, IV: thumbnailIV, s3ID: thumbnailFilename});
const thumbnailModel = new Thumbnail({name: thumbnailFilename, owner: user._id, IV: thumbnailIV, s3ID: thumbnailFilename, personalFile: isPersonalFile});
await thumbnailModel.save();
@@ -1,18 +1,38 @@
import s3 from "../../../db/s3";
import s3Auth from "../../../db/S3Personal";
import env from "../../../enviroment/env";
import {UserInterface} from "../../../models/user"
const getPrevIV = (start: number, key: string) => {
const getPrevIV = (start: number, key: string, isPersonal: boolean, user: UserInterface) => {
return new Promise<Buffer | string>((resolve, reject) => {
return new Promise<Buffer | string>(async(resolve, reject) => {
const params: any = {Bucket: env.s3Bucket, Key: key, Range: `bytes=${start}-${start + 15}`};
if (isPersonal) {
const stream = s3.getObject(params).createReadStream();
const s3Data = await user.decryptS3Data();
stream.on("data", (data) => {
const params: any = {Bucket: s3Data.bucket, Key: key, Range: `bytes=${start}-${start + 15}`};
resolve(data);
})
const s3Storage = s3Auth(s3Data.id, s3Data.key);
const stream = s3Storage.getObject(params).createReadStream();
stream.on("data", (data) => {
resolve(data);
})
} else {
const params: any = {Bucket: env.s3Bucket, Key: key, Range: `bytes=${start}-${start + 15}`};
const stream = s3.getObject(params).createReadStream();
stream.on("data", (data) => {
resolve(data);
})
}
})
}
@@ -16,7 +16,6 @@ const removeChunks = async(bucketStream: GridFSBucketWriteStream) => {
}
await dbUtilsFile.removeChunksByID(uploadID);
console.log("Upload Request Cancelled, Chunks Removed");
} catch(e) {
@@ -11,10 +11,10 @@ const removeChunksFS = (path: string) => {
resolve();
}
console.log("File Removed");
resolve();
})
})
}
export default removeChunksFS;
export default removeChunksFS;
module.exports = removeChunksFS;
@@ -1,21 +1,21 @@
import s3 from "../../../db/s3";
//import s3 from "../../../db/s3";
const removeChunksS3 = (parmas: any) => {
const removeChunksS3 = (s3: any, parmas: any) => {
return new Promise((resolve, reject) => {
s3.deleteObject(parmas, (err, data) => {
s3.deleteObject(parmas, (err: any, data: any) => {
if (err) {
console.log("Could not remove S3 file");
reject("Could Not Remove S3 File");
}
console.log("Delete S3 file");
resolve();
})
})
}
export default removeChunksS3;
export default removeChunksS3;
module.exports = removeChunksS3;
@@ -0,0 +1,24 @@
import User, {UserInterface} from "../../../models/user";
import env from "../../../enviroment/env";
const subtractFromStorageSize = async(userID: string, size: number, isPersonalFile: boolean) => {
const user = await User.findById(userID) as UserInterface;
if (isPersonalFile) {
user.storageDataPersonal!.storageSize = +user.storageDataPersonal!.storageSize! - +size;
if (user.storageDataPersonal!.storageSize < 0) user.storageDataPersonal!.storageSize = 0;
await user.save();
return
}
if (!user.storageData || (!user.storageData.storageSize && !user.storageData.storageLimit)) user.storageData = {storageSize: 0, storageLimit: 0}
user.storageData!.storageSize = +user.storageData!.storageSize! - +size;
if (user.storageData?.storageSize! < 0) user.storageData!.storageSize! = 0;
await user.save();
}
export default subtractFromStorageSize;
+34 -17
View File
@@ -10,10 +10,17 @@ import DbUtilFolder from "../../db/utils/folderUtils";
import { UserInterface } from "../../models/user";
import { FileInterface } from "../../models/file";
import tempStorage from "../../tempStorage/tempStorage";
import uuid from "uuid";
const dbUtilsFile = new DbUtilFile();
const dbUtilsFolder = new DbUtilFolder();
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
s3Enabled: boolean,
}
class MongoFileService {
@@ -38,10 +45,9 @@ class MongoFileService {
if (!file.lastErrorObject.updatedExisting) throw new NotFoundError("Remove Link File Not Found Error")
}
makePublic = async(user: UserInterface, fileID: string) => {
makePublic = async(userID: string, fileID: string) => {
const userID = user._id;
const token = await jwt.sign({_id: userID.toString()}, env.password!);
const token = jwt.sign({_id: userID.toString()}, env.passwordAccess!);
const file = await dbUtilsFile.makePublic(fileID, userID, token);
@@ -66,7 +72,7 @@ class MongoFileService {
makeOneTimePublic = async(userID: string, fileID: string) => {
const token = await jwt.sign({_id: userID.toString()}, env.password!);
const token = jwt.sign({_id: userID.toString()}, env.passwordAccess!);
const file = await dbUtilsFile.makeOneTimePublic(fileID, userID, token);
@@ -107,16 +113,21 @@ class MongoFileService {
return {...currentFile, parentName}
}
getQuickList = async(userID: string) => {
getQuickList = async(user: userAccessType | UserInterface) => {
const quickList = await dbUtilsFile.getQuickList(userID);
const userID = user._id;
const s3Enabled = user.s3Enabled ? true : false;
const quickList = await dbUtilsFile.getQuickList(userID, s3Enabled);
if (!quickList) throw new NotFoundError("Quick List Not Found Error");
return quickList;
}
getList = async(userID: string, query: any) => {
getList = async(user: userAccessType | UserInterface, query: any) => {
const userID = user._id;
let searchQuery = query.search || "";
const parent = query.parent || "/";
@@ -125,11 +136,15 @@ class MongoFileService {
const startAt = query.startAt || undefined
const startAtDate = query.startAtDate || "0"
const startAtName = query.startAtName || ""
const storageType = query.storageType || undefined;
const folderSearch = query.folder_search || undefined;
sortBy = sortBySwitch(sortBy)
limit = parseInt(limit)
const s3Enabled = user.s3Enabled ? true : false;
const queryObj = createQuery(userID, parent, query.sortby,startAt, startAtDate, searchQuery, startAtName)
const queryObj = createQuery(userID, parent, query.sortby,startAt, startAtDate, searchQuery, s3Enabled ,startAtName, storageType, folderSearch);
const fileList = await dbUtilsFile.getList(queryObj, sortBy, limit);
if (!fileList) throw new NotFoundError("File List Not Found");
@@ -146,22 +161,24 @@ class MongoFileService {
return tempToken;
}
getDownloadTokenVideo = async(user: UserInterface, cookie: string) => {
// No longer needed left for reference
if (!cookie) throw new NotAuthorizedError("Get Download Token Video Cookie Not Authorized Error");
// getDownloadTokenVideo = async(user: UserInterface, cookie: string) => {
const tempToken = await user.generateTempAuthTokenVideo(cookie);
// if (!cookie) throw new NotAuthorizedError("Get Download Token Video Cookie Not Authorized Error");
if (!tempToken) throw new NotAuthorizedError("Get Download Token Video Not Authorized Error");
// const tempToken = await user.generateTempAuthTokenVideo(cookie);
return tempToken;
}
// if (!tempToken) throw new NotAuthorizedError("Get Download Token Video Not Authorized Error");
// return tempToken;
// }
removeTempToken = async(user: UserInterface, tempToken: any, currentUUID: string) => {
const key = user.getEncryptionKey();
const decoded = await jwt.verify(tempToken, env.password!) as any;
const decoded = await jwt.verify(tempToken, env.passwordAccess!) as any;
const publicKey = decoded.iv;
+61 -4
View File
@@ -4,6 +4,14 @@ import NotFoundError from "../../utils/NotFoundError";
import UtilsFile from "../../db/utils/fileUtils";
import UtilsFolder from "../../db/utils/folderUtils";
import sortBySwitch from "../../utils/sortBySwitchFolder";
import { UserInterface } from "../../models/user";
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
s3Enabled: boolean,
}
const utilsFile = new UtilsFile();
const utilsFolder = new UtilsFolder();
@@ -97,16 +105,24 @@ class FolderService {
}
}
getFolderList = async(userID: string, query: any) => {
getFolderList = async(user: userAccessType | UserInterface, query: any) => {
const userID = user._id;
let searchQuery = query.search || "";
const parent = query.parent || "/";
let sortBy = query.sortby || "DEFAULT"
sortBy = sortBySwitch(sortBy)
const type = query.type;
const storageType = query.storageType || undefined;
const folderSearch = query.folder_search || undefined;
const itemType = query.itemType || undefined;
sortBy = sortBySwitch(sortBy);
const s3Enabled = user.s3Enabled ? true : false;
if (searchQuery.length === 0) {
const folderList = await utilsFolder.getFolderListByParent(userID, parent, sortBy);
const folderList = await utilsFolder.getFolderListByParent(userID, parent, sortBy, s3Enabled, type, storageType, itemType);
if (!folderList) throw new NotFoundError("Folder List Not Found Error");
@@ -115,7 +131,7 @@ class FolderService {
} else {
searchQuery = new RegExp(searchQuery, 'i')
const folderList = await utilsFolder.getFolderListBySearch(userID, searchQuery, sortBy);
const folderList = await utilsFolder.getFolderListBySearch(userID, searchQuery, sortBy, type, parent, storageType, folderSearch, itemType, s3Enabled);
if (!folderList) throw new NotFoundError("Folder List Not Found Error");
@@ -130,6 +146,47 @@ class FolderService {
if (!folder) throw new NotFoundError("Rename Folder Not Found");
}
getSubfolderFullList = async(user: userAccessType, id: string) => {
const userID = user._id;
const folder = await utilsFolder.getFolderInfo(id, userID)
const subFolders = await this.getFolderList(user, {parent: id}) //folderService.getFolderList(user._id, {parent: id})
let folderList: any[] = [];
const rootID = "/"
let currentID = folder.parent;
folderList.push({
_id: folder._id,
parent: folder._id,
name: folder.name,
subFolders: subFolders
})
while (true) {
if (rootID === currentID) break;
const currentFolder = await this.getFolderInfo(userID, currentID);
const currentSubFolders = await this.getFolderList(user, {parent: currentFolder._id});
folderList.splice(0, 0, {
_id: currentFolder._id,
parent: currentFolder._id,
name: currentFolder.name,
subFolders: currentSubFolders
})
currentID = currentFolder.parent;
}
return folderList;
}
moveFolder = async(userID: string, folderID: string, parentID: string) => {
let parentList = ["/"];
+364
View File
@@ -0,0 +1,364 @@
import { UserInterface } from "../../models/user";
import getGoogleAuth from "../../db/googleAuth";
import {google} from "googleapis";
import convertDriveListToMongoList from "../../utils/convertDriveListToMongoList";
import FileService from "../FileService";
import sortGoogleMongoList from "../../utils/sortGoogleMongoList";
import convertDriveToMongo from "../../utils/convertDriveToMongo";
import convertDriveFoldersToMongoFolders from "../../utils/convertDriveFoldersToMongoFolders";
import { Response, Request } from "express";
import tempStorage from "../../tempStorage/tempStorage";
import uuid from "uuid";
import getBusboyData from "../ChunkService/utils/getBusboyData";
import axios from "axios";
import awaitUploadGoogle from "../ChunkService/utils/awaitUploadGoogle";
import {googleQueryType} from "../../utils/createQueryGoogle";
import GoogleDbFileUtils from "../../db/utils/googleFileUtils";
import sortGoogleMongoQuickFiles from "../../utils/sortGoogleMongoQuickFiles";
const googleDbFileUtils = new GoogleDbFileUtils();
interface RequestType extends Request {
user?: UserInterface,
auth?: boolean,
busboy: any,
}
const fileService = new FileService();
const fields = 'id, name, size, modifiedTime, hasThumbnail, parents, mimeType, thumbnailLink, webViewLink, shared';
class GoogleFileService {
constructor() {
}
getList = async(user: UserInterface, query: googleQueryType) => {
const files = await googleDbFileUtils.getList(query, user);
const nextPageToken = files.data.nextPageToken;
const userID = user._id;
const convertedFiles = convertDriveListToMongoList(files.data.files, userID, nextPageToken);
return convertedFiles;
}
getMongoGoogleList = async(user: UserInterface, query: googleQueryType) => {
const files = await googleDbFileUtils.getList(query, user);
const nextPageToken = files.data.nextPageToken;
const userID = user._id;
const convertedFiles = convertDriveListToMongoList(files.data.files, userID, nextPageToken);
const fileList = await fileService.getList(user, query);
const sortedList = sortGoogleMongoList([...convertedFiles, ...fileList], query)
return sortedList;
}
getFileInfo = async(user: UserInterface, id: string) => {
const file = await googleDbFileUtils.getFileInfo(id, user);
const userID = user._id;
const convertedFile = convertDriveToMongo(file.data, userID);
return convertedFile;
}
getGoogleMongoQuickList = async(user: UserInterface) => {
const files = await googleDbFileUtils.getQuickList(user);
const userID = user._id
const convertedFiles = convertDriveListToMongoList(files.data.files, userID);
const quickList = await fileService.getQuickList(user);
const sortedGoogleMongoQuickList = sortGoogleMongoQuickFiles(convertedFiles, quickList);
return sortedGoogleMongoQuickList;
}
getGoogleMongoSuggestedList = async(user: UserInterface, searchQuery: string) => {
const {files, folders} = await googleDbFileUtils.getSuggestedList(searchQuery, user);
const userID = user._id;
const convertedFiles = convertDriveListToMongoList(files.data.files, userID);
const convertedFolders = convertDriveFoldersToMongoFolders(folders.data.files, userID);
const {fileList: mongoFileList, folderList: mongoFolderList} = await fileService.getSuggestedList(user._id, searchQuery);
return {
folderList: [...mongoFolderList, ...convertedFolders],
fileList: [...mongoFileList, ...convertedFiles]
}
}
renameFile = async(user: UserInterface, fileID: string, title: string) => {
await googleDbFileUtils.renameFile(fileID, title, user);
}
removeFile = async(user: UserInterface, fileID: string) => {
await googleDbFileUtils.removeFile(fileID, user);
}
downloadFile = async(user: UserInterface, fileID: string, res: Response) => {
const {fileMetadata, drive} = await googleDbFileUtils.getDownloadFileMetadata(fileID, user);
res.set('Content-Type', 'binary/octet-stream');
res.set('Content-Disposition', 'attachment; filename="' + fileMetadata.data.name! + '"');
res.set('Content-Length', fileMetadata.data.size!);
return new Promise((resolve, reject) => {
drive.files.get(
{fileId: fileID, alt: "media"},
{responseType: "stream"},
(err, resp) => {
if (err) {
console.log(err);
return;
}
resp?.data
.on("end", () => {resolve()})
.on("error", (err: any) => {
console.log(err);
reject();
})
.pipe(res);
})
})
}
downloadDoc = async(user: UserInterface, fileID: string, res: Response) => {
const {fileMetadata, drive} = await googleDbFileUtils.getDownloadFileMetadata(fileID, user);
res.set('Content-Type', 'binary/octet-stream');
res.set('Content-Disposition', 'attachment; filename="' + fileMetadata.data.name! + ".pdf" + '"');
res.set('Content-Length', fileMetadata.data.size!);
drive.files.export({
fileId: fileID,
mimeType: 'application/pdf'
}, {
responseType: "stream"
}, (err, resp) => {
if (err) {
console.log("export err", err);
res.end();
return;
}
resp?.data.pipe(res)
})
}
getThumbnail = async(user: UserInterface, fileID: string, res: Response) => {
const {fileMetadata, drive} = await googleDbFileUtils.getDownloadFileMetadata(fileID, user);
res.set('Content-Type', 'binary/octet-stream');
res.set('Content-Disposition', 'attachment; filename="' + fileMetadata.data.name! + '"');
res.set('Content-Length', fileMetadata.data.size!);
return new Promise((resolve, reject) => {
drive.files.get(
{fileId: fileID, alt: "media"},
{responseType: "stream"},
(err, resp) => {
if (err) {
console.log(err);
return;
}
resp?.data
.on("end", () => {resolve()})
.on("error", (err: any) => {
console.log(err);
reject();
})
.pipe(res);
})
})
}
getFullThumbnail = async(user: UserInterface, fileID: string, res: Response) => {
const {fileMetadata, drive} = await googleDbFileUtils.getDownloadFileMetadata(fileID, user);
res.set('Content-Type', 'binary/octet-stream');
res.set('Content-Disposition', 'attachment; filename="' + fileMetadata.data.name! + '"');
res.set('Content-Length', fileMetadata.data.size!);
return new Promise((resolve, reject) => {
drive.files.get(
{fileId: fileID, alt: "media"},
{responseType: "stream"},
(err, resp) => {
if (err) {
console.log(err);
reject()
return;
}
resp?.data
.on("end", () => {resolve()})
.on("error", (err: any) => {
console.log(err);
reject();
})
.pipe(res);
})
})
}
streamVideo = async(user: UserInterface, fileID: string, tempUUID: string, req: RequestType, res: Response) => {
const currentUUID = uuid.v4();
tempStorage[tempUUID] = currentUUID;
const {fileMetadata, drive} = await googleDbFileUtils.getDownloadFileMetadata(fileID, user);
const fileSize = +fileMetadata.data.size!
const headers = req.headers;
const range = headers.range!
const parts = range.replace(/bytes=/, "").split("-")
let start = parseInt(parts[0], 10)
let end = parts[1]
? parseInt(parts[1], 10)
: fileSize-1
const chunksize = (end-start)+1
let head = {
'Content-Range': 'bytes ' + start + '-' + end + '/' + fileSize,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4'}
res.writeHead(206, head);
return new Promise((resolve, reject) => {
drive.files.get(
{fileId: fileID, alt: "media"},
{responseType: "stream", headers: {
Range: `bytes=${start}-${end}`
}},
(err, resp) => {
if (err) {
console.log(err);
return;
}
resp?.data
.on("end", () => {resolve()})
.on("error", (err: any) => {
console.log(err);
reject();
})
.on("data", () => {
if (tempStorage[tempUUID] !== currentUUID) {
resp?.data.destroy();
resolve();
}
})
.pipe(res);
})
})
}
uploadFile = async(user: UserInterface, busboy: any, req: RequestType, res: Response) => {
const streamsToErrorCatch = [req, busboy];
const {file, filename, formData} = await getBusboyData(busboy);
let parent = formData.get("parent") || "/"
const size = formData.get("size") || ""
parent = parent === "/" ? "root" : parent;
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
type fileMetadataType = {
name?: string,
parents?: string[]
}
let fileMetadata: fileMetadataType = {}
fileMetadata.name = filename;
if (parent !== "/") {
fileMetadata.parents = [parent]
}
const googleIDandKey = await user.decryptDriveIDandKey();
const clientID = googleIDandKey.clientID;
const clientKey = googleIDandKey.clientKey;
const googleToken = await user.decryptDriveTokenData();
const refreshToken = googleToken.refresh_token;
const data = {
client_id: clientID,
client_secret: clientKey,
refresh_token: refreshToken,
grant_type: 'refresh_token'
}
const tokenResp = await axios.post("https://www.googleapis.com/oauth2/v4/token", data);
const accessToken = tokenResp.data.access_token
const config = {
'Authorization': `Bearer ${accessToken}`,
'Content-Length': size,
'Content-Type': 'application/json'
}
const axiosConfigObj = {
headers: config,
}
const axiosBody = {
name: filename,
parents: [parent],
fields: "*"
}
await awaitUploadGoogle(file, size, axiosBody, axiosConfigObj, drive, req, res, streamsToErrorCatch);
}
moveFile = async(user: UserInterface, fileID: string, parentID: string) => {
await googleDbFileUtils.moveFile(fileID, parentID, user);
}
makeFilePublic = async(user: UserInterface, fileID: string) => {
const publicURL = await googleDbFileUtils.makeFilePublic(fileID, user);
return publicURL;
}
removePublicLink = async(user: UserInterface, fileID: string) => {
await googleDbFileUtils.removePublicLink(fileID, user);
}
}
export default GoogleFileService;
@@ -0,0 +1,186 @@
import { UserInterface } from "../../models/user";
import getGoogleAuth from "../../db/googleAuth";
import {google} from "googleapis";
import convertDriveFoldersToMongoFolders from "../../utils/convertDriveFoldersToMongoFolders";
import FolderService from "../FolderService";
import sortGoogleMongoFolderList from "../../utils/sortGoogleMongoFolderList";
import convertDriveFolderToMongoFolder from "../../utils/convertDriveFolderToMongoFolder";
import {googleQueryType} from "../../utils/createQueryGoogle";
import GoogleDbFolderUtils from "../../db/utils/googleFolderUtils";
const googleDbFolderUtils = new GoogleDbFolderUtils();
const folderService = new FolderService();
const fields = 'id, name, createdTime, parents, mimeType'
class GoogleFolderService {
constructor() {
}
getList = async(user: UserInterface, query: googleQueryType) => {
const folders = await googleDbFolderUtils.getList(query, user);
const userID = user._id;
const convertedFolders = convertDriveFoldersToMongoFolders(folders.data.files, userID);
return convertedFolders;
}
getGoogleMongoList = async(user: UserInterface, query: any) => {
const googleFolderList = await googleDbFolderUtils.getList(query, user);
const userID = user._id;
const convertedFolders = convertDriveFoldersToMongoFolders(googleFolderList.data.files, userID);
const folderList = await folderService.getFolderList(user, query);
const mongoGoogleList = sortGoogleMongoFolderList([...convertedFolders, ...folderList], query);
return mongoGoogleList;
}
getInfo = async(user: UserInterface, id: string) => {
const folder = await googleDbFolderUtils.getInfo(id, user);
const userID = user._id;
const convertedFolder = convertDriveFolderToMongoFolder(folder.data, userID);
return convertedFolder;
}
getSubFolderList = async(user: UserInterface, id: string) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const file = await drive.files.get({fileId: id, fields: fields});
let folderIDList = []
let folderNameList = []
folderIDList.push("/");
folderNameList.push("Home")
const rootGet = await drive.files.get({fileId: "root"});
const rootID = rootGet.data.id!;
let currentID = file.data.parents![0]
while (true) {
if (rootID === currentID) break;
const currentFile = await drive.files.get({fileId: currentID, fields: fields});
folderIDList.splice(1, 0, currentFile.data.id!)
folderNameList.splice(1,0, currentFile.data.name!)
currentID = currentFile.data.parents![0];
}
folderIDList.push(id);
folderNameList.push(file.data.name)
return {
folderIDList,
folderNameList
}
}
getSubFolderFullList = async(user: UserInterface, id: string) => {
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const file = await drive.files.get({fileId: id, fields: fields});
const parent = file.data.parents![0];
let queryBuilder = `mimeType = "application/vnd.google-apps.folder"`
queryBuilder += ` and "${file.data.id!}" in parents`
const subFolders = await drive.files.list({fields: `files(${fields})`, q: queryBuilder})
const convertedFile = convertDriveFolderToMongoFolder(file.data, user._id);
const convertedSubFolders = convertDriveFoldersToMongoFolders(subFolders.data.files, user._id)
let folderList: any[] = [];
const rootGet = await drive.files.get({fileId: 'root', fields: fields});
const rootID = rootGet.data.id!;
let currentID = file.data.parents![0]
folderList.push({
_id: convertedFile._id,
parent: convertedFile._id,
name: convertedFile.name,
subFolders: convertedSubFolders
})
while (true) {
if (rootID === currentID) break;
const currentFile = await drive.files.get({fileId: currentID, fields: fields});
const currentConvertedFile = convertDriveFolderToMongoFolder(currentFile.data, user._id)
queryBuilder = `mimeType = "application/vnd.google-apps.folder"`
queryBuilder += ` and "${currentConvertedFile._id}" in parents`
const currentSubFolders = await drive.files.list({fields: `files(${fields})`, q: queryBuilder})
const currentConvertedSubFolders = convertDriveFoldersToMongoFolders(currentSubFolders.data.files, user._id)
folderList.splice(0, 0, {
_id: currentConvertedFile._id,
parent: currentConvertedFile._id,
name: currentConvertedFile.name,
subFolders: currentConvertedSubFolders
})
currentID = currentFile.data.parents![0];
}
return folderList;
}
renameFolder = async(user: UserInterface, folderID: string, title: string) => {
await googleDbFolderUtils.renameFolder(folderID, title, user);
}
removeFolder = async(user: UserInterface, folderID: string) => {
await googleDbFolderUtils.removeFolder(folderID, user);
}
upload = async(user: UserInterface, name: string, parent: string) => {
const createdFolder = await googleDbFolderUtils.uploadFolder(name, parent, user);
const userID = user._id;
const convertedFolder = convertDriveFolderToMongoFolder(createdFolder.data, userID)
return convertedFolder;
}
moveFolder = async(user: UserInterface, fileID: string, parentID: string) => {
await googleDbFolderUtils.moveFolder(fileID, parentID, user);
}
}
export default GoogleFolderService;
+76
View File
@@ -0,0 +1,76 @@
import {UserInterface} from "../../models/user";
import env from "../../enviroment/env";
import { google } from "googleapis";
class UserGoogleService {
constructor() {
}
createGoogleStorageURL = async(user: UserInterface, googleData: any) => {
const {clientID, clientKey, clientRedirect} = googleData;
const oauth2Client = new google.auth.OAuth2(
clientID, clientKey, env.remoteURL + "/add-google-account"
);
const url = oauth2Client.generateAuthUrl({
access_type: 'offline',
prompt: "consent",
scope: ["https://www.googleapis.com/auth/drive"],
});
await user.encryptDriveIDandKey(clientID, clientKey);
user.decryptDriveIDandKey();
return url;
}
addGoogleStorage = async(user: UserInterface, code: string, uuid: string | undefined) => {
const redirectURL = env.remoteURL + "/add-google-account";
const decrypyedIdandKey = await user.decryptDriveIDandKey();
const clientID = decrypyedIdandKey.clientID;
const clientKey = decrypyedIdandKey.clientKey
const oauth2Client = new google.auth.OAuth2(
clientID,
clientKey,
redirectURL,
);
return new Promise<{accessToken: string, refreshToken: string}>((resolve, reject) => {
oauth2Client.getToken(code, async(err, tokens) => {
if (!err) {
const token: any = tokens;
user.encryptDriveTokenData(token);
const {accessToken, refreshToken} = await user.generateAuthToken(uuid);
resolve({accessToken, refreshToken});
} else {
reject("Get Google Token Error")
}
})
})
}
removeGoogleStorage = async(user: UserInterface, uuid: string | undefined) => {
user.googleDriveEnabled = undefined;
user.googleDriveData = undefined;
await user.save();
return await user.generateAuthToken(uuid);
}
}
export default UserGoogleService;
@@ -0,0 +1,153 @@
import {UserInterface} from "../../models/user";
import File, { FileInterface } from "../../models/file";
import Folder, { FolderInterface } from "../../models/folder";
import Thumbnail, { ThumbnailInterface } from "../../models/thumbnail";
import { ObjectID } from "mongodb";;
import calculateS3Size from "../ChunkService/utils/calculateS3Size";
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
s3Enabled: boolean,
}
class UserPeronsalService {
constructor() {
}
addS3Storage = async(user: UserInterface, s3Data: any, uuid: string | undefined) => {
const {id, key, bucket} = s3Data;
user.storageDataPersonal!.storageSize = await calculateS3Size(id, key, bucket);
user.personalStorageCanceledDate = undefined;
await user.encryptS3Data(id, key, bucket);
await user.save();
return await user.generateAuthToken(uuid);
}
removeS3Storage = async(user: UserInterface, uuid: string | undefined) => {
const date = new Date();
user.s3Enabled = undefined;
user.s3Data = undefined;
user.storageDataPersonal = undefined;
user.personalStorageCanceledDate = date.getTime();
await user.save();
return await user.generateAuthToken(uuid);
}
downloadPersonalFileList = async(user: userAccessType) => {
const fileList = await File.find({"metadata.owner": new ObjectID(user._id), "metadata.personalFile": true});
const folderList = await Folder.find({"owner": user._id.toString(), 'personalFolder': true});
const thumbnailList: ThumbnailInterface[] = [];
for (const currentFile of fileList) {
if (currentFile.metadata.hasThumbnail) {
const currentThumbnail = await Thumbnail.findById(new ObjectID(currentFile.metadata.thumbnailID)) as ThumbnailInterface;
thumbnailList.push(currentThumbnail);
}
}
const combined = {fileList, folderList, thumbnailList}
const data = JSON.stringify(combined);
return data;
}
uploadPersonalFileList = async(user: userAccessType, data: any) => {
const personalFileList: FileInterface[] = data.fileList;
const personalFolderList: FolderInterface[] = data.folderList;
const personalThumbnailList: ThumbnailInterface[] = data.thumbnailList;
const fixedFileList: FileInterface[] = []
for (let currentObj of personalFileList) {
await File.deleteMany({_id: new ObjectID(currentObj._id), 'metadata.owner': new ObjectID(user._id)});
currentObj.metadata.owner = new ObjectID(user._id);
currentObj._id = new ObjectID(currentObj._id)
const oldIV: any = currentObj.metadata.IV;
const IV: any = Buffer.from(oldIV, 'base64');
currentObj.metadata.IV = IV;
const newDate: any = new Date(currentObj.uploadDate);
currentObj.uploadDate = newDate;
currentObj.metadata.parent = currentObj.metadata.parent.toString();
fixedFileList.push(currentObj);
}
const fixedFolderList: FolderInterface[] = []
for (let currentObj of personalFolderList) {
await Folder.deleteMany({_id: new ObjectID(currentObj._id), owner: user._id.toString()});
currentObj._id = new ObjectID(currentObj._id)
currentObj.owner = user._id.toString();
currentObj.createdAt = new Date(currentObj.createdAt);
currentObj.updatedAt = new Date(currentObj.updatedAt);
fixedFolderList.push(currentObj);
}
const fixedThumbnailList: ThumbnailInterface[] = [];
for (let currentObj of personalThumbnailList) {
await Thumbnail.deleteMany({_id: new ObjectID(currentObj._id), owner: user._id.toString()});
currentObj._id = new ObjectID(currentObj._id);
currentObj.owner = user._id.toString();
currentObj.createdAt = new Date(currentObj.createdAt);
currentObj.updatedAt = new Date(currentObj.updatedAt);
const oldIV: any = currentObj.IV;
const IV: any = Buffer.from(oldIV, 'base64');
currentObj.IV = IV;
fixedThumbnailList.push(currentObj);
}
await File.insertMany(fixedFileList);
await Folder.insertMany(fixedFolderList);
await Thumbnail.insertMany(fixedThumbnailList);
}
removeS3Metadata = async(user: userAccessType) => {
const fileList = await File.find({"metadata.owner": new ObjectID(user._id),
"metadata.personalFile": true})
for (let currentFile of fileList) {
await File.deleteOne({_id: new ObjectID(currentFile._id)});
await File.deleteOne({_id: currentFile._id});
if (currentFile.metadata.hasThumbnail) {
await Thumbnail.deleteOne({_id: new ObjectID(currentFile.metadata.thumbnailID)})
await Thumbnail.deleteOne({_id: currentFile.metadata.thumbnailID})
}
}
await Folder.deleteMany({'owner': user._id.toString(), 'personalFolder': true});
}
}
export default UserPeronsalService;
+228 -24
View File
@@ -2,78 +2,124 @@ import User, {UserInterface} from "../../models/user";
import bcrypt from "bcrypt";
import NotFoundError from "../../utils/NotFoundError";
import InternalServerError from "../../utils/InternalServerError";
import NotAuthorizedError from "../../utils/NotAuthorizedError";
import sendEmailVerification from "../../utils/sendVerificationEmail";
import File from "../../models/file";
import env from "../../enviroment/env";
import getGoogleAuth from "../../db/googleAuth";
import { google } from "googleapis";
import jwt from "jsonwebtoken"
import sendVerificationEmail from "../../utils/sendVerificationEmail";
import sendPasswordResetEmail from "../../utils/sendPasswordResetEmail";
import ForbiddenError from "../../utils/ForbiddenError";
type UserDataType = {
email: string,
password: string,
}
type jwtType = {
iv: Buffer,
_id: string,
}
const uknownUserType = User as unknown;
const UserStaticType = uknownUserType as {
findByCreds: (email: string, password: string) => Promise<UserInterface>;
};
class UserService {
constructor() {
}
login = async(userData: UserDataType) => {
login = async(userData: UserDataType, uuid: string | undefined) => {
const email = userData.email;
const password = userData.password;
const user = await UserStaticType.findByCreds(email, password);
const token = await user.generateAuthToken();
if (!user) throw new NotFoundError("Cannot Find User");
if (!user || !token) throw new NotFoundError("Login User Not Found Error");
const {accessToken, refreshToken} = await user.generateAuthToken(uuid);
return {user, token}
if (!accessToken || !refreshToken) throw new NotFoundError("Login User Not Found Error");
return {user, accessToken, refreshToken}
}
logout = async(user: UserInterface, userToken: any) => {
logout = async(userID: string, refreshToken: string) => {
user.tokens = user.tokens.filter((token) => {
return token.token !== userToken;
})
const user = await User.findById(userID);
if (!user) throw new NotFoundError("Could Not Find User");
if (refreshToken) {
const decoded = jwt.verify(refreshToken, env.passwordRefresh!) as jwtType;
const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(refreshToken, encrpytionKey, decoded.iv);
for (let i = 0; i < user.tokens.length; i++) {
const currentEncryptedToken = user.tokens[i].token;
if (currentEncryptedToken === encryptedToken) {
user.tokens.splice(i, 1);
await user.save();
break;
}
}
}
await user.save();
}
logoutAll = async(user: UserInterface) => {
logoutAll = async(userID: string) => {
user.tokens = []
const user = await User.findById(userID);
if (!user) throw new NotFoundError("Could Not Find User");
user.tokens = [];
user.tempTokens = [];
await user.save();
}
create = async(userData: any) => {
create = async(userData: any, uuid: string | undefined) => {
console.log("Create");
const user = new User(userData);
const user = new User({email: userData.email, password: userData.password, emailVerified: env.disableEmailVerification});
await user.save();
if (!user) throw new NotFoundError("User Not Found");
await user.generateEncryptionKeys();
const token = await user.generateAuthToken();
const {accessToken, refreshToken} = await user.generateAuthToken(uuid);
const emailToken = await user.generateEmailVerifyToken();
if (!user || !token) throw new InternalServerError("Could Not Create New User Error");
if (!env.disableEmailVerification) await sendEmailVerification(user, emailToken);
return {user, token}
if (!accessToken || !refreshToken) throw new InternalServerError("Could Not Create New User Error");
return {user, accessToken, refreshToken}
}
changePassword = async(user: UserInterface, oldPassword: string, newPassword: string) => {
changePassword = async(userID: string, oldPassword: string, newPassword: string, oldRefreshToken: string, uuid: string | undefined) => {
const user = await User.findById(userID);
if (!user) throw new NotFoundError("Could Not Find User");
const date = new Date();
const isMatch = await bcrypt.compare(oldPassword, user.password);
if (!isMatch) throw new NotAuthorizedError("Change Passwords Do Not Match Error");
if (!isMatch) throw new ForbiddenError("Change Passwords Do Not Match Error");
const encryptionKey = user.getEncryptionKey();
@@ -81,15 +127,173 @@ class UserService {
user.tokens = [];
user.tempTokens = [];
user.passwordLastModified = date.getTime();
await user.save();
await user.changeEncryptionKey(encryptionKey!);
const newToken = await user.generateAuthToken();
const {accessToken, refreshToken} = await user.generateAuthToken(uuid);
return newToken;
return {accessToken, refreshToken};
}
refreshStorageSize = async(userID: string) => {
const user = await User.findById(userID);
if (!user) throw new NotFoundError("Cannot find user");
const fileList = await File.find({"metadata.owner": user._id, "metadata.personalFile": null});
let size = 0;
for (let currentFile of fileList) {
size += currentFile.length;
}
user.storageData = {storageSize: size, storageLimit: 0};
await user.save();
}
getUserDetailed = async(userID: string) => {
const user = await User.findById(userID);
if (!user) throw new NotFoundError("Cannot find user");
if (user.s3Enabled) {
try {
const {bucket} = await user.decryptS3Data()
user.s3Data!.bucket = bucket;
} catch (e) {
console.log("getting s3 storage data error");
user.storageDataPersonal = {storageSize: 0, failed: true}
}
}
if (user.googleDriveEnabled) {
try {
const {clientID} = await user.decryptDriveIDandKey()
const oauth2Client = await getGoogleAuth(user);
const drive = google.drive({version:"v3", auth: oauth2Client});
const googleData = await drive.about.get({
fields: "storageQuota"
})
user.storageDataGoogle = {storageLimit: +googleData.data.storageQuota!.limit!, storageSize: +googleData.data.storageQuota!.usage!}
user.googleDriveData!.id = clientID;
} catch (e) {
user.storageDataGoogle = {storageLimit: 0, storageSize: 0, failed: true}
console.log("get google drive storage data error", e.message);
}
}
if (!user.storageData || (!user.storageData.storageSize && !user.storageData.storageLimit)) user.storageData = {storageLimit: 0, storageSize: 0}
if (!user.storageDataPersonal || (!user.storageDataPersonal.storageSize && !user.storageDataPersonal.failed)) user.storageDataPersonal = {storageSize: 0}
if (!user.storageDataGoogle || (!user.storageDataGoogle.storageLimit && !user.storageDataGoogle.storageSize && !user.storageDataGoogle.failed)) user.storageDataGoogle = {storageLimit: 0, storageSize: 0}
return user;
}
verifyEmail = async(verifyToken: any) => {
const decoded: any = jwt.verify(verifyToken!, env.passwordAccess!);
const iv = decoded.iv;
const user = await User.findOne({_id: decoded._id}) as UserInterface;
const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(verifyToken, encrpytionKey, iv);
if (encryptedToken === user.emailToken) {
user.emailVerified = true;
await user.save();
return user;
} else {
throw new ForbiddenError('Email Token Verification Failed')
}
}
resendVerifyEmail = async(userID: string) => {
const user = await User.findById(userID);
if (!user) throw new NotFoundError("Cannot find user")
const verifiedEmail = user.emailVerified;
if (!verifiedEmail) {
const emailToken = await user.generateEmailVerifyToken();
await sendVerificationEmail(user, emailToken);
} else {
throw new ForbiddenError("Email Already Authorized")
}
}
sendPasswordReset = async(email: string) => {
const user = await User.findOne({email});
if (!user) throw new NotFoundError("User Not Found Password Reset Email")
const passwordResetToken = await user.generatePasswordResetToken();
await sendPasswordResetEmail(user, passwordResetToken!);
}
resetPassword = async(newPassword: string, verifyToken: any) => {
const decoded: any = jwt.verify(verifyToken!, env.passwordAccess!);
const iv = decoded.iv;
const user = await User.findOne({_id: decoded._id}) as UserInterface;
const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(verifyToken, encrpytionKey, iv);
if (encryptedToken === user.passwordResetToken) {
const encryptionKey = user.getEncryptionKey();
user.password = newPassword;
user.tokens = [];
user.tempTokens = [];
user.passwordResetToken = undefined;
await user.save();
await user.changeEncryptionKey(encryptionKey!);
} else {
throw new ForbiddenError("Reset Password Token Do Not Match")
}
}
addName = async(userID: string, name: string) => {
if (!name || name.length === 0) throw new ForbiddenError("No name")
const user = await User.findById(userID);
if (!user) throw new NotFoundError("Cannot find user")
user.name = name;
await user.save();
}
}
export default UserService;
+12
View File
@@ -0,0 +1,12 @@
class ForbiddenError extends Error {
code: number;
constructor(args: any) {
super(args);
this.code = 403;
}
}
export default ForbiddenError;
+12
View File
@@ -0,0 +1,12 @@
class NotEmailVerifiedError extends Error {
code: number;
constructor(args: any) {
super(args);
this.code = 404;
}
}
export default NotEmailVerifiedError;
+12
View File
@@ -0,0 +1,12 @@
class NotValidDataError extends Error {
code: number;
constructor(args: any) {
super(args);
this.code = 403;
}
}
export default NotValidDataError;
@@ -0,0 +1,17 @@
const convertDriveFolderToMongoFolder = (driveObj: any, ownerID: string) => {
let convertedObj:any = {};
convertedObj._id = driveObj.id;
convertedObj.name = driveObj.name;
convertedObj.createdAt = driveObj.createdTime;
convertedObj.owner = ownerID;
convertedObj.parent = driveObj.parents[driveObj.parents.length - 1];
convertedObj.parentList = driveObj.parents;
convertedObj.updatedAt = driveObj.createdTime;
convertedObj.drive = true;
convertedObj.googleDoc = driveObj.mimeType === "application/vnd.google-apps.document";
return convertedObj;
}
export default convertDriveFolderToMongoFolder;
@@ -0,0 +1,14 @@
import convertDriveFolderToMongoFolder from "./convertDriveFolderToMongoFolder";
const convertDriveFoldersToMongoFolders = (driveObjs: any, ownerID: string) => {
let convertedFolders = [];
for (let currentFolder of driveObjs) {
convertedFolders.push(convertDriveFolderToMongoFolder(currentFolder, ownerID))
}
return convertedFolders;
}
export default convertDriveFoldersToMongoFolders;
@@ -0,0 +1,14 @@
import convertDriveToMongo from "./convertDriveToMongo";
const convertDriveListToMongoList = (driveObjs: any, ownerID:string, pageToken?: string | null | undefined) => {
let convertedObjs = [];
for (let currentObj of driveObjs) {
convertedObjs.push(convertDriveToMongo(currentObj, ownerID, pageToken));
}
return convertedObjs;
}
export default convertDriveListToMongoList;
+30
View File
@@ -0,0 +1,30 @@
import videoChecker from "./videoChecker";
import {FileInterface} from "../models/file";
const convertDriveToMongo = (driveObj: any, ownerID: string, pageToken?: string | undefined | null) => {
let convertedObj: any = {};
convertedObj._id = driveObj.id;
convertedObj.filename = driveObj.name;
convertedObj.length = driveObj.size;
convertedObj.uploadDate = driveObj.modifiedTime;
convertedObj.pageToken = pageToken
convertedObj.metadata = {
IV: "",
hasThumbnail: driveObj.hasThumbnail,
isVideo: videoChecker(driveObj.name),
owner: ownerID,
parent: driveObj.parents[driveObj.parents.length - 1] === "root" ? "/" : driveObj.parents[driveObj.parents.length - 1],
parentList: driveObj.parents,
size: driveObj.size,
drive: true,
googleDoc: driveObj.mimeType === "application/vnd.google-apps.document",
thumbnailID: driveObj.thumbnailLink,
link: driveObj.shared ? driveObj.webViewLink : undefined,
linkType: driveObj.shared ? "public" : undefined
}
return convertedObj;
}
export default convertDriveToMongo;
+20 -3
View File
@@ -1,5 +1,8 @@
import s3 from "../db/s3"
import { ObjectID } from "mongodb"
export interface QueryInterface {
"metadata.owner": string,
"metadata.owner": ObjectID,
"metadata.parent"?: string,
filename?: string | RegExp | {
$lt?: string,
@@ -9,11 +12,12 @@ export interface QueryInterface {
$lt?: Date,
$gt?: Date,
},
"metadata.personalFile"?: boolean | null
}
const createQuery = (owner: string, parent: string, sortBy: string, startAt: number, startAtDate: number, searchQuery: string | RegExp, startAtName: string) => {
const createQuery = (owner: string, parent: string, sortBy: string, startAt: number, startAtDate: number, searchQuery: string | RegExp, s3Enabled: boolean,startAtName: string, storageType: string, folderSearch: boolean) => {
let query: QueryInterface = {"metadata.owner": owner}
let query: QueryInterface = {"metadata.owner": new ObjectID(owner)}
if (searchQuery !== "") {
@@ -21,6 +25,9 @@ const createQuery = (owner: string, parent: string, sortBy: string, startAt: num
query = {...query, filename: searchQuery}
if (parent !== "/" || folderSearch) query = {...query, "metadata.parent": parent}
//if (parent === "home") query = {...query, "metadata.parent": "/"};
} else {
query = {...query, "metadata.parent": parent}
@@ -46,6 +53,16 @@ const createQuery = (owner: string, parent: string, sortBy: string, startAt: num
}
}
// if (s3Enabled) {
// query = {...query, "metadata.personalFile": true}
// } else
if (!s3Enabled) {
query = {...query, "metadata.personalFile": null}
}
// if (storageType === "s3") {
// query = {...query, "metadata.personalFile": true}
// }
return query;
+34
View File
@@ -0,0 +1,34 @@
const createQueryGoogle = (query: any, parent: any) => {
let queryBuilder = `mimeType != "application/vnd.google-apps.folder"`
let orderBy = ""
if (query.sortby === "date_desc" || query.sortby === "DEFAULT") {
orderBy = "modifiedTime desc"
} else if (query.sortby === "date_asc") {
orderBy = "modifiedTime asc"
} else if (query.sortby === "alp_desc") {
orderBy = "name desc"
} else {
orderBy = "name asc"
}
if (query.search && query.search.length !== 0) {
queryBuilder += ` and name contains "${query.search}"`
} else {
queryBuilder += ` and "${parent}" in parents`
}
queryBuilder += ` and trashed=false`;
return {queryBuilder, orderBy}
}
export interface googleQueryType {
limit: number,
parent: string,
pageToken: string,
}
export default createQueryGoogle;
+28
View File
@@ -0,0 +1,28 @@
const createQueryGoogleFolder = (query: any, parent: string) => {
let orderBy = ""
if (query.sortby === "date_desc") {
orderBy = "modifiedTime desc"
} else if (query.sortby === "date_asc") {
orderBy = "modifiedTime asc"
} else if (query.sortby === "alp_desc") {
orderBy = "name desc"
} else {
orderBy = "name asc"
}
let queryBuilder = `mimeType = "application/vnd.google-apps.folder"`
if (query.search && query.search.length !== 0) {
queryBuilder += ` and name contains "${query.search}"`
} else {
queryBuilder += ` and "${parent}" in parents`;
}
queryBuilder += ` and trashed=false`;
return {orderBy, queryBuilder}
}
export default createQueryGoogleFolder;
+31
View File
@@ -0,0 +1,31 @@
import env from "../enviroment/env";
import {UserInterface} from "../models/user"
import sgMail from "@sendgrid/mail";
const sendPasswordResetEmail = async (user: UserInterface, passwordResetToken: string) => {
if (process.env.NODE_ENV === "test") {
return;
}
const apiKey: any = env.sendgridKey;
const sendgridEmail:any = env.sendgridEmail;
const url = env.remoteURL + `/reset-password/${passwordResetToken}`
// console.log("send grid api key", apiKey)
// console.log("send grid email", sendgridEmail);
// console.log("send grid reset url", url)
sgMail.setApiKey(apiKey);
const msg = {
to: user.email,
from: sendgridEmail,
subject: "myDrive Password Reset",
text: `Please navigate to the following link to reset your password: ${url}`
}
await sgMail.send(msg);
//console.log("Send grid email sent reset");
}
export default sendPasswordResetEmail;
+29
View File
@@ -0,0 +1,29 @@
import env from "../enviroment/env";
import sgMail from "@sendgrid/mail";
const currentURL = env.remoteURL;
const sendShareEmail = async (file: any, respient: string) => {
if (process.env.NODE_ENV === "test") {
return;
}
const apiKey: any = env.sendgridKey;
const sendgridEmail:any = env.sendgridEmail;
sgMail.setApiKey(apiKey);
const fileLink = `${currentURL}/download-page/${file._id}/${file.metadata.link}`
const msg = {
to: respient,
from: sendgridEmail,
subject: "A File Was Shared With You Through myDrive",
text: `Please navigate to the following link to view the file ${fileLink}`
}
await sgMail.send(msg);
}
export default sendShareEmail
+31
View File
@@ -0,0 +1,31 @@
import env from "../enviroment/env";
import {UserInterface} from "../models/user"
import sgMail from "@sendgrid/mail";
const sendVerificationEmail = async (user: UserInterface, emailToken: string) => {
if (process.env.NODE_ENV === "test") {
return;
}
const apiKey: any = env.sendgridKey;
const sendgridEmail:any = env.sendgridEmail;
const url = env.remoteURL + `/verify-email/${emailToken}`
// console.log("send grid api key", apiKey)
// console.log("send grid email", sendgridEmail);
// console.log("send grid verify url", url)
sgMail.setApiKey(apiKey);
const msg = {
to: user.email,
from: sendgridEmail,
subject: "myDrive Email Verification",
text: `Please navigate to the following link to verify your email address: ${url}`
}
await sgMail.send(msg);
//console.log("Send grid email sent");
}
export default sendVerificationEmail;
@@ -0,0 +1,58 @@
import { FolderInterface } from "../models/folder";
const sortGoogleMongoFolderList = (combinedList: FolderInterface[], query: any) => {
if (query.sortby === "date_desc" || query.sortby === "DEFAULT") {
combinedList = combinedList.sort((a, b) => {
const convertedDateA = new Date(a.createdAt).getTime();
const convertedDateB = new Date(b.createdAt).getTime();
return convertedDateB - convertedDateA;
})
} else if (query.sortby === "date_asc") {
combinedList = combinedList.sort((a, b) => {
const convertedDateA = new Date(a.createdAt).getTime();
const convertedDateB = new Date(b.createdAt).getTime();
return convertedDateA - convertedDateB;
})
} else if (query.sortby === "alp_desc") {
combinedList = combinedList.sort((a, b) => {
const name1 = a.name.toLowerCase();
const name2 = b.name.toLowerCase();
if (name1 > name2) {
return -1;
}
if (name2 > name1) {
return 1
}
return 0;
})
} else if (query.sortby === "alp_asc") {
combinedList = combinedList.sort((a, b) => {
const name1 = a.name.toLowerCase();
const name2 = b.name.toLowerCase();
if (name1 > name2) {
return 1;
}
if (name2 > name1) {
return -1
}
return 0;
})
}
return combinedList;
}
export default sortGoogleMongoFolderList;
+62
View File
@@ -0,0 +1,62 @@
import { FileInterface } from "../models/file";
const sortGoogleMongoList = (fileList: FileInterface[], query: any) => {
let combinedList = fileList;
if (query.sortby === "date_desc" || query.sortby === "DEFAULT") {
combinedList = combinedList.sort((a, b) => {
const convertedDateA = new Date(a.uploadDate).getTime();
const convertedDateB = new Date(b.uploadDate).getTime();
//onsole.log("data", b.uploadDate, convertedDate)
return convertedDateB - convertedDateA;
})
} else if (query.sortby === "date_asc") {
combinedList = combinedList.sort((a, b) => {
const convertedDateA = new Date(a.uploadDate).getTime();
const convertedDateB = new Date(b.uploadDate).getTime();
//onsole.log("data", b.uploadDate, convertedDate)
return convertedDateA - convertedDateB;
})
} else if (query.sortby === "alp_desc") {
combinedList = combinedList.sort((a, b) => {
const name1 = a.filename.toLowerCase();
const name2 = b.filename.toLowerCase();
if (name1 > name2) {
return -1;
}
if (name2 > name1) {
return 1
}
return 0;
})
} else if (query.sortby === "alp_asc") {
combinedList = combinedList.sort((a, b) => {
const name1 = a.filename.toLowerCase();
const name2 = b.filename.toLowerCase();
if (name1 > name2) {
return 1;
}
if (name2 > name1) {
return -1
}
return 0;
})
}
return combinedList;
}
export default sortGoogleMongoList;
@@ -0,0 +1,19 @@
const sortGoogleMongoQuickFiles = (convertedFiles: any[], quickList: any[]) => {
let combinedData = [...convertedFiles, ...quickList]
combinedData = combinedData.sort((a, b) => {
const convertedDateA = new Date(a.uploadDate).getTime();
const convertedDateB = new Date(b.uploadDate).getTime();
return convertedDateB - convertedDateA;
})
if (combinedData.length >= 10) {
combinedData = combinedData.slice(0, 10);
}
return combinedData;
}
export default sortGoogleMongoQuickFiles;
+37
View File
@@ -0,0 +1,37 @@
import User, {UserInterface} from "../models/user";
import {Request, Response, NextFunction} from "express";
import NotFoundError from "./NotFoundError";
import { createLoginCookie } from "../cookies/createCookies";
// interface RequestType extends Request {
// user?: userAccessType,
// token?: string,
// encryptedToken?: string,
// }
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
botChecked: boolean,
}
const userUpdateCheck = async(res: Response, id: string, uuid: string | undefined) => {
const updatedUser = await User.findById(id);
if (!updatedUser) throw new NotFoundError("Cannot find updated user auth");
if (updatedUser.emailVerified) {
const {accessToken, refreshToken} = await updatedUser.generateAuthToken(uuid);
createLoginCookie(res, accessToken, refreshToken);
}
let strippedUser: userAccessType = {_id: updatedUser._id, emailVerified: updatedUser.emailVerified!, email: updatedUser.email, botChecked: false}
return strippedUser;
}
export default userUpdateCheck;
+102
View File
@@ -0,0 +1,102 @@
import express, {Request, Response} from "express";
import http from "http";
import path from "path";
import bodyParser from "body-parser";
import fs from "fs";
const app = express();
const getWebUIKey = () => {
console.log("Starting Server...\n")
const publicPath = path.join(__dirname, "..", "..", "webUISetup");
return new Promise<string>((resolve, reject) => {
app.use(express.static(publicPath));
app.use(express.json());
app.use(bodyParser.json({limit: "50mb"}));
app.use(bodyParser.urlencoded({limit: "50mb", extended: true, parameterLimit:50000}))
app.post("/submit", async(req: Request, res: Response) => {
console.log("Response")
await createConfigFiles(req.body.clientObj, req.body.serverObj);
res.send();
})
app.get("*", (req: Request, res: Response) => {
res.sendFile(path.join(publicPath, "index.html"));
})
const port = process.env.HTTP_PORT || process.env.PORT || "3000";
const url = process.env.DOCKER ? undefined : "localhost";
const server = http.createServer(app) as any;
server.listen(port, () => {
console.log(`\nPlease navigate to http://localhost:${port} to enter setup details\n`)
});
})
}
const awaitcreateDir = (path: any) => {
return new Promise((resolve, reject) => {
fs.mkdir(path, () => {
resolve();
})
})
}
const awaitWriteFile = (path:string, data:string) => {
return new Promise((resolve, reject) => {
fs.writeFile(path, data, async(err:any) => {
if (err) {
console.log("file write error", err);
reject();
}
resolve();
})
})
}
const createConfigFiles = async(clientObj: any, serverObj: any) => {
await awaitcreateDir("./config")
let totalClientString = "";
let totalServerString = "";
for (let currentKey in clientObj) {
totalClientString += `${currentKey}=${clientObj[currentKey]}\n`
}
for (let currentKey in serverObj) {
totalServerString += `${currentKey}=${serverObj[currentKey]}\n`
}
if (serverObj.DOCKER) {
const combinedStrings = totalClientString + totalServerString;
await awaitWriteFile("./docker-variables.env", combinedStrings);
} else {
await awaitWriteFile("./.env.production", totalClientString);
await awaitWriteFile("./config/prod.env", totalServerString);
}
console.log("File(s) Created");
}
getWebUIKey();
Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 MiB

Some files were not shown because too many files have changed in this diff Show More