From 2d232042e0ebe94cb1cba6ba06b76e075e08ca9f Mon Sep 17 00:00:00 2001 From: Kai Devrim Date: Mon, 15 May 2023 11:01:08 -0700 Subject: [PATCH 01/10] Add style changes --- apps/web/hooks/useS3.tsx | 2 +- apps/web/util/helpers/s3-helpers.ts | 2 +- apps/web/util/upload/chunks/getChunkBuffer.ts | 39 ++++++----- apps/web/util/upload/chunks/getChunkStream.ts | 65 ++++++++++--------- .../web/util/upload/chunks/getDataReadable.ts | 6 +- .../upload/chunks/getDataReadableStream.ts | 34 +++++----- apps/web/yarn.lock | 28 ++------ 7 files changed, 81 insertions(+), 95 deletions(-) diff --git a/apps/web/hooks/useS3.tsx b/apps/web/hooks/useS3.tsx index e188d06..0bb5186 100644 --- a/apps/web/hooks/useS3.tsx +++ b/apps/web/hooks/useS3.tsx @@ -3,7 +3,7 @@ import { DeleteObjectsCommand, GetObjectCommand, ListObjectsV2Command, - S3Client + S3Client, } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { Drive } from "@prisma/client"; diff --git a/apps/web/util/helpers/s3-helpers.ts b/apps/web/util/helpers/s3-helpers.ts index 85deb18..6a4aebf 100644 --- a/apps/web/util/helpers/s3-helpers.ts +++ b/apps/web/util/helpers/s3-helpers.ts @@ -4,7 +4,7 @@ import { PutBucketCorsCommand, PutBucketCorsCommandInput, PutPublicAccessBlockCommand, - S3Client + S3Client, } from "@aws-sdk/client-s3"; import { NextApiRequest, NextApiResponse } from "next"; diff --git a/apps/web/util/upload/chunks/getChunkBuffer.ts b/apps/web/util/upload/chunks/getChunkBuffer.ts index 2e3d96e..2a35ff8 100644 --- a/apps/web/util/upload/chunks/getChunkBuffer.ts +++ b/apps/web/util/upload/chunks/getChunkBuffer.ts @@ -1,23 +1,26 @@ import { RawDataPart } from "../index"; -export async function* getChunkBuffer(data: Buffer, partSize: number): AsyncGenerator { - let partNumber = 1; - let startByte = 0; - let endByte = partSize; +export async function* getChunkBuffer( + data: Buffer, + partSize: number +): AsyncGenerator { + let partNumber = 1; + let startByte = 0; + let endByte = partSize; - while (endByte < data.byteLength) { - yield { - partNumber, - data: data.slice(startByte, endByte), - }; - partNumber += 1; - startByte = endByte; - endByte = startByte + partSize; - } + while (endByte < data.byteLength) { + yield { + partNumber, + data: data.slice(startByte, endByte), + }; + partNumber += 1; + startByte = endByte; + endByte = startByte + partSize; + } - yield { - partNumber, - data: data.slice(startByte), - lastPart: true, - }; + yield { + partNumber, + data: data.slice(startByte), + lastPart: true, + }; } diff --git a/apps/web/util/upload/chunks/getChunkStream.ts b/apps/web/util/upload/chunks/getChunkStream.ts index f1d1ec1..2e1a88d 100644 --- a/apps/web/util/upload/chunks/getChunkStream.ts +++ b/apps/web/util/upload/chunks/getChunkStream.ts @@ -3,43 +3,46 @@ import { Buffer } from "buffer"; import { RawDataPart } from "../index"; interface Buffers { - chunks: Buffer[]; - length: number; + chunks: Buffer[]; + length: number; } export async function* getChunkStream( - data: T, - partSize: number, - getNextData: (data: T) => AsyncGenerator + data: T, + partSize: number, + getNextData: (data: T) => AsyncGenerator ): AsyncGenerator { - let partNumber = 1; - const currentBuffer: Buffers = { chunks: [], length: 0 }; + let partNumber = 1; + const currentBuffer: Buffers = { chunks: [], length: 0 }; - for await (const datum of getNextData(data)) { - currentBuffer.chunks.push(datum); - currentBuffer.length += datum.length; + for await (const datum of getNextData(data)) { + currentBuffer.chunks.push(datum); + currentBuffer.length += datum.length; - while (currentBuffer.length >= partSize) { - /** - * Concat all the buffers together once if there is more than one to concat. Attempt - * to minimize concats as Buffer.Concat is an extremely expensive operation. - */ - const dataChunk = currentBuffer.chunks.length > 1 ? Buffer.concat(currentBuffer.chunks) : currentBuffer.chunks[0]; + while (currentBuffer.length >= partSize) { + /** + * Concat all the buffers together once if there is more than one to concat. Attempt + * to minimize concats as Buffer.Concat is an extremely expensive operation. + */ + const dataChunk = + currentBuffer.chunks.length > 1 + ? Buffer.concat(currentBuffer.chunks) + : currentBuffer.chunks[0]; - yield { - partNumber, - data: dataChunk.slice(0, partSize), - }; + yield { + partNumber, + data: dataChunk.slice(0, partSize), + }; - // Reset the buffer. - currentBuffer.chunks = [dataChunk.slice(partSize)]; - currentBuffer.length = currentBuffer.chunks[0].length; - partNumber += 1; - } - } - yield { - partNumber, - data: Buffer.concat(currentBuffer.chunks), - lastPart: true, - }; + // Reset the buffer. + currentBuffer.chunks = [dataChunk.slice(partSize)]; + currentBuffer.length = currentBuffer.chunks[0].length; + partNumber += 1; + } + } + yield { + partNumber, + data: Buffer.concat(currentBuffer.chunks), + lastPart: true, + }; } diff --git a/apps/web/util/upload/chunks/getDataReadable.ts b/apps/web/util/upload/chunks/getDataReadable.ts index 7e71acf..e3d8f8f 100644 --- a/apps/web/util/upload/chunks/getDataReadable.ts +++ b/apps/web/util/upload/chunks/getDataReadable.ts @@ -2,7 +2,7 @@ import { Buffer } from "buffer"; import { Readable } from "stream"; export async function* getDataReadable(data: Readable): AsyncGenerator { - for await (const chunk of data) { - yield Buffer.from(chunk); - } + for await (const chunk of data) { + yield Buffer.from(chunk); + } } diff --git a/apps/web/util/upload/chunks/getDataReadableStream.ts b/apps/web/util/upload/chunks/getDataReadableStream.ts index 0f76716..2d7d1a2 100644 --- a/apps/web/util/upload/chunks/getDataReadableStream.ts +++ b/apps/web/util/upload/chunks/getDataReadableStream.ts @@ -1,22 +1,22 @@ import { Buffer } from "buffer"; export async function* getDataReadableStream(data: ReadableStream): AsyncGenerator { - // Get a lock on the stream. - const reader = data.getReader(); + // Get a lock on the stream. + const reader = data.getReader(); - try { - while (true) { - // Read from the stream. - const { done, value } = await reader.read(); - // Exit if we're done. - if (done) return; - // Else yield the chunk. - yield Buffer.from(value); - } - } catch (e) { - throw e; - } finally { - // release the lock for reading from this stream. - reader.releaseLock(); - } + try { + while (true) { + // Read from the stream. + const { done, value } = await reader.read(); + // Exit if we're done. + if (done) return; + // Else yield the chunk. + yield Buffer.from(value); + } + } catch (e) { + throw e; + } finally { + // release the lock for reading from this stream. + reader.releaseLock(); + } } diff --git a/apps/web/yarn.lock b/apps/web/yarn.lock index 771a7bb..57a1e77 100644 --- a/apps/web/yarn.lock +++ b/apps/web/yarn.lock @@ -2735,11 +2735,6 @@ dependencies: "@types/unist" "*" -"@types/js-md5@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@types/js-md5/-/js-md5-0.4.3.tgz#c134cbb71c75018876181f886a12a2f9962a312a" - integrity sha512-BIga/WEqTi35ccnGysOuO4RmwVnpajv9oDB/sDQSY2b7/Ac7RyYR30bv7otZwByMvOJV9Vqq6/O1DFAnOzE4Pg== - "@types/json-schema@^7.0.5": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" @@ -3379,15 +3374,10 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228: - version "1.0.30001283" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz#8573685bdae4d733ef18f78d44ba0ca5fe9e896b" - integrity sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg== - -caniuse-lite@^1.0.30001286: - version "1.0.30001307" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001307.tgz#27a67f13ebc4aa9c977e6b8256a11d5eafb30f27" - integrity sha512-+MXEMczJ4FuxJAUp0jvAl6Df0NI/OfW1RWEE61eSmzS7hw6lz4IKutbhbXendwq8BljfFuHtu26VWsg4afQ7Ng== +caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001286: + version "1.0.30001477" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001477.tgz" + integrity sha512-lZim4iUHhGcy5p+Ri/G7m84hJwncj+Kz7S5aD4hoQfslKZJgt0tHc/hafVbqHC5bbhHb+mrW2JOUHkI5KH7toQ== capture-stack-trace@^1.0.0: version "1.0.1" @@ -5334,16 +5324,6 @@ jest-worker@27.0.0-next.5: merge-stream "^2.0.0" supports-color "^8.0.0" -js-md5@^0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/js-md5/-/js-md5-0.7.3.tgz#b4f2fbb0b327455f598d6727e38ec272cd09c3f2" - integrity sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ== - -js-sha256@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" - integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" From 9887d2928d7be344105ba13c720aa3615fae0de1 Mon Sep 17 00:00:00 2001 From: Kai Devrim Date: Tue, 16 May 2023 17:31:56 -0700 Subject: [PATCH 02/10] Start samba support, add "addFolder" and "removeFolder" methods --- apps/web/hooks/useSamba.tsx | 56 ++++++++++++++++ apps/web/package.json | 5 +- apps/web/pages/new/samba.tsx | 0 apps/web/yarn.lock | 123 +++++++++++++++++++++++++++-------- yarn.lock | 4 ++ 5 files changed, 159 insertions(+), 29 deletions(-) create mode 100644 apps/web/hooks/useSamba.tsx create mode 100644 apps/web/pages/new/samba.tsx create mode 100644 yarn.lock diff --git a/apps/web/hooks/useSamba.tsx b/apps/web/hooks/useSamba.tsx new file mode 100644 index 0000000..d308fed --- /dev/null +++ b/apps/web/hooks/useSamba.tsx @@ -0,0 +1,56 @@ +import { createContext, useContext, useRef, useState } from "react"; +import SambaClient from "samba-client"; +import { ContextValue, ROOT_FOLDER } from "./useBucket"; +import { Drive } from "@prisma/client"; +import { DriveFile, DriveFolder, UploadingFile } from "@util/types"; + +const SambaContext = createContext(null); +export default () => useContext(SambaContext); + +type Props = { + data: Drive & { keys: any }; + fullPath?: string; +}; +export const SambaProvider: React.FC = ({ data, fullPath, children }) => { + const sambaClient = new SambaClient({ + address: data.keys.address, + username: "" || data.keys.username, + password: "" || data.keys.password, + domain: "WORKGROUP" || data.keys.domain, + maxProtocol: "SMB3", + }) + const [loading, setLoading] = useState(false); + const [currentFolder, setCurrentFolder] = useState(null); + const [folders, setFolders] = useState(null); + const [uploadingFiles, setUploadingFiles] = useState([]); + const [files, setFiles] = useState(null); + const isMounted = useRef(false); + + const addFolder = async (name: string) => { + const path = + currentFolder.fullPath !== "" + ? decodeURIComponent(currentFolder.fullPath) + name + "/" + : name + "/"; + const newFolder: DriveFolder = { + name, + fullPath: path, + parent: currentFolder.fullPath + } + setFolders((folders) => [...folders, newFolder]) + await sambaClient.mkdir(newFolder.name, path); + const localFolders = localStorage.getItem(`local_folders_${data.id}`); + const folders: DriveFolder[] = localFolders ? JSON.parse(localFolders) : []; + localStorage.setItem(`local_folders_${data.id}`, JSON.stringify([...folders, newFolder])); + }; + + const removeFolder = async (folder: DriveFolder) => { + setFolders((folders) => folders.filter((f) => f.fullPath !== folder.fullPath)); + const localFolders = localStorage.getItem(`local_folders_${data.id}`); + if (localFolders) { + const folders = JSON.parse(localFolders); + const filtered = folders.filter((f) => !f.fullPath.includes(folder.fullPath)); + localStorage.setItem(`local_folders_${data.id}`, JSON.stringify(filtered)); + } + await sambaClient.deleteFile(folder.fullPath) + } +}; diff --git a/apps/web/package.json b/apps/web/package.json index e52e1d9..81ec864 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -16,7 +16,7 @@ "@chakra-ui/react": "^1.7.2", "@emotion/react": "^11", "@emotion/styled": "^11", - "@prisma/client": "3.13.0", + "@prisma/client": "^4.14.0", "@react-hook/media-query": "^1.1.1", "@sendgrid/mail": "^7.6.2", "@uiw/react-markdown-preview": "^3.4.7", @@ -46,6 +46,7 @@ "react-error-boundary": "^3.1.4", "react-hot-toast": "^2.2.0", "react-loading-overlay": "^1.0.1", + "samba-client": "^7.2.0", "swr": "^1.1.2-beta.0", "tabler-icons-react": "^1.45.0", "underscore": "^1.13.2", @@ -66,7 +67,7 @@ "@types/underscore": "^1.11.4", "@types/validator": "^13.7.2", "prettier": "^2.6.2", - "prisma": "3.13.0", + "prisma": "^4.14.0", "typescript": "4.3.2" } } diff --git a/apps/web/pages/new/samba.tsx b/apps/web/pages/new/samba.tsx new file mode 100644 index 0000000..e69de29 diff --git a/apps/web/yarn.lock b/apps/web/yarn.lock index 57a1e77..a0d29c1 100644 --- a/apps/web/yarn.lock +++ b/apps/web/yarn.lock @@ -2530,22 +2530,22 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590" integrity sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ== -"@prisma/client@3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.13.0.tgz#84511ebdf6ba75f77ca08495b9f73f22c4255654" - integrity sha512-lnEA2tTyVbO5mS1ehmHJQKBDiKB8shaR6s3azwj3Azfi5XHIfnqmkolLCvUeFYnkDCNVzGXJpUgKwQt/UOOYVQ== +"@prisma/client@^4.14.0": + version "4.14.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.14.0.tgz#715b3dd045d094b03cb0a7f2991f088d15ae553e" + integrity sha512-MK/XaA2sFdfaOa7I9MjNKz6dxeIEdeZlnpNRoF2w3JuRLlFJLkpp6cD3yaqw2nUUhbrn3Iqe3ZpVV+VuGGil7Q== dependencies: - "@prisma/engines-version" "3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b" + "@prisma/engines-version" "4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c" -"@prisma/engines-version@3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b": - version "3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b.tgz#676aca309d66d9be2aad8911ca31f1ee5561041c" - integrity sha512-TGp9rvgJIKo8NgvAHSwOosbut9mTA7VC6/rpQI9gh+ySSRjdQFhbGyNUiOcQrlI9Ob2DWeO7y4HEnhdKxYiECg== +"@prisma/engines-version@4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c": + version "4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c.tgz#0aeca447c4a5f23c83f68b8033e627b60bc01850" + integrity sha512-3jum8/YSudeSN0zGW5qkpz+wAN2V/NYCQ+BPjvHYDfWatLWlQkqy99toX0GysDeaUoBIJg1vaz2yKqiA3CFcQw== -"@prisma/engines@3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b": - version "3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b.tgz#d3a457cec4ef7a3b3412c45b1f2eac68c974474b" - integrity sha512-Ip9CcCeUocH61eXu4BUGpvl5KleQyhcUVLpWCv+0ZmDv44bFaDpREqjGHHdRupvPN/ugB6gTlD9b9ewdj02yVA== +"@prisma/engines@4.14.0": + version "4.14.0" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.14.0.tgz#4bed9e6f3d01bb0e4598e76c1726f33579d6a2c3" + integrity sha512-PDNlhP/1vyTgmNyiucGqGCdXIp7HIkkvKO50si3y3PcceeHvqtiKPaH1iJdz63jCWMVMbj2MElSxXPOeBvEVIQ== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -3774,6 +3774,15 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypto-browserify@3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -4200,6 +4209,21 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -4460,6 +4484,11 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -4894,6 +4923,11 @@ https-browserify@1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -5263,6 +5297,11 @@ is-stream@^1.0.0, is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -6328,6 +6367,13 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + nprogress@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" @@ -6380,7 +6426,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -6578,6 +6624,11 @@ path-key@^2.0.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -6665,13 +6716,12 @@ pretty-bytes@^5.6.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -prisma@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.13.0.tgz#b11edd5631222ff1bf1d5324732d47801386aa8c" - integrity sha512-oO1auBnBtieGdiN+57IgsA9Vr7Sy4HkILi1KSaUG4mpKfEbnkTGnLOxAqjLed+K2nsG/GtE1tJBtB7JxN1a78Q== +prisma@^4.14.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.14.0.tgz#1328308f6b2b4b0f5f1e4839b4d81e817377e179" + integrity sha512-+5dMl1uxMQb4RepndY6AwR9xi1cDcaGFICu+ws6/Nmgt93mFPNj8tYxSfTdmfg+rkNrUId9rk/Ac2vTgLe/oXA== dependencies: - "@prisma/engines" "3.13.0-17.efdf9b1183dddfd4258cd181a72125755215ab7b" - ts-pattern "^4.0.1" + "@prisma/engines" "4.14.0" prismjs@~1.25.0: version "1.25.0" @@ -7296,6 +7346,13 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +samba-client@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/samba-client/-/samba-client-7.2.0.tgz#079c5d21af6d9accbff85cb6e175bf8853254733" + integrity sha512-3wVq4UiHcJ3ZArsbDRsZ9W1P4fAgXU2CPjldsDuLDH3xzN7sd3MbtuNlTjc0Vkfq7+Bl4vcHqcpZEWRBMltHtw== + dependencies: + execa "^5.1.1" + scheduler@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" @@ -7396,11 +7453,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shell-quote@1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" @@ -7420,7 +7489,7 @@ sigmund@^1.0.1: resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= -signal-exit@^3.0.0: +signal-exit@^3.0.0, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -7623,6 +7692,11 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -7800,11 +7874,6 @@ trough@^2.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-2.0.2.tgz#94a3aa9d5ce379fc561f6244905b3f36b7458d96" integrity sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w== -ts-pattern@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/ts-pattern/-/ts-pattern-4.0.2.tgz#b36afdb2de1ec0224539dcb7cea3a57c41453b9f" - integrity sha512-eHqR/7A6fcw05vCOfnL6RwgGJbVi9G/YHTdYdjYmElhDdJ1SMn7pWs+6+YuxygaFwQS/g+cIDlu+UD8IVpur1A== - tslib@^1.0.0, tslib@^1.11.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -8298,7 +8367,7 @@ which@^1.2.9, which@^1.3.0: dependencies: isexe "^2.0.0" -which@^2.0.2: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fb57ccd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + From 08e94e086042f1f02f987941c1668295ebf85c5a Mon Sep 17 00:00:00 2001 From: Kai Devrim Date: Wed, 24 May 2023 19:30:08 -0700 Subject: [PATCH 03/10] Add addFile and removeFile methods --- apps/web/hooks/useSamba.tsx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/apps/web/hooks/useSamba.tsx b/apps/web/hooks/useSamba.tsx index d308fed..5f6d5b6 100644 --- a/apps/web/hooks/useSamba.tsx +++ b/apps/web/hooks/useSamba.tsx @@ -3,6 +3,8 @@ import SambaClient from "samba-client"; import { ContextValue, ROOT_FOLDER } from "./useBucket"; import { Drive } from "@prisma/client"; import { DriveFile, DriveFolder, UploadingFile } from "@util/types"; +import toast from "react-hot-toast"; +import { Upload } from "@util/upload"; const SambaContext = createContext(null); export default () => useContext(SambaContext); @@ -53,4 +55,26 @@ export const SambaProvider: React.FC = ({ data, fullPath, children }) => } await sambaClient.deleteFile(folder.fullPath) } + + const addFile = async (filesToUpload: File[] | FileList) => { + Array.from(filesToUpload).forEach(async (file) => { + if (/[#\$\[\]\*/]/.test(file.name)) + return toast.error("File name cannot contain special characters (#$[]*/)."); + + const Key = + currentFolder === ROOT_FOLDER + ? file.name + : `${decodeURIComponent(currentFolder.fullPath)}${file.name}`; + if (await sambaClient.fileExists(file.name, Key)) { + return toast.error("File with same name already exists."); + } + await sambaClient.sendFile(file.name, Key) + }) + } + + const removeFile = async (file: DriveFile) => { + setFiles((files) => files.filter((f) => f.fullPath !== file.fullPath)); + await sambaClient.deleteFile(file.fullPath); + return true; + } }; From 6ad397518eca61adc525d399d7c4031753a122c4 Mon Sep 17 00:00:00 2001 From: Kai Devrim Date: Wed, 24 May 2023 19:56:00 -0700 Subject: [PATCH 04/10] Maybe finish the useSamba hook --- apps/web/hooks/useSamba.tsx | 74 ++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/apps/web/hooks/useSamba.tsx b/apps/web/hooks/useSamba.tsx index 5f6d5b6..9197c3e 100644 --- a/apps/web/hooks/useSamba.tsx +++ b/apps/web/hooks/useSamba.tsx @@ -1,10 +1,11 @@ -import { createContext, useContext, useRef, useState } from "react"; +import { createContext, useContext, useEffect, useRef, useState } from "react"; import SambaClient from "samba-client"; import { ContextValue, ROOT_FOLDER } from "./useBucket"; import { Drive } from "@prisma/client"; import { DriveFile, DriveFolder, UploadingFile } from "@util/types"; import toast from "react-hot-toast"; -import { Upload } from "@util/upload"; +import useUser from "./useUser"; +import mime from "mime"; const SambaContext = createContext(null); export default () => useContext(SambaContext); @@ -21,6 +22,7 @@ export const SambaProvider: React.FC = ({ data, fullPath, children }) => domain: "WORKGROUP" || data.keys.domain, maxProtocol: "SMB3", }) + const { user } = useUser(); const [loading, setLoading] = useState(false); const [currentFolder, setCurrentFolder] = useState(null); const [folders, setFolders] = useState(null); @@ -77,4 +79,72 @@ export const SambaProvider: React.FC = ({ data, fullPath, children }) => await sambaClient.deleteFile(file.fullPath); return true; } + + useEffect(() => { + if (!user?.email) return; + setFiles(null); + setFolders(null); + + if (fullPath === "" || !fullPath) { + setCurrentFolder(ROOT_FOLDER); + return; + } + + setCurrentFolder({ + fullPath: fullPath + "/", + name: fullPath.split("/").pop(), + parent: fullPath.split("/").shift() + "/", + }); + }, [fullPath, user]); + + // get files and folders + useEffect(() => { + if (!user?.email || !currentFolder) return; + setLoading(true); + + (async () => { + try { + if (!files) { + var results = await sambaClient.list(currentFolder.fullPath) + results.forEach(async (result) => { + const driveFile: DriveFile = { + fullPath: currentFolder.fullPath + "/" + result.name, + name: result.name.split("/").pop(), + parent: currentFolder.fullPath, + createdAt: result.modifyTime.toISOString(), + size: result.size.toString(), + contentType: mime.lookup(result.type) || "", + }; + + setFiles((files) => (files ? [...files, driveFile] : [driveFile])); + }); + + const localFolders = localStorage.getItem(`local_folders_${data.id}`); + setLoading(false); + } + } + catch (err) { + console.error(err); + } + }) + }, [currentFolder, user]); + + return ( + + {children} + + ); }; From f3b33972b653499661ababe6ac5e31318b73d303 Mon Sep 17 00:00:00 2001 From: fayd Date: Sat, 19 Aug 2023 11:21:13 +0530 Subject: [PATCH 05/10] Delete yarn.lock --- yarn.lock | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 yarn.lock diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index fb57ccd..0000000 --- a/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - From a9cf6630c53e1cea5b16a7f08a4d2815e2548afd Mon Sep 17 00:00:00 2001 From: faisalsayed10 Date: Sat, 19 Aug 2023 11:22:16 +0530 Subject: [PATCH 06/10] chore: linting --- apps/web/util/upload/chunks/getChunkBuffer.ts | 38 +++++------ apps/web/util/upload/chunks/getChunkStream.ts | 68 +++++++++---------- .../web/util/upload/chunks/getDataReadable.ts | 6 +- .../upload/chunks/getDataReadableStream.ts | 34 +++++----- 4 files changed, 73 insertions(+), 73 deletions(-) diff --git a/apps/web/util/upload/chunks/getChunkBuffer.ts b/apps/web/util/upload/chunks/getChunkBuffer.ts index 2a35ff8..a6d5231 100644 --- a/apps/web/util/upload/chunks/getChunkBuffer.ts +++ b/apps/web/util/upload/chunks/getChunkBuffer.ts @@ -1,26 +1,26 @@ import { RawDataPart } from "../index"; export async function* getChunkBuffer( - data: Buffer, - partSize: number + data: Buffer, + partSize: number, ): AsyncGenerator { - let partNumber = 1; - let startByte = 0; - let endByte = partSize; + let partNumber = 1; + let startByte = 0; + let endByte = partSize; - while (endByte < data.byteLength) { - yield { - partNumber, - data: data.slice(startByte, endByte), - }; - partNumber += 1; - startByte = endByte; - endByte = startByte + partSize; - } + while (endByte < data.byteLength) { + yield { + partNumber, + data: data.slice(startByte, endByte), + }; + partNumber += 1; + startByte = endByte; + endByte = startByte + partSize; + } - yield { - partNumber, - data: data.slice(startByte), - lastPart: true, - }; + yield { + partNumber, + data: data.slice(startByte), + lastPart: true, + }; } diff --git a/apps/web/util/upload/chunks/getChunkStream.ts b/apps/web/util/upload/chunks/getChunkStream.ts index 2e1a88d..e121947 100644 --- a/apps/web/util/upload/chunks/getChunkStream.ts +++ b/apps/web/util/upload/chunks/getChunkStream.ts @@ -3,46 +3,46 @@ import { Buffer } from "buffer"; import { RawDataPart } from "../index"; interface Buffers { - chunks: Buffer[]; - length: number; + chunks: Buffer[]; + length: number; } export async function* getChunkStream( - data: T, - partSize: number, - getNextData: (data: T) => AsyncGenerator + data: T, + partSize: number, + getNextData: (data: T) => AsyncGenerator, ): AsyncGenerator { - let partNumber = 1; - const currentBuffer: Buffers = { chunks: [], length: 0 }; + let partNumber = 1; + const currentBuffer: Buffers = { chunks: [], length: 0 }; - for await (const datum of getNextData(data)) { - currentBuffer.chunks.push(datum); - currentBuffer.length += datum.length; + for await (const datum of getNextData(data)) { + currentBuffer.chunks.push(datum); + currentBuffer.length += datum.length; - while (currentBuffer.length >= partSize) { - /** - * Concat all the buffers together once if there is more than one to concat. Attempt - * to minimize concats as Buffer.Concat is an extremely expensive operation. - */ - const dataChunk = - currentBuffer.chunks.length > 1 - ? Buffer.concat(currentBuffer.chunks) - : currentBuffer.chunks[0]; + while (currentBuffer.length >= partSize) { + /** + * Concat all the buffers together once if there is more than one to concat. Attempt + * to minimize concats as Buffer.Concat is an extremely expensive operation. + */ + const dataChunk = + currentBuffer.chunks.length > 1 + ? Buffer.concat(currentBuffer.chunks) + : currentBuffer.chunks[0]; - yield { - partNumber, - data: dataChunk.slice(0, partSize), - }; + yield { + partNumber, + data: dataChunk.slice(0, partSize), + }; - // Reset the buffer. - currentBuffer.chunks = [dataChunk.slice(partSize)]; - currentBuffer.length = currentBuffer.chunks[0].length; - partNumber += 1; - } - } - yield { - partNumber, - data: Buffer.concat(currentBuffer.chunks), - lastPart: true, - }; + // Reset the buffer. + currentBuffer.chunks = [dataChunk.slice(partSize)]; + currentBuffer.length = currentBuffer.chunks[0].length; + partNumber += 1; + } + } + yield { + partNumber, + data: Buffer.concat(currentBuffer.chunks), + lastPart: true, + }; } diff --git a/apps/web/util/upload/chunks/getDataReadable.ts b/apps/web/util/upload/chunks/getDataReadable.ts index e3d8f8f..7e71acf 100644 --- a/apps/web/util/upload/chunks/getDataReadable.ts +++ b/apps/web/util/upload/chunks/getDataReadable.ts @@ -2,7 +2,7 @@ import { Buffer } from "buffer"; import { Readable } from "stream"; export async function* getDataReadable(data: Readable): AsyncGenerator { - for await (const chunk of data) { - yield Buffer.from(chunk); - } + for await (const chunk of data) { + yield Buffer.from(chunk); + } } diff --git a/apps/web/util/upload/chunks/getDataReadableStream.ts b/apps/web/util/upload/chunks/getDataReadableStream.ts index 2d7d1a2..0f76716 100644 --- a/apps/web/util/upload/chunks/getDataReadableStream.ts +++ b/apps/web/util/upload/chunks/getDataReadableStream.ts @@ -1,22 +1,22 @@ import { Buffer } from "buffer"; export async function* getDataReadableStream(data: ReadableStream): AsyncGenerator { - // Get a lock on the stream. - const reader = data.getReader(); + // Get a lock on the stream. + const reader = data.getReader(); - try { - while (true) { - // Read from the stream. - const { done, value } = await reader.read(); - // Exit if we're done. - if (done) return; - // Else yield the chunk. - yield Buffer.from(value); - } - } catch (e) { - throw e; - } finally { - // release the lock for reading from this stream. - reader.releaseLock(); - } + try { + while (true) { + // Read from the stream. + const { done, value } = await reader.read(); + // Exit if we're done. + if (done) return; + // Else yield the chunk. + yield Buffer.from(value); + } + } catch (e) { + throw e; + } finally { + // release the lock for reading from this stream. + reader.releaseLock(); + } } From c1700ff01249a998372182751065d642fe769d9b Mon Sep 17 00:00:00 2001 From: faisalsayed10 Date: Sat, 19 Aug 2023 11:23:56 +0530 Subject: [PATCH 07/10] chore: linting --- apps/web/util/upload/chunks/getChunkBuffer.ts | 5 +---- apps/web/util/upload/chunks/getChunkStream.ts | 7 ++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/web/util/upload/chunks/getChunkBuffer.ts b/apps/web/util/upload/chunks/getChunkBuffer.ts index a6d5231..2e3d96e 100644 --- a/apps/web/util/upload/chunks/getChunkBuffer.ts +++ b/apps/web/util/upload/chunks/getChunkBuffer.ts @@ -1,9 +1,6 @@ import { RawDataPart } from "../index"; -export async function* getChunkBuffer( - data: Buffer, - partSize: number, -): AsyncGenerator { +export async function* getChunkBuffer(data: Buffer, partSize: number): AsyncGenerator { let partNumber = 1; let startByte = 0; let endByte = partSize; diff --git a/apps/web/util/upload/chunks/getChunkStream.ts b/apps/web/util/upload/chunks/getChunkStream.ts index e121947..f1d1ec1 100644 --- a/apps/web/util/upload/chunks/getChunkStream.ts +++ b/apps/web/util/upload/chunks/getChunkStream.ts @@ -10,7 +10,7 @@ interface Buffers { export async function* getChunkStream( data: T, partSize: number, - getNextData: (data: T) => AsyncGenerator, + getNextData: (data: T) => AsyncGenerator ): AsyncGenerator { let partNumber = 1; const currentBuffer: Buffers = { chunks: [], length: 0 }; @@ -24,10 +24,7 @@ export async function* getChunkStream( * Concat all the buffers together once if there is more than one to concat. Attempt * to minimize concats as Buffer.Concat is an extremely expensive operation. */ - const dataChunk = - currentBuffer.chunks.length > 1 - ? Buffer.concat(currentBuffer.chunks) - : currentBuffer.chunks[0]; + const dataChunk = currentBuffer.chunks.length > 1 ? Buffer.concat(currentBuffer.chunks) : currentBuffer.chunks[0]; yield { partNumber, From 969755e07cb6cca043e09f574c222c0d4aded868 Mon Sep 17 00:00:00 2001 From: faisalsayed10 Date: Sat, 19 Aug 2023 11:29:28 +0530 Subject: [PATCH 08/10] minor fixes --- apps/web/hooks/useSamba.tsx | 8 +- apps/web/pages/new/samba.tsx | 219 +++++++++++++++++++++++++++++++++++ apps/web/pnpm-lock.yaml | 78 +++++++++++++ 3 files changed, 302 insertions(+), 3 deletions(-) diff --git a/apps/web/hooks/useSamba.tsx b/apps/web/hooks/useSamba.tsx index 9197c3e..b99f1f4 100644 --- a/apps/web/hooks/useSamba.tsx +++ b/apps/web/hooks/useSamba.tsx @@ -1,11 +1,11 @@ -import { createContext, useContext, useEffect, useRef, useState } from "react"; +import { createContext, PropsWithChildren, useContext, useEffect, useRef, useState } from "react"; import SambaClient from "samba-client"; import { ContextValue, ROOT_FOLDER } from "./useBucket"; import { Drive } from "@prisma/client"; import { DriveFile, DriveFolder, UploadingFile } from "@util/types"; import toast from "react-hot-toast"; import useUser from "./useUser"; -import mime from "mime"; +import mime from "mime-types"; const SambaContext = createContext(null); export default () => useContext(SambaContext); @@ -14,7 +14,7 @@ type Props = { data: Drive & { keys: any }; fullPath?: string; }; -export const SambaProvider: React.FC = ({ data, fullPath, children }) => { +export const SambaProvider: React.FC> = ({ data, fullPath, children }) => { const sambaClient = new SambaClient({ address: data.keys.address, username: "" || data.keys.username, @@ -29,6 +29,7 @@ export const SambaProvider: React.FC = ({ data, fullPath, children }) => const [uploadingFiles, setUploadingFiles] = useState([]); const [files, setFiles] = useState(null); const isMounted = useRef(false); + const enableTags = false; const addFolder = async (name: string) => { const path = @@ -137,6 +138,7 @@ export const SambaProvider: React.FC = ({ data, fullPath, children }) => files, folders, uploadingFiles, + enableTags, setUploadingFiles, addFile, addFolder, diff --git a/apps/web/pages/new/samba.tsx b/apps/web/pages/new/samba.tsx index e69de29..8849f23 100644 --- a/apps/web/pages/new/samba.tsx +++ b/apps/web/pages/new/samba.tsx @@ -0,0 +1,219 @@ +import { Bucket, ListBucketsCommandOutput } from "@aws-sdk/client-s3"; +import { + Box, + Button, + Container, + Divider, + Flex, + Heading, + IconButton, + Input, + Select, + Text, +} from "@chakra-ui/react"; +import VideoModal from "@components/ui/VideoModal"; +import useUser from "@hooks/useUser"; +import axios from "axios"; +import Head from "next/head"; +import { useRouter } from "next/router"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import { ArrowNarrowLeft } from "tabler-icons-react"; +import "video-react/dist/video-react.css"; +import validator from "validator"; + +const NewSamba = () => { + const { user } = useUser(); + const router = useRouter(); + const [loading, setLoading] = useState(false); + const [keyId, setKeyId] = useState(""); + const [applicationKey, setApplicationKey] = useState(""); + const [endpoint, setEndpoint] = useState(""); + const [bucketName, setBucketName] = useState(""); + const [buckets, setBuckets] = useState([]); + const [selectedBucket, setSelectedBucket] = useState("Not Selected"); + + const listBuckets = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + + try { + if (!user?.email) throw new Error("You need to login to perform this action!"); + + if (!keyId.trim() || !applicationKey.trim() || !endpoint.trim()) + throw new Error("One or more fields are missing!"); + + if ( + !validator.isURL(endpoint, { require_protocol: true, protocols: ["https"] }) || + !/^(https:\/\/s3\.).+(\.backblazeb2.com)$/g.test(endpoint) + ) + throw new Error("Endpoint URL does not match the required format!"); + + const { data } = await axios.post("/api/s3/list-buckets", { + accessKey: keyId, + secretKey: applicationKey, + endpoint, + region: endpoint.split(".")[1], + }); + + setBuckets(data.Buckets); + } catch (err) { + console.error(err); + toast.error(err?.response?.data?.error || err.message); + } + + setLoading(false); + }; + + const createBucket = async () => { + setLoading(true); + + try { + if (!user?.email) throw new Error("You need to login to perform this action!"); + + if (!keyId.trim() || !applicationKey.trim()) + throw new Error("One or more fields are missing!"); + + if ( + !validator.isURL(endpoint, { require_protocol: true, protocols: ["https"] }) || + !/^(https:\/\/s3\.).+(\.backblazeb2.com)$/g.test(endpoint) + ) + throw new Error("Endpoint URL does not match the required format!"); + + if ((selectedBucket === "Not Selected" && !bucketName.trim()) || !endpoint.trim()) + throw new Error("Select an existing bucket or enter a new bucket name!"); + + if ( + (selectedBucket === "Not Selected" && bucketName.trim().length < 3) || + bucketName.trim().length > 63 + ) + throw new Error("Bucket name must be between 3 and 63 characters!"); + + const Bucket = selectedBucket !== "Not Selected" ? selectedBucket : bucketName.trim(); + + await axios.post("/api/drive", { + data: { + accessKey: keyId, + secretKey: applicationKey, + Bucket, + bucketUrl: `https://${Bucket}.s3.${endpoint.split(".")[1]}.backblazeb2.com`, + endpoint, + region: endpoint.split(".")[1], + }, + name: Bucket, + type: "backblaze", + }); + + toast.success("Drive created successfully!"); + router.push("/"); + } catch (err) { + console.error(err); + toast.error(err?.response?.data?.error || err.message); + } + + setLoading(false); + }; + + return ( + <> + + Backblaze | Firefiles + + + } + mr="3" + onClick={() => router.push("/new")} + /> + + Enter your Backblaze keys + + + + + setKeyId(e.target.value)} + required + /> + setApplicationKey(e.target.value)} + required + /> + setEndpoint(e.target.value)} + required + /> + + + + {buckets?.length > 0 ? ( + <> + + + + Found {buckets.length} buckets: + + Choose a bucket: + + + OR + + Create New: + { + // Bucket name must not contain spaces or uppercase letters + const text = e.target.value.replace(" ", "").toLowerCase(); + setBucketName(text); + }} + required + /> + + + + ) : null} + + + ); +}; + +export default NewSamba; diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index da8245f..2bb6afc 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -116,6 +116,9 @@ dependencies: resend: specifier: ^0.17.1 version: 0.17.1 + samba-client: + specifier: ^7.2.0 + version: 7.2.0 sendgrid: specifier: ^5.2.3 version: 5.2.3 @@ -4335,6 +4338,15 @@ packages: which: 1.3.1 dev: false + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: false + /crypto-js@4.1.1: resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} dev: false @@ -4672,6 +4684,21 @@ packages: strip-eof: 1.0.0 dev: false + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: false + /extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -4907,6 +4934,11 @@ packages: pump: 3.0.0 dev: false + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: false + /git-config-path@1.0.1: resolution: {integrity: sha512-KcJ2dlrrP5DbBnYIZ2nlikALfRhKzNSX0stvv3ImJ+fvC4hXKoV+U+74SV0upg+jlQZbrtQzc0bu6/Zh+7aQbg==} engines: {node: '>=0.10.0'} @@ -5257,6 +5289,11 @@ packages: resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} dev: false + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: false + /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -5516,6 +5553,11 @@ packages: engines: {node: '>=0.10.0'} dev: false + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: false + /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} dev: false @@ -6462,6 +6504,13 @@ packages: path-key: 2.0.1 dev: false + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: false + /nprogress@0.2.0: resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} dev: false @@ -6651,6 +6700,11 @@ packages: engines: {node: '>=4'} dev: false + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: false + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: false @@ -7323,6 +7377,13 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: false + /samba-client@7.2.0: + resolution: {integrity: sha512-3wVq4UiHcJ3ZArsbDRsZ9W1P4fAgXU2CPjldsDuLDH3xzN7sd3MbtuNlTjc0Vkfq7+Bl4vcHqcpZEWRBMltHtw==} + engines: {node: '>=14.0.0'} + dependencies: + execa: 5.1.1 + dev: false + /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: @@ -7423,11 +7484,23 @@ packages: shebang-regex: 1.0.0 dev: false + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: false + /shebang-regex@1.0.0: resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} engines: {node: '>=0.10.0'} dev: false + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: false + /sigmund@1.0.1: resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} dev: false @@ -7554,6 +7627,11 @@ packages: engines: {node: '>=0.10.0'} dev: false + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: false + /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} From 6c23dd98be1b6a190ae5a2acf9da6d4db9019f2f Mon Sep 17 00:00:00 2001 From: faisalsayed10 Date: Sat, 19 Aug 2023 11:39:02 +0530 Subject: [PATCH 09/10] feat: Add samba to the list of providers --- apps/web/hooks/useBucket.tsx | 7 +++++-- apps/web/pages/drives/[id].tsx | 5 +++++ apps/web/public/samba.png | Bin 0 -> 36917 bytes apps/web/util/globals.ts | 13 +++---------- apps/web/util/helpers/s3-helpers.ts | 1 + apps/web/util/types.ts | 1 + 6 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 apps/web/public/samba.png diff --git a/apps/web/hooks/useBucket.tsx b/apps/web/hooks/useBucket.tsx index 5aeb853..bd86347 100644 --- a/apps/web/hooks/useBucket.tsx +++ b/apps/web/hooks/useBucket.tsx @@ -2,6 +2,7 @@ import { DriveFile, DriveFolder, Provider, Tag, UploadingFile } from "@util/type import useFirebase from "./useFirebase"; import useKeys from "./useKeys"; import useS3 from "./useS3"; +import useSamba from "./useSamba"; export type ContextValue = { loading: boolean; @@ -34,13 +35,15 @@ export default function useBucket(): ContextValue { switch (Provider[keys.type] as Provider) { case Provider.firebase: - return useFirebase(); + return useFirebase(); case Provider.s3: case Provider.backblaze: case Provider.wasabi: case Provider.digitalocean: case Provider.cloudflare: - return useS3(); + return useS3(); + case Provider.samba: + return useSamba(); default: return null; } diff --git a/apps/web/pages/drives/[id].tsx b/apps/web/pages/drives/[id].tsx index 934ff7f..8704cbc 100644 --- a/apps/web/pages/drives/[id].tsx +++ b/apps/web/pages/drives/[id].tsx @@ -2,6 +2,7 @@ import Dashboard from "@components/Dashboard"; import { FirebaseProvider } from "@hooks/useFirebase"; import { KeysProvider } from "@hooks/useKeys"; import { S3Provider } from "@hooks/useS3"; +import { SambaProvider } from "@hooks/useSamba"; import useUser from "@hooks/useUser"; import { Drive } from "@prisma/client"; import prisma from "@util/prisma"; @@ -40,6 +41,10 @@ const DrivePage: React.FC = ({ data }) => { + ) : data.type === "samba" ? ( + + + ) : (

No provider found.

)} diff --git a/apps/web/public/samba.png b/apps/web/public/samba.png new file mode 100644 index 0000000000000000000000000000000000000000..572316a67b252ee80cd04dc40174118fbc98b8c1 GIT binary patch literal 36917 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 literal 0 HcmV?d00001 diff --git a/apps/web/util/globals.ts b/apps/web/util/globals.ts index cc1083e..d9521e8 100644 --- a/apps/web/util/globals.ts +++ b/apps/web/util/globals.ts @@ -30,16 +30,9 @@ export const PROVIDERS = [ logo: "/cloudflare.png", }, { - id: "deta", - name: "Deta", - logo: "/deta.png", - isComingSoon: true, - }, - { - id: "git", - name: "Git", - logo: "/git.png", - isComingSoon: true, + id: "samba", + name: "Samba", + logo: "/samba.png", } ]; diff --git a/apps/web/util/helpers/s3-helpers.ts b/apps/web/util/helpers/s3-helpers.ts index cfcbac6..f1a4952 100644 --- a/apps/web/util/helpers/s3-helpers.ts +++ b/apps/web/util/helpers/s3-helpers.ts @@ -35,6 +35,7 @@ export const beforeCreatingDoc = async (req: NextApiRequest, res: NextApiRespons switch (type) { case "firebase": case "wasabi": + case "samba": return { success: true }; case "digitalocean": case "s3": diff --git a/apps/web/util/types.ts b/apps/web/util/types.ts index b7f2be5..7d9d0f5 100644 --- a/apps/web/util/types.ts +++ b/apps/web/util/types.ts @@ -26,6 +26,7 @@ export enum Provider { wasabi, digitalocean, cloudflare, + samba, } export type FileSortConfig = { From 2fdb51dea967a70cafe5c1d90efd2ef1b77f5711 Mon Sep 17 00:00:00 2001 From: faisalsayed10 Date: Sat, 19 Aug 2023 11:41:15 +0530 Subject: [PATCH 10/10] fix deployment --- apps/web/util/globals.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/util/globals.ts b/apps/web/util/globals.ts index d9521e8..12c8078 100644 --- a/apps/web/util/globals.ts +++ b/apps/web/util/globals.ts @@ -33,6 +33,7 @@ export const PROVIDERS = [ id: "samba", name: "Samba", logo: "/samba.png", + isComingSoon: true, } ];