-
-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #64 from nghiapham1026/main
- Loading branch information
Showing
15 changed files
with
686 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Select } from "@chakra-ui/react"; | ||
import React from "react"; | ||
|
||
const regions = [ | ||
{ name: "US East 1 (N. Virginia)", code: "us-east-1" }, | ||
{ name: "US East 2 (N. Virginia)", code: "us-east-2" }, | ||
{ name: "US Central 1 (Texas)", code: "us-central-1" }, | ||
{ name: "US West 1 (Oregon)", code: "us-west-1" }, | ||
{ name: "CA Central 1 (Toronto)", code: "ca-central-1" }, | ||
{ name: "EU Central 1 (Amsterdam)", code: "eu-central-1" }, | ||
{ name: "EU Central 2 (Frankfurt)", code: "eu-central-2" }, | ||
{ name: "EU West 1 (London)", code: "eu-west-1" }, | ||
{ name: "EU West 2 (Paris)", code: "eu-west-2" }, | ||
{ name: "AP Northeast 1 (Tokyo)", code: "ap-northeast-1" }, | ||
{ name: "AP Northeast 2 (Osaka)", code: "ap-Northeast-2" }, | ||
{ name: "AP Southeast 1 (Singapore)", code: "ap-southeast-1" }, | ||
{ name: "AP Southeast 2 (Sydney)", code: "ap-southeast-2" }, | ||
]; | ||
|
||
type Props = { | ||
value: string; | ||
onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void; | ||
}; | ||
|
||
const WasabiRegionSelect: React.FC<Props> = ({ value, onChange }) => { | ||
return ( | ||
<Select | ||
placeholder="Select Region" | ||
variant="flushed" | ||
value={value} | ||
onChange={onChange} | ||
isRequired | ||
> | ||
{regions.map((region) => ( | ||
<option key={region.code} value={region.code}> | ||
{region.name} - {region.code} | ||
</option> | ||
))} | ||
</Select> | ||
); | ||
}; | ||
|
||
export default WasabiRegionSelect; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
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"; | ||
|
||
const NewS3 = () => { | ||
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<Bucket[]>([]); | ||
const [selectedBucket, setSelectedBucket] = useState("Not Selected"); | ||
|
||
const listBuckets = async (e: React.FormEvent<HTMLDivElement>) => { | ||
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!"); | ||
|
||
const { data } = await axios.post<ListBucketsCommandOutput>("/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 ((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}.${endpoint.split(".")[1]}.digitaloceanspaces.com`, | ||
endpoint, | ||
region: endpoint.split(".")[1], | ||
}, | ||
name: Bucket, | ||
type: "digitalocean", | ||
}); | ||
|
||
toast.success("Drive created successfully!"); | ||
router.push("/"); | ||
} catch (err) { | ||
console.error(err); | ||
toast.error(err?.response?.data?.error || err.message); | ||
} | ||
|
||
setLoading(false); | ||
}; | ||
|
||
return ( | ||
<> | ||
<Head> | ||
<title>Digital Ocean | Firefiles</title> | ||
</Head> | ||
<Flex px="16px" pt="3"> | ||
<IconButton | ||
variant="ghost" | ||
aria-label="back" | ||
icon={<ArrowNarrowLeft />} | ||
mr="3" | ||
onClick={() => router.push("/new")} | ||
/> | ||
<Heading as="h3" size="lg"> | ||
Enter your Digital Ocean keys | ||
</Heading> | ||
</Flex> | ||
<Container display="flex" minH="90vh" flexDir="column" justifyContent="center" maxW="lg"> | ||
<Flex as="form" onSubmit={listBuckets} flexDir="column" w="full"> | ||
<Input | ||
mb="2" | ||
variant="flushed" | ||
placeholder="Access Key ID" | ||
type="text" | ||
value={keyId} | ||
onChange={(e) => setKeyId(e.target.value)} | ||
required | ||
/> | ||
<Input | ||
mb="2" | ||
variant="flushed" | ||
placeholder="Secret Key" | ||
type="text" | ||
value={applicationKey} | ||
onChange={(e) => setApplicationKey(e.target.value)} | ||
required | ||
/> | ||
<Input | ||
mb="2" | ||
variant="flushed" | ||
placeholder="Endpoint - https://<your-region>.digitaloceanspaces.com" | ||
type="text" | ||
value={endpoint} | ||
onChange={(e) => setEndpoint(e.target.value)} | ||
required | ||
/> | ||
<VideoModal src="/digital-ocean-keys-tutorial.mov" /> | ||
<Button type="submit" isLoading={loading} colorScheme="green" variant="solid"> | ||
Next | ||
</Button> | ||
</Flex> | ||
{buckets?.length > 0 ? ( | ||
<> | ||
<Divider my="6" /> | ||
<Box> | ||
<Heading as="h4" size="md" mb="2"> | ||
Found {buckets.length} buckets: | ||
</Heading> | ||
<Text fontSize="sm">Choose a bucket:</Text> | ||
<Select value={selectedBucket} onChange={(e) => setSelectedBucket(e.target.value)}> | ||
<option>Not Selected</option> | ||
{buckets.map((bucket) => ( | ||
<option key={bucket.CreationDate.toString()} value={bucket.Name}> | ||
{bucket.Name} | ||
</option> | ||
))} | ||
</Select> | ||
<Text fontSize="lg" align="center" my="2"> | ||
OR | ||
</Text> | ||
<Text fontSize="sm">Create New:</Text> | ||
<Input | ||
mb="2" | ||
variant="flushed" | ||
placeholder="Bucket Name" | ||
type="text" | ||
value={bucketName} | ||
onChange={(e) => { | ||
// Bucket name must not contain spaces or uppercase letters | ||
const text = e.target.value.replace(" ", "").toLowerCase(); | ||
setBucketName(text); | ||
}} | ||
required | ||
/> | ||
<Button | ||
mt="2" | ||
w="full" | ||
isLoading={loading} | ||
onClick={createBucket} | ||
colorScheme="green" | ||
variant="solid" | ||
> | ||
Create | ||
</Button> | ||
</Box> | ||
</> | ||
) : null} | ||
</Container> | ||
</> | ||
); | ||
}; | ||
|
||
export default NewS3; |
Oops, something went wrong.
7452e88
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
firefiles-landing – ./apps/landing
firefiles.vercel.app
firefiles-landing-fayd.vercel.app
firefiles-landing-git-main-fayd.vercel.app
firefiles-landing.vercel.app
firefiles.app
www.firefiles.app
7452e88
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
firefiles – ./apps/web
usefirefiles.vercel.app
firefiles-git-main-fayd.vercel.app
firefiles-fayd.vercel.app
beta.firefiles.app