Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
17046735da
|
|||
|
5cd80fe27e
|
|||
|
d2e9f80c30
|
|||
|
43d04c7f93
|
|||
|
1cbd74b6a5
|
|||
|
d88f1b6e0b
|
|||
|
73a1c521d5
|
|||
|
9050bbc5cf
|
|||
|
2cce8d89ca
|
|||
|
41435aa4fc
|
|||
|
7dc8a49a8d
|
|||
|
816d115fbc
|
|||
|
715f3a9d96
|
|||
|
3febc68b4e
|
|||
|
f06967708d
|
|||
|
f79435d64f
|
|||
|
04ac930900
|
|||
|
d8193f8174
|
|||
|
d08b0d6f90
|
|||
|
3b027e4a39
|
|||
|
49f57b5c10
|
|||
|
ac75a64ec8
|
|||
|
0e195ac079
|
|||
|
6358e7e72d
|
|||
|
97be5d1b93
|
|||
|
c42a9dacf0
|
|||
|
4b929bb272
|
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "Backend"]
|
||||||
|
path = Backend
|
||||||
|
url = https://git.kska.io/notkshitij/SkycrateBackend.git
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Info
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Work distribution
|
||||||
|
|
||||||
|
- Design: Kapil
|
||||||
|
- Frontend: Ombase, Shriniwas, Dinesh, Lalit, Shivani, Pracheta, Vaibhavi
|
||||||
|
- Backend: Vedang, Sonali, Lalit
|
||||||
|
- DBMS: Lalit
|
||||||
|
- HDFS: Sonali, Prajakta, Poonam
|
||||||
|
- Deployment: Kshitij, Sahil
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
In this mini project, we'll be creating something similar to Google Drive. There shall be 3 pages, landing, login/registration and main page where all the files uploaded by the user will be shown. Kapil is supposed to design the UI and send it over by Sunday. Based on this design, people in the frontend department shall work on the pages.
|
||||||
|
|
||||||
|
Landing page is basically a home page containing small description of the project, features etc. Login/registration page will ask for username/password. Lalit is expected to implement it using MySQL/MongoDB, i.e. he is responsible for user authentication. Once the user is authenticated, they shall be redirected to the main page where they can view their files, and upload/delete them.
|
||||||
|
|
||||||
|
Vedang is responsible to developing the backend code in Java for encrypting the uploaded files and decrypting the downloaded files. These files will be stored in Hadoop File System (HDFS) which shall be handled by Sonali, Prajakta and Poonam.
|
||||||
|
|
||||||
|
We are planning to make this a good enough project so that we can maybe open source it and make it a part of our resume. Therefore it is important that y'all work sahi se and finish your stuff by the deadlines. We are expected to finish the entire project ✨ before IN-SEM exam ✨
|
||||||
|
|
||||||
|
While you are working on the project, note down the things you are doing so that we can provide it to the people doing the documentation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
Submodule
+1
Submodule Backend added at d3e1aff0fb
+1
-1
@@ -1 +1 @@
|
|||||||
VITE_API_URL=http://192.168.29.61:8081
|
VITE_API_URL=http://localhost:8081
|
||||||
|
|||||||
@@ -23,3 +23,7 @@ package-lock.json
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
#########
|
||||||
|
|
||||||
|
.vite/
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
## FRONTEND ##
|
||||||
|
|
||||||
|
# Base image
|
||||||
|
FROM node:22
|
||||||
|
|
||||||
|
# Metadata
|
||||||
|
LABEL maintainer="kshitijka"
|
||||||
|
LABEL version=1.0
|
||||||
|
LABEL description="Skycrate is a web based file management system that uses Hadoop as filesystem."
|
||||||
|
|
||||||
|
# Update & upgrade & rm
|
||||||
|
RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/* && npm install -g http-server
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN useradd -s /bin/bash skycrateFront
|
||||||
|
|
||||||
|
# Create work dir
|
||||||
|
RUN mkdir /app
|
||||||
|
RUN chown -R skycrateFront:skycrateFront /app
|
||||||
|
COPY ./dist/ /app
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Switch user
|
||||||
|
USER skycrateFront
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["http-server", "/app"]
|
||||||
+15
-17
@@ -10,30 +10,28 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@reduxjs/toolkit": "^2.6.0",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"@tailwindcss/vite": "^4.0.9",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"axios": "^1.8.4",
|
"i18next": "^25.2.1",
|
||||||
"lucide-react": "^0.476.0",
|
"lucide-react": "^0.476.0",
|
||||||
"react": "^19.0.0",
|
"prop-types": "^15.8.1",
|
||||||
"react-dom": "^19.0.0",
|
"react": "^19.1.0",
|
||||||
|
"react-dom": "^19.1.0",
|
||||||
"react-hot-toast": "^2.5.2",
|
"react-hot-toast": "^2.5.2",
|
||||||
|
"react-i18next": "^15.5.3",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.2.0"
|
"react-router-dom": "^7.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.21.0",
|
"@eslint/js": "^9.21.0",
|
||||||
"@types/react": "^19.0.10",
|
"@types/react": "^19.1.8",
|
||||||
"@types/react-dom": "^19.0.4",
|
"@vitejs/plugin-react": "^4.6.0",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"eslint": "^9.29.0",
|
||||||
"autoprefixer": "^10.4.20",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint": "^9.21.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react": "^7.37.4",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
"eslint-plugin-react-hooks": "^5.0.0",
|
|
||||||
"eslint-plugin-react-refresh": "^0.4.19",
|
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
"postcss": "^8.5.3",
|
"vite": "^6.3.5"
|
||||||
"tailwindcss": "^4.0.9",
|
|
||||||
"vite": "^6.2.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "./App.css";
|
import "./App.css";
|
||||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
||||||
|
import LanguageSwitcher from './components/LanguageSwitcher'; // Language switcher dropdown menu
|
||||||
import Login from "./pages/Authentication/Login";
|
import Login from "./pages/Authentication/Login";
|
||||||
import SignUp from "./pages/Authentication/SignUp";
|
import SignUp from "./pages/Authentication/SignUp";
|
||||||
import DrivethruLandingPage from "./pages/UserPages/DrivethruLandingPage";
|
import DrivethruLandingPage from "./pages/UserPages/DrivethruLandingPage";
|
||||||
@@ -9,6 +10,7 @@ import NotFoundPage from "./pages/UserPages/NotFoundPage";
|
|||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
|
<LanguageSwitcher />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<DrivethruLandingPage />} />
|
<Route path="/" element={<DrivethruLandingPage />} />
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8080";
|
const API_URL = import.meta.env.VITE_API_URL;
|
||||||
|
|
||||||
const FileTable = ({ initialPath }) => {
|
const FileTable = ({ initialPath }) => {
|
||||||
// Read username dynamically to avoid stale null on first load
|
// Read username dynamically to avoid stale null on first load
|
||||||
|
|||||||
@@ -8,15 +8,17 @@ import {
|
|||||||
Phone,
|
Phone,
|
||||||
MapPin,
|
MapPin,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||||
|
|
||||||
const Footer = () => {
|
const Footer = () => {
|
||||||
|
const { t } = useTranslation(); // for multilinguality
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
|
|
||||||
//Currently storing user email in localstorage
|
//Currently storing user email in localstorage
|
||||||
const handleSubscribe = () => {
|
const handleSubscribe = () => {
|
||||||
if (email.trim() !== "") {
|
if (email.trim() !== "") {
|
||||||
localStorage.setItem("subscribedEmail", email);
|
localStorage.setItem("subscribedEmail", email);
|
||||||
alert("You have successfully subscribed!");
|
alert(t("subscribe_success"));
|
||||||
setEmail("");
|
setEmail("");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -42,10 +44,10 @@ const Footer = () => {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-2xl font-bold text-white">Skycrate</h3>
|
<h3 className="text-2xl font-bold text-white">{t("footer_brand")}</h3>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-white/90">
|
<p className="text-white/90">
|
||||||
Your secure cloud storage solution for all your digital needs.
|
{t("footer_tagline")}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex space-x-4">
|
<div className="flex space-x-4">
|
||||||
<a
|
<a
|
||||||
@@ -81,14 +83,14 @@ const Footer = () => {
|
|||||||
|
|
||||||
{/* Quick Links */}
|
{/* Quick Links */}
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-white mb-4">Quick Links</h4>
|
<h4 className="font-semibold text-white mb-4">{t("footer_quick_links")}</h4>
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="#about"
|
href="#about"
|
||||||
className="text-white/90 hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
className="text-white/90 hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||||
>
|
>
|
||||||
About Us
|
{t("footer_about_us")}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
@@ -96,7 +98,7 @@ const Footer = () => {
|
|||||||
href="#features"
|
href="#features"
|
||||||
className="text-white/90 hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
className="text-white/90 hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||||
>
|
>
|
||||||
Features
|
{t("footer_features")}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
@@ -104,7 +106,7 @@ const Footer = () => {
|
|||||||
href="#howItWorks"
|
href="#howItWorks"
|
||||||
className="text-white/90 hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
className="text-white/90 hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||||
>
|
>
|
||||||
How It Works
|
{t("footer_how_it_works")}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -112,43 +114,42 @@ const Footer = () => {
|
|||||||
|
|
||||||
{/* Contact Info */}
|
{/* Contact Info */}
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-white mb-4">Contact</h4>
|
<h4 className="font-semibold text-white mb-4">{t("footer_contact")}</h4>
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
<li className="flex items-center text-white/90 hover:text-white group transition-colors duration-200">
|
<li className="flex items-center text-white/90 hover:text-white group transition-colors duration-200">
|
||||||
<Mail className="w-4 h-4 mr-2 group-hover:text-cyan-200" />
|
<Mail className="w-4 h-4 mr-2 group-hover:text-cyan-200" />
|
||||||
support@drivethru.com
|
{t("footer_email")}
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-center text-white/90 hover:text-white group transition-colors duration-200">
|
<li className="flex items-center text-white/90 hover:text-white group transition-colors duration-200">
|
||||||
<Phone className="w-4 h-4 mr-2 group-hover:text-cyan-200" />
|
<Phone className="w-4 h-4 mr-2 group-hover:text-cyan-200" />
|
||||||
+91 3628206234
|
{t("footer_phone")}
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-center text-white/90 hover:text-white group transition-colors duration-200">
|
<li className="flex items-center text-white/90 hover:text-white group transition-colors duration-200">
|
||||||
<MapPin className="w-4 h-4 mr-2 group-hover:text-cyan-200" />
|
<MapPin className="w-4 h-4 mr-2 group-hover:text-cyan-200" />
|
||||||
123 Cloud Street, Digital City
|
{t("footer_address")}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Newsletter */}
|
{/* Newsletter */}
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-white mb-4">Stay Updated</h4>
|
<h4 className="font-semibold text-white mb-4">{t("footer_newsletter_title")}</h4>
|
||||||
<p className="text-white/90 mb-4">
|
<p className="text-white/90 mb-4">
|
||||||
Get exclusive tips, updates on new features, and special offers
|
{t("footer_newsletter_desc")}
|
||||||
directly in your inbox.
|
|
||||||
</p>
|
</p>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
placeholder="Enter your email"
|
placeholder={t("footer_newsletter_placeholder")}
|
||||||
className="w-full px-4 py-2 rounded-md bg-white/10 border border-white/20 text-white placeholder:text-white/50 focus:bg-white/20 transition-all duration-200 outline-none focus:ring-2 focus:ring-white/30"
|
className="w-full px-4 py-2 rounded-md bg-white/10 border border-white/20 text-white placeholder:text-white/50 focus:bg-white/20 transition-all duration-200 outline-none focus:ring-2 focus:ring-white/30"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleSubscribe}
|
onClick={handleSubscribe}
|
||||||
className="w-full px-4 py-2 rounded-md bg-white text-blue-600 font-medium hover:bg-opacity-90 transition-all duration-200 transform hover:scale-105"
|
className="w-full px-4 py-2 rounded-md bg-white text-blue-600 font-medium hover:bg-opacity-90 transition-all duration-200 transform hover:scale-105"
|
||||||
>
|
>
|
||||||
Subscribe to Newsletter
|
{t("footer_newsletter_button")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -158,25 +159,25 @@ const Footer = () => {
|
|||||||
|
|
||||||
{/* Bottom Section */}
|
{/* Bottom Section */}
|
||||||
<div className="flex flex-col md:flex-row justify-between items-center text-white/90 text-sm">
|
<div className="flex flex-col md:flex-row justify-between items-center text-white/90 text-sm">
|
||||||
<p>© {new Date().getFullYear()} Skycrate. All rights reserved.</p>
|
<p>© {new Date().getFullYear()} {t("footer_brand")}. {t("footer_rights")}</p>
|
||||||
<div className="flex gap-4 mt-4 md:mt-0">
|
<div className="flex gap-4 mt-4 md:mt-0">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
className="hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
className="hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||||
>
|
>
|
||||||
Privacy Policy
|
{t("footer_privacy_policy")}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
className="hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
className="hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||||
>
|
>
|
||||||
Terms of Service
|
{t("footer_terms_of_service")}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
className="hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
className="hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||||
>
|
>
|
||||||
Cookie Policy
|
{t("footer_cookie_policy")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
const languages = [
|
||||||
|
{ code: 'en', label: 'English' },
|
||||||
|
{ code: 'hi', label: 'Hindi (हिंदी)' },
|
||||||
|
{ code: 'mr', label: 'Marathi (मराठी)' },
|
||||||
|
{ code: 'fr', label: 'French (Français)' },
|
||||||
|
// Add more languages as needed
|
||||||
|
];
|
||||||
|
|
||||||
|
function LanguageSwitcher() {
|
||||||
|
const { i18n } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '1rem',
|
||||||
|
right: '1rem',
|
||||||
|
zIndex: 1000,
|
||||||
|
background: 'rgba(255,255,255,0.95)',
|
||||||
|
borderRadius: '4px',
|
||||||
|
padding: '0.25em 0.5em',
|
||||||
|
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
value={i18n.language}
|
||||||
|
onChange={e => i18n.changeLanguage(e.target.value)}
|
||||||
|
style={{
|
||||||
|
border: '1px solid #ccc',
|
||||||
|
borderRadius: '4px',
|
||||||
|
padding: '0.25em 2em 0.25em 0.5em', // More right padding for arrow
|
||||||
|
minWidth: '100px',
|
||||||
|
appearance: 'auto', // Use browser default arrow
|
||||||
|
background: '#fff',
|
||||||
|
}}
|
||||||
|
|
||||||
|
aria-label="Select language"
|
||||||
|
>
|
||||||
|
{languages.map(lang => (
|
||||||
|
<option key={lang.code} value={lang.code}>
|
||||||
|
{lang.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LanguageSwitcher;
|
||||||
|
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
import { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
|
const { t } = useTranslation(); // for multilinguality
|
||||||
const navigate = useNavigate(); // Hook for programmatic navigation
|
const navigate = useNavigate(); // Hook for programmatic navigation
|
||||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||||
const menuRef = useRef();
|
const menuRef = useRef();
|
||||||
|
|
||||||
// Show loading toast and perform logout
|
// Show loading toast and perform logout
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
const loadingToast = toast.loading("Logging out...");
|
const loadingToast = toast.loading(t("sidebar_logging_out"));
|
||||||
|
|
||||||
// Simulate a delay (for example, network request)
|
// Simulate a delay (for example, network request)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -23,7 +25,7 @@ const Sidebar = () => {
|
|||||||
|
|
||||||
// Show success toast after logout
|
// Show success toast after logout
|
||||||
toast.update(loadingToast, {
|
toast.update(loadingToast, {
|
||||||
render: "Logged out successfully!",
|
render: t("sidebar_logged_out"),
|
||||||
type: "success",
|
type: "success",
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
autoClose: 2000,
|
autoClose: 2000,
|
||||||
@@ -55,7 +57,7 @@ const Sidebar = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
className="inline-flex items-center p-2 text-lg text-white rounded-lg sm:hidden hover:bg-[#37A0EA] focus:outline-none"
|
className="inline-flex items-center p-2 text-lg text-white rounded-lg sm:hidden hover:bg-[#37A0EA] focus:outline-none"
|
||||||
>
|
>
|
||||||
<span className="sr-only">Open sidebar</span>
|
<span className="sr-only">{t("sidebar_open_sidebar")}</span>
|
||||||
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path
|
<path
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@@ -67,7 +69,7 @@ const Sidebar = () => {
|
|||||||
<Link to="/" className="flex ms-2 md:me-24">
|
<Link to="/" className="flex ms-2 md:me-24">
|
||||||
<img src="./image.png" className="h-8 me-3" alt="Skycrate Logo" />
|
<img src="./image.png" className="h-8 me-3" alt="Skycrate Logo" />
|
||||||
<span className="self-center text-xl font-semibold sm:text-2xl whitespace-nowrap">
|
<span className="self-center text-xl font-semibold sm:text-2xl whitespace-nowrap">
|
||||||
Skycrate
|
{t("sidebar_brand")}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,11 +86,11 @@ const Sidebar = () => {
|
|||||||
onClick={() => setUserMenuOpen((o) => !o)}
|
onClick={() => setUserMenuOpen((o) => !o)}
|
||||||
className="flex text-lg bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300"
|
className="flex text-lg bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300"
|
||||||
>
|
>
|
||||||
<span className="sr-only">Open user menu</span>
|
<span className="sr-only">{t("sidebar_open_user_menu")}</span>
|
||||||
<img
|
<img
|
||||||
className="w-8 h-8 rounded-full"
|
className="w-8 h-8 rounded-full"
|
||||||
src="https://flowbite.com/docs/images/people/profile-picture-5.jpg"
|
src="https://flowbite.com/docs/images/people/profile-picture-5.jpg"
|
||||||
alt="User Photo"
|
alt={t("sidebar_user_photo")}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ const Sidebar = () => {
|
|||||||
className="w-full text-left px-4 py-2 text-lg text-white hover:bg-[#37A0EA]"
|
className="w-full text-left px-4 py-2 text-lg text-white hover:bg-[#37A0EA]"
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
>
|
>
|
||||||
Log out
|
{t("sidebar_logout")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -139,7 +141,7 @@ const Sidebar = () => {
|
|||||||
>
|
>
|
||||||
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.62L12 2 9.19 8.62 2 9.24l5.46 4.73L5.82 21z" />
|
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.62L12 2 9.19 8.62 2 9.24l5.46 4.73L5.82 21z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="ms-3">Starred</span>
|
<span className="ms-3">{t("sidebar_starred")}</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
{/* ...additional sidebar items... */}
|
{/* ...additional sidebar items... */}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import i18n from 'i18next';
|
||||||
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
|
||||||
|
import en from './locales/en.json';
|
||||||
|
import hi from './locales/hi.json';
|
||||||
|
import mr from './locales/mr.json';
|
||||||
|
import fr from './locales/fr.json';
|
||||||
|
// import more languages as needed
|
||||||
|
|
||||||
|
const resources = {
|
||||||
|
en: { translation: en },
|
||||||
|
hi: { translation: hi },
|
||||||
|
mr: { translation: mr },
|
||||||
|
fr: { translation: fr },
|
||||||
|
// add other languages here
|
||||||
|
};
|
||||||
|
|
||||||
|
i18n
|
||||||
|
.use(initReactI18next)
|
||||||
|
.init({
|
||||||
|
resources,
|
||||||
|
lng: 'en', // default language
|
||||||
|
fallbackLng: 'en',
|
||||||
|
//interpolation: {
|
||||||
|
// escapeValue: false, // not needed for React
|
||||||
|
//},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"failed_to_load_files": "Failed to load files. Please try again later.",
|
||||||
|
|
||||||
|
"skycrate": "Skycrate",
|
||||||
|
"hero_subtitle": "Store, Access & Share Your Files — Anytime, Anywhere!",
|
||||||
|
"hero_desc": "A simple, secure, and fast cloud storage solution for all your files. Upload, organize, and access with ease.",
|
||||||
|
"get_started": "Get Started",
|
||||||
|
"login": "Login",
|
||||||
|
"key_features": "Key Features",
|
||||||
|
"feature_easy_upload_title": "Easy Upload & Access",
|
||||||
|
"feature_easy_upload_desc": "Drag & drop, instant access.",
|
||||||
|
"feature_secure_title": "Secure & Private",
|
||||||
|
"feature_secure_desc": "End-to-end encryption.",
|
||||||
|
"feature_sharing_title": "Seamless Sharing",
|
||||||
|
"feature_sharing_desc": "Share files with one click.",
|
||||||
|
"feature_access_anywhere_title": "Access Anywhere",
|
||||||
|
"feature_access_anywhere_desc": "Works on all devices.",
|
||||||
|
"how_it_works": "How It Works",
|
||||||
|
"how_create_account_title": "Create an account",
|
||||||
|
"how_create_account_desc": "Sign up in seconds.",
|
||||||
|
"how_upload_files_title": "Upload files",
|
||||||
|
"how_upload_files_desc": "Drag & drop or select from your device.",
|
||||||
|
"how_manage_files_title": "Manage files",
|
||||||
|
"how_manage_files_desc": "Rename, move, or delete easily.",
|
||||||
|
"how_access_anytime_title": "Access anytime",
|
||||||
|
"how_access_anytime_desc": "Open files from any device.",
|
||||||
|
|
||||||
|
"not_found_title": "Page Not Found",
|
||||||
|
"not_found_description": "Sorry, we couldn't find the page you were looking for. It may have been moved or deleted.",
|
||||||
|
"go_home": "Go Home",
|
||||||
|
|
||||||
|
"login_title": "Log in",
|
||||||
|
"email_placeholder": "Enter your email",
|
||||||
|
"password_placeholder": "Enter your password",
|
||||||
|
"forgot_password": "Forgot password?",
|
||||||
|
"logging_in": "Logging In...",
|
||||||
|
"login": "Login",
|
||||||
|
"dont_have_account": "Don’t have an account?",
|
||||||
|
"sign_up": "Sign up",
|
||||||
|
"login_successful": "Login successful!",
|
||||||
|
"login_failed": "Login failed.",
|
||||||
|
"an_error_occurred": "An error occurred. Please try again.",
|
||||||
|
"logging_in_toast": "Logging in...",
|
||||||
|
|
||||||
|
"signup_title": "Sign Up",
|
||||||
|
"first_name": "First Name",
|
||||||
|
"last_name": "Last Name",
|
||||||
|
"email_placeholder": "Enter your email",
|
||||||
|
"password_placeholder": "Enter your password",
|
||||||
|
"confirm_password_placeholder": "Confirm your password",
|
||||||
|
"signing_up": "Signing Up...",
|
||||||
|
"sign_up": "Sign Up",
|
||||||
|
"already_have_account": "Already have an account?",
|
||||||
|
"login": "Login",
|
||||||
|
"passwords_do_not_match": "Passwords do not match.",
|
||||||
|
"registering": "Registering...",
|
||||||
|
"signup_failed": "Signup failed.",
|
||||||
|
"folder_creation_failed": "Failed to create user folder.",
|
||||||
|
"signup_success": "Successfully registered and folder created!",
|
||||||
|
"an_error_occurred": "An error occurred. Please try again.",
|
||||||
|
|
||||||
|
"footer_brand": "Skycrate",
|
||||||
|
"footer_tagline": "Your secure cloud storage solution for all your digital needs.",
|
||||||
|
"footer_quick_links": "Quick Links",
|
||||||
|
"footer_about_us": "About Us",
|
||||||
|
"footer_features": "Features",
|
||||||
|
"footer_how_it_works": "How It Works",
|
||||||
|
"footer_contact": "Contact",
|
||||||
|
"footer_email": "support@drivethru.com",
|
||||||
|
"footer_phone": "+91 3628206234",
|
||||||
|
"footer_address": "123 Cloud Street, Digital City",
|
||||||
|
"footer_newsletter_title": "Stay Updated",
|
||||||
|
"footer_newsletter_desc": "Get exclusive tips, updates on new features, and special offers directly in your inbox.",
|
||||||
|
"footer_newsletter_placeholder": "Enter your email",
|
||||||
|
"footer_newsletter_button": "Subscribe to Newsletter",
|
||||||
|
"subscribe_success": "You have successfully subscribed!",
|
||||||
|
"footer_rights": "All rights reserved.",
|
||||||
|
"footer_privacy_policy": "Privacy Policy",
|
||||||
|
"footer_terms_of_service": "Terms of Service",
|
||||||
|
"footer_cookie_policy": "Cookie Policy",
|
||||||
|
|
||||||
|
"sidebar_logging_out": "Logging out...",
|
||||||
|
"sidebar_logged_out": "Logged out successfully!",
|
||||||
|
"sidebar_open_sidebar": "Open sidebar",
|
||||||
|
"sidebar_brand": "Skycrate",
|
||||||
|
"sidebar_open_user_menu": "Open user menu",
|
||||||
|
"sidebar_user_photo": "User Photo",
|
||||||
|
"sidebar_logout": "Log out",
|
||||||
|
"sidebar_starred": "Starred"
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"dashboard": "Tableau de bord",
|
||||||
|
"failed_to_load_files": "Échec du chargement des fichiers. Veuillez réessayer plus tard.",
|
||||||
|
|
||||||
|
"skycrate": "Skycrate",
|
||||||
|
"hero_subtitle": "Stockez, accédez et partagez vos fichiers — à tout moment, partout !",
|
||||||
|
"hero_desc": "Une solution de stockage cloud simple, sécurisée et rapide pour tous vos fichiers. Téléchargez, organisez et accédez facilement.",
|
||||||
|
"get_started": "Commencer",
|
||||||
|
"login": "Connexion",
|
||||||
|
"key_features": "Fonctionnalités clés",
|
||||||
|
"feature_easy_upload_title": "Téléversement et accès faciles",
|
||||||
|
"feature_easy_upload_desc": "Glissez-déposez, accès instantané.",
|
||||||
|
"feature_secure_title": "Sécurisé et privé",
|
||||||
|
"feature_secure_desc": "Chiffrement de bout en bout.",
|
||||||
|
"feature_sharing_title": "Partage sans effort",
|
||||||
|
"feature_sharing_desc": "Partagez des fichiers en un clic.",
|
||||||
|
"feature_access_anywhere_title": "Accès partout",
|
||||||
|
"feature_access_anywhere_desc": "Fonctionne sur tous les appareils.",
|
||||||
|
"how_it_works": "Comment ça marche",
|
||||||
|
"how_create_account_title": "Créer un compte",
|
||||||
|
"how_create_account_desc": "Inscrivez-vous en quelques secondes.",
|
||||||
|
"how_upload_files_title": "Téléverser des fichiers",
|
||||||
|
"how_upload_files_desc": "Glissez-déposez ou sélectionnez depuis votre appareil.",
|
||||||
|
"how_manage_files_title": "Gérer les fichiers",
|
||||||
|
"how_manage_files_desc": "Renommez, déplacez ou supprimez facilement.",
|
||||||
|
"how_access_anytime_title": "Accès à tout moment",
|
||||||
|
"how_access_anytime_desc": "Ouvrez des fichiers depuis n'importe quel appareil.",
|
||||||
|
|
||||||
|
"not_found_title": "Page non trouvée",
|
||||||
|
"not_found_description": "Désolé, nous n'avons pas pu trouver la page que vous cherchiez. Elle a peut-être été déplacée ou supprimée.",
|
||||||
|
"go_home": "Accueil",
|
||||||
|
|
||||||
|
"login_title": "Connexion",
|
||||||
|
"email_placeholder": "Entrez votre e-mail",
|
||||||
|
"password_placeholder": "Entrez votre mot de passe",
|
||||||
|
"forgot_password": "Mot de passe oublié ?",
|
||||||
|
"logging_in": "Connexion...",
|
||||||
|
"login": "Connexion",
|
||||||
|
"dont_have_account": "Vous n'avez pas de compte ?",
|
||||||
|
"sign_up": "S'inscrire",
|
||||||
|
"login_successful": "Connexion réussie !",
|
||||||
|
"login_failed": "Échec de la connexion.",
|
||||||
|
"an_error_occurred": "Une erreur s'est produite. Veuillez réessayer.",
|
||||||
|
"logging_in_toast": "Connexion en cours...",
|
||||||
|
|
||||||
|
"sign_up": "S'inscrire",
|
||||||
|
"first_name": "Prénom",
|
||||||
|
"last_name": "Nom de famille",
|
||||||
|
"email_placeholder": "Entrez votre e-mail",
|
||||||
|
"password_placeholder": "Entrez votre mot de passe",
|
||||||
|
"confirm_password_placeholder": "Confirmez votre mot de passe",
|
||||||
|
"already_have_account": "Vous avez déjà un compte ?",
|
||||||
|
"login": "Connexion",
|
||||||
|
"signing_up": "Inscription...",
|
||||||
|
"passwords_do_not_match": "Les mots de passe ne correspondent pas.",
|
||||||
|
"registering": "Enregistrement...",
|
||||||
|
"signup_failed": "Échec de l'inscription.",
|
||||||
|
"failed_create_folder": "Échec de la création du dossier utilisateur.",
|
||||||
|
"signup_success": "Inscription réussie et dossier créé !",
|
||||||
|
"an_error_occurred": "Une erreur s'est produite. Veuillez réessayer.",
|
||||||
|
|
||||||
|
"footer_brand": "Skycrate",
|
||||||
|
"footer_tagline": "Votre solution de stockage cloud sécurisée pour tous vos besoins numériques.",
|
||||||
|
"footer_quick_links": "Liens rapides",
|
||||||
|
"footer_about_us": "À propos",
|
||||||
|
"footer_features": "Fonctionnalités",
|
||||||
|
"footer_how_it_works": "Comment ça marche",
|
||||||
|
"footer_contact": "Contact",
|
||||||
|
"footer_email": "support@drivethru.com",
|
||||||
|
"footer_phone": "+91 3628206234",
|
||||||
|
"footer_address": "123 Rue du Cloud, Ville Digitale",
|
||||||
|
"footer_newsletter_title": "Restez informé",
|
||||||
|
"footer_newsletter_desc": "Recevez des conseils exclusifs, des mises à jour sur les nouvelles fonctionnalités et des offres spéciales directement dans votre boîte de réception.",
|
||||||
|
"footer_newsletter_placeholder": "Entrez votre e-mail",
|
||||||
|
"footer_newsletter_button": "S'abonner à la newsletter",
|
||||||
|
"subscribe_success": "Vous vous êtes abonné avec succès !",
|
||||||
|
"footer_rights": "Tous droits réservés.",
|
||||||
|
"footer_privacy_policy": "Politique de confidentialité",
|
||||||
|
"footer_terms_of_service": "Conditions d'utilisation",
|
||||||
|
"footer_cookie_policy": "Politique relative aux cookies",
|
||||||
|
|
||||||
|
"sidebar_logging_out": "Déconnexion...",
|
||||||
|
"sidebar_logged_out": "Déconnecté avec succès !",
|
||||||
|
"sidebar_open_sidebar": "Ouvrir la barre latérale",
|
||||||
|
"sidebar_brand": "Skycrate",
|
||||||
|
"sidebar_open_user_menu": "Ouvrir le menu utilisateur",
|
||||||
|
"sidebar_user_photo": "Photo de l'utilisateur",
|
||||||
|
"sidebar_logout": "Se déconnecter",
|
||||||
|
"sidebar_starred": "Favoris"
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"dashboard": "डैशबोर्ड",
|
||||||
|
"failed_to_load_files": "फ़ाइलें लोड करने में विफल। कृपया बाद में पुनः प्रयास करें।",
|
||||||
|
|
||||||
|
"skycrate": "Skycrate",
|
||||||
|
"hero_subtitle": "अपनी फ़ाइलें संग्रहित करें, एक्सेस करें और साझा करें — कभी भी, कहीं भी!",
|
||||||
|
"hero_desc": "आपकी सभी फ़ाइलों के लिए एक सरल, सुरक्षित और तेज़ क्लाउड स्टोरेज समाधान। अपलोड करें, व्यवस्थित करें और आसानी से एक्सेस करें।",
|
||||||
|
"get_started": "शुरू करें",
|
||||||
|
"login": "लॉगिन",
|
||||||
|
"key_features": "मुख्य विशेषताएं",
|
||||||
|
"feature_easy_upload_title": "सरल अपलोड और एक्सेस",
|
||||||
|
"feature_easy_upload_desc": "ड्रैग और ड्रॉप करें, त्वरित एक्सेस पाएं।",
|
||||||
|
"feature_secure_title": "सुरक्षित और निजी",
|
||||||
|
"feature_secure_desc": "एंड-टू-एंड एन्क्रिप्शन।",
|
||||||
|
"feature_sharing_title": "बिना रुकावट साझाकरण",
|
||||||
|
"feature_sharing_desc": "एक क्लिक में फ़ाइलें साझा करें।",
|
||||||
|
"feature_access_anywhere_title": "कहीं से भी एक्सेस करें",
|
||||||
|
"feature_access_anywhere_desc": "सभी डिवाइस पर कार्य करता है।",
|
||||||
|
|
||||||
|
"how_it_works": "यह कैसे कार्य करता है",
|
||||||
|
"how_create_account_title": "खाता बनाएं",
|
||||||
|
"how_create_account_desc": "कुछ ही सेकंड में साइन अप करें।",
|
||||||
|
"how_upload_files_title": "फ़ाइलें अपलोड करें",
|
||||||
|
"how_upload_files_desc": "ड्रैग और ड्रॉप करें या डिवाइस से चुनें।",
|
||||||
|
"how_manage_files_title": "फ़ाइलें प्रबंधित करें",
|
||||||
|
"how_manage_files_desc": "आसानी से नाम बदलें, स्थानांतरित करें या हटाएं।",
|
||||||
|
"how_access_anytime_title": "कभी भी एक्सेस करें",
|
||||||
|
"how_access_anytime_desc": "किसी भी डिवाइस से फ़ाइलें खोलें।",
|
||||||
|
|
||||||
|
"not_found_title": "पृष्ठ नहीं मिला",
|
||||||
|
"not_found_description": "क्षमा करें, हम वह पृष्ठ नहीं ढूंढ सके जिसे आप खोज रहे थे। यह हटाया गया हो सकता है या स्थानांतरित कर दिया गया हो।",
|
||||||
|
"go_home": "मुख्य पृष्ठ पर जाएं",
|
||||||
|
|
||||||
|
"login_title": "लॉग इन करें",
|
||||||
|
"email_placeholder": "अपना ईमेल दर्ज करें",
|
||||||
|
"password_placeholder": "अपना पासवर्ड दर्ज करें",
|
||||||
|
"forgot_password": "पासवर्ड भूल गए?",
|
||||||
|
"logging_in": "लॉग इन किया जा रहा है...",
|
||||||
|
"login": "लॉगिन",
|
||||||
|
"dont_have_account": "कोई खाता नहीं है?",
|
||||||
|
"sign_up": "साइन अप करें",
|
||||||
|
"login_successful": "सफलतापूर्वक लॉगिन हुआ!",
|
||||||
|
"login_failed": "लॉगिन विफल रहा।",
|
||||||
|
"an_error_occurred": "एक त्रुटि हुई। कृपया पुनः प्रयास करें।",
|
||||||
|
"logging_in_toast": "लॉग इन किया जा रहा है...",
|
||||||
|
|
||||||
|
"signup_title": "साइन अप करें",
|
||||||
|
"first_name": "पहला नाम",
|
||||||
|
"last_name": "अंतिम नाम",
|
||||||
|
"email_placeholder": "अपना ईमेल दर्ज करें",
|
||||||
|
"password_placeholder": "अपना पासवर्ड दर्ज करें",
|
||||||
|
"confirm_password_placeholder": "अपना पासवर्ड पुष्टि करें",
|
||||||
|
"signing_up": "साइन अप किया जा रहा है...",
|
||||||
|
"sign_up": "साइन अप करें",
|
||||||
|
"already_have_account": "पहले से ही खाता है?",
|
||||||
|
"login": "लॉगिन",
|
||||||
|
"passwords_do_not_match": "पासवर्ड मेल नहीं खा रहे हैं।",
|
||||||
|
"registering": "पंजीकरण किया जा रहा है...",
|
||||||
|
"signup_failed": "साइन अप विफल रहा।",
|
||||||
|
"folder_creation_failed": "यूज़र फ़ोल्डर बनाने में विफल।",
|
||||||
|
"signup_success": "सफलतापूर्वक पंजीकरण हुआ और फ़ोल्डर बनाया गया!",
|
||||||
|
"an_error_occurred": "एक त्रुटि हुई। कृपया पुनः प्रयास करें।",
|
||||||
|
|
||||||
|
"footer_brand": "Skycrate",
|
||||||
|
"footer_tagline": "आपकी सभी डिजिटल आवश्यकताओं के लिए सुरक्षित क्लाउड स्टोरेज समाधान।",
|
||||||
|
"footer_quick_links": "त्वरित लिंक",
|
||||||
|
"footer_about_us": "हमारे बारे में",
|
||||||
|
"footer_features": "विशेषताएं",
|
||||||
|
"footer_how_it_works": "यह कैसे कार्य करता है",
|
||||||
|
"footer_contact": "संपर्क करें",
|
||||||
|
"footer_email": "support@drivethru.com",
|
||||||
|
"footer_phone": "+91 3628206234",
|
||||||
|
"footer_address": "123 क्लाउड स्ट्रीट, डिजिटल सिटी",
|
||||||
|
"footer_newsletter_title": "अपडेट प्राप्त करें",
|
||||||
|
"footer_newsletter_desc": "विशेष सुझाव, नई सुविधाओं के अपडेट और ऑफ़र सीधे अपने इनबॉक्स में पाएं।",
|
||||||
|
"footer_newsletter_placeholder": "अपना ईमेल दर्ज करें",
|
||||||
|
"footer_newsletter_button": "न्यूज़लेटर की सदस्यता लें",
|
||||||
|
"subscribe_success": "आपने सफलतापूर्वक सदस्यता ली है!",
|
||||||
|
"footer_rights": "सभी अधिकार सुरक्षित।",
|
||||||
|
"footer_privacy_policy": "गोपनीयता नीति",
|
||||||
|
"footer_terms_of_service": "सेवा की शर्तें",
|
||||||
|
"footer_cookie_policy": "कुकी नीति",
|
||||||
|
|
||||||
|
"sidebar_logging_out": "लॉग आउट किया जा रहा है...",
|
||||||
|
"sidebar_logged_out": "सफलतापूर्वक लॉग आउट हुआ!",
|
||||||
|
"sidebar_open_sidebar": "साइडबार खोलें",
|
||||||
|
"sidebar_brand": "Skycrate",
|
||||||
|
"sidebar_open_user_menu": "उपयोगकर्ता मेनू खोलें",
|
||||||
|
"sidebar_user_photo": "उपयोगकर्ता फोटो",
|
||||||
|
"sidebar_logout": "लॉग आउट",
|
||||||
|
"sidebar_starred": "चिह्नित"
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"dashboard": "डॅशबोर्ड",
|
||||||
|
"failed_to_load_files": "फायली लोड करण्यात अयशस्वी. कृपया नंतर पुन्हा प्रयत्न करा.",
|
||||||
|
|
||||||
|
"skycrate": "Skycrate",
|
||||||
|
"hero_subtitle": "तुमच्या फायली साठवा, प्रवेश मिळवा आणि शेअर करा — कधीही, कुठेही!",
|
||||||
|
"hero_desc": "सर्व फायलींसाठी एक साधे, सुरक्षित आणि जलद क्लाऊड स्टोरेज सोल्यूशन. अपलोड करा, व्यवस्थापित करा आणि सहजपणे वापरा.",
|
||||||
|
"get_started": "सुरुवात करा",
|
||||||
|
"login": "लॉगिन",
|
||||||
|
"key_features": "मुख्य वैशिष्ट्ये",
|
||||||
|
"feature_easy_upload_title": "सोपे अपलोड आणि प्रवेश",
|
||||||
|
"feature_easy_upload_desc": "ड्रॅग आणि ड्रॉप, त्वरित प्रवेश.",
|
||||||
|
"feature_secure_title": "सुरक्षित आणि खाजगी",
|
||||||
|
"feature_secure_desc": "संपूर्ण एन्क्रिप्शन.",
|
||||||
|
"feature_sharing_title": "सुलभ शेअरिंग",
|
||||||
|
"feature_sharing_desc": "एक क्लिकमध्ये फायली शेअर करा.",
|
||||||
|
"feature_access_anywhere_title": "कोठूनही प्रवेश",
|
||||||
|
"feature_access_anywhere_desc": "सर्व डिव्हाइसेसवर कार्यरत.",
|
||||||
|
|
||||||
|
"how_it_works": "हे कसे कार्य करते",
|
||||||
|
"how_create_account_title": "खाते तयार करा",
|
||||||
|
"how_create_account_desc": "काही सेकंदांत साइन अप करा.",
|
||||||
|
"how_upload_files_title": "फायली अपलोड करा",
|
||||||
|
"how_upload_files_desc": "ड्रॅग आणि ड्रॉप करा किंवा तुमच्या डिव्हाइसमधून निवडा.",
|
||||||
|
"how_manage_files_title": "फायली व्यवस्थापित करा",
|
||||||
|
"how_manage_files_desc": "नाव बदला, हलवा किंवा हटवा.",
|
||||||
|
"how_access_anytime_title": "कधीही प्रवेश करा",
|
||||||
|
"how_access_anytime_desc": "कोणत्याही डिव्हाइसवरून फायली उघडा.",
|
||||||
|
|
||||||
|
"not_found_title": "पृष्ठ सापडले नाही",
|
||||||
|
"not_found_description": "क्षमस्व, तुम्ही शोधत असलेले पृष्ठ आम्हाला सापडले नाही. कदाचित ते हलवले गेले असेल किंवा हटवले गेले असेल.",
|
||||||
|
"go_home": "मुख्य पृष्ठावर जा",
|
||||||
|
|
||||||
|
"login_title": "लॉग इन करा",
|
||||||
|
"email_placeholder": "तुमचा ईमेल टाका",
|
||||||
|
"password_placeholder": "तुमचा पासवर्ड टाका",
|
||||||
|
"forgot_password": "पासवर्ड विसरलात?",
|
||||||
|
"logging_in": "लॉग इन करत आहे...",
|
||||||
|
"login": "लॉग इन",
|
||||||
|
"dont_have_account": "अजून खाते नाही?",
|
||||||
|
"sign_up": "साइन अप",
|
||||||
|
"login_successful": "यशस्वीरित्या लॉग इन झाले!",
|
||||||
|
"login_failed": "लॉग इन अयशस्वी.",
|
||||||
|
"an_error_occurred": "त्रुटी आली. कृपया पुन्हा प्रयत्न करा.",
|
||||||
|
"logging_in_toast": "लॉग इन होत आहे...",
|
||||||
|
|
||||||
|
"signup_title": "साइन अप करा",
|
||||||
|
"first_name": "पहिले नाव",
|
||||||
|
"last_name": "आडनाव",
|
||||||
|
"email_placeholder": "तुमचा ईमेल टाका",
|
||||||
|
"password_placeholder": "तुमचा पासवर्ड टाका",
|
||||||
|
"confirm_password_placeholder": "तुमचा पासवर्ड पुन्हा टाका",
|
||||||
|
"signing_up": "साइन अप करत आहे...",
|
||||||
|
"sign_up": "साइन अप",
|
||||||
|
"already_have_account": "आधीच खाते आहे?",
|
||||||
|
"login": "लॉग इन",
|
||||||
|
"passwords_do_not_match": "पासवर्ड जुळत नाहीत.",
|
||||||
|
"registering": "नोंदणी करत आहे...",
|
||||||
|
"signup_failed": "साइन अप अयशस्वी.",
|
||||||
|
"folder_creation_failed": "वापरकर्त्याची फोल्डर तयार करण्यात अयशस्वी.",
|
||||||
|
"signup_success": "यशस्वीरित्या नोंदणी झाली आणि फोल्डर तयार झाला!",
|
||||||
|
"an_error_occurred": "त्रुटी आली. कृपया पुन्हा प्रयत्न करा.",
|
||||||
|
|
||||||
|
"footer_brand": "Skycrate",
|
||||||
|
"footer_tagline": "तुमच्या सर्व डिजिटल गरजांसाठी सुरक्षित क्लाऊड स्टोरेज सोल्यूशन.",
|
||||||
|
"footer_quick_links": "त्वरित दुवे",
|
||||||
|
"footer_about_us": "आमच्याबद्दल",
|
||||||
|
"footer_features": "वैशिष्ट्ये",
|
||||||
|
"footer_how_it_works": "हे कसे कार्य करते",
|
||||||
|
"footer_contact": "संपर्क",
|
||||||
|
"footer_email": "support@drivethru.com",
|
||||||
|
"footer_phone": "+९१ ३६२८२०६२३४",
|
||||||
|
"footer_address": "१२३ क्लाऊड स्ट्रीट, डिजिटल सिटी",
|
||||||
|
"footer_newsletter_title": "अपडेट मिळवा",
|
||||||
|
"footer_newsletter_desc": "विशेष टिप्स, नवीन वैशिष्ट्यांवरील अपडेट्स आणि खास ऑफर्स तुमच्या इनबॉक्समध्ये मिळवा.",
|
||||||
|
"footer_newsletter_placeholder": "तुमचा ईमेल टाका",
|
||||||
|
"footer_newsletter_button": "न्यूजलेटरची सदस्यता घ्या",
|
||||||
|
"subscribe_success": "तुमची सदस्यता यशस्वीरित्या घेतली गेली आहे!",
|
||||||
|
"footer_rights": "सर्व हक्क राखीव.",
|
||||||
|
"footer_privacy_policy": "गोपनीयता धोरण",
|
||||||
|
"footer_terms_of_service": "सेवेच्या अटी",
|
||||||
|
"footer_cookie_policy": "कुकी धोरण",
|
||||||
|
|
||||||
|
"sidebar_logging_out": "लॉग आउट करत आहे...",
|
||||||
|
"sidebar_logged_out": "यशस्वीरित्या लॉग आउट झाले!",
|
||||||
|
"sidebar_open_sidebar": "साइडबार उघडा",
|
||||||
|
"sidebar_brand": "Skycrate",
|
||||||
|
"sidebar_open_user_menu": "वापरकर्ता मेनू उघडा",
|
||||||
|
"sidebar_user_photo": "वापरकर्त्याचा फोटो",
|
||||||
|
"sidebar_logout": "लॉग आउट",
|
||||||
|
"sidebar_starred": "आवडते"
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import './i18n'; // for multilingual functionality
|
||||||
|
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { useState, useEffect } from "react";
|
|||||||
import { FiEye, FiEyeOff } from "react-icons/fi";
|
import { FiEye, FiEyeOff } from "react-icons/fi";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import toast from "react-hot-toast"; // Import React Hot Toast
|
import toast from "react-hot-toast"; // Import React Hot Toast
|
||||||
|
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||||
|
|
||||||
const API_URL = import.meta.env.VITE_API_URL; // Using .env variable
|
const API_URL = import.meta.env.VITE_API_URL; // Using .env variable
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
|
const { t } = useTranslation(); // for multilinguality
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
@@ -28,7 +30,7 @@ const Login = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// Show loading toast
|
// Show loading toast
|
||||||
const toastId = toast.loading("Logging in...");
|
const toastId = toast.loading(t("logging_in_toast"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_URL}/api/login`, {
|
const response = await fetch(`${API_URL}/api/login`, {
|
||||||
@@ -61,18 +63,17 @@ const Login = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Show success toast
|
// Show success toast
|
||||||
toast.success("Login successful!");
|
toast.success(t("login_successful"));
|
||||||
|
|
||||||
// Redirect to Dashboard
|
// Redirect to Dashboard
|
||||||
navigate("/dashboard");
|
navigate("/dashboard");
|
||||||
} else {
|
} else {
|
||||||
// Show error toast if login fails
|
// Show error toast if login fails
|
||||||
toast.error(data.message || "Login failed.");
|
toast.error(data.message || t("login_failed"));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Dismiss the loading toast and show error
|
// Dismiss the loading toast and show error
|
||||||
toast.dismiss(toastId);
|
toast.dismiss(toastId);
|
||||||
toast.error("An error occurred. Please try again.", error);
|
toast.error(t("an_error_occurred"));
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -82,7 +83,7 @@ const Login = () => {
|
|||||||
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
|
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
|
||||||
<div className="w-full max-w-md bg-white rounded-4xl shadow-lg p-8">
|
<div className="w-full max-w-md bg-white rounded-4xl shadow-lg p-8">
|
||||||
<h1 className="text-2xl font-bold mb-6 text-gray-900 text-center">
|
<h1 className="text-2xl font-bold mb-6 text-gray-900 text-center">
|
||||||
Log in
|
{t("login_title")}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
@@ -91,7 +92,7 @@ const Login = () => {
|
|||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
placeholder="Enter your email"
|
placeholder={t("email_placeholder")}
|
||||||
className="w-full border border-gray-300 rounded-l-lg px-4 py-4 focus:outline-none focus:border-blue-500"
|
className="w-full border border-gray-300 rounded-l-lg px-4 py-4 focus:outline-none focus:border-blue-500"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
@@ -104,7 +105,7 @@ const Login = () => {
|
|||||||
<input
|
<input
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
id="password"
|
id="password"
|
||||||
placeholder="Enter your password"
|
placeholder={t("password_placeholder")}
|
||||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:border-blue-500 pr-10"
|
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:border-blue-500 pr-10"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
@@ -124,7 +125,7 @@ const Login = () => {
|
|||||||
to="#!"
|
to="#!"
|
||||||
className="text-sm text-blue-600 hover:underline inline-block"
|
className="text-sm text-blue-600 hover:underline inline-block"
|
||||||
>
|
>
|
||||||
Forgot password?
|
{t("forgot_password")}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -132,17 +133,17 @@ const Login = () => {
|
|||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full py-3 bg-gradient-to-r from-[#1877F2] to-[#0E458C] hover:from-[#0E458C] hover:to-[#1877F2] text-white font-semibold rounded-full shadow-md transition duration-300"
|
className="w-full py-3 bg-gradient-to-r from-[#1877F2] to-[#0E458C] hover:from-[#0E458C] hover:to-[#1877F2] text-white font-semibold rounded-full shadow-md transition duration-300"
|
||||||
>
|
>
|
||||||
{loading ? "Logging In..." : "Login"}
|
{loading ? t("logging_in") : t("login")}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<div className="text-center mt-6">
|
<div className="text-center mt-6">
|
||||||
<p className="text-gray-700">
|
<p className="text-gray-700">
|
||||||
Don’t have an account?{" "}
|
{t("dont_have_account")}{" "}
|
||||||
<Link
|
<Link
|
||||||
to="/signup"
|
to="/signup"
|
||||||
className="text-emerald-500 hover:underline font-medium"
|
className="text-emerald-500 hover:underline font-medium"
|
||||||
>
|
>
|
||||||
Sign up
|
{t("sign_up")}
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { useState } from "react";
|
|||||||
import { FiEye, FiEyeOff } from "react-icons/fi";
|
import { FiEye, FiEyeOff } from "react-icons/fi";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import toast, { Toaster } from "react-hot-toast";
|
import toast, { Toaster } from "react-hot-toast";
|
||||||
|
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||||
|
|
||||||
const API_URL = import.meta.env.VITE_API_URL;
|
const API_URL = import.meta.env.VITE_API_URL;
|
||||||
|
|
||||||
const SignUp = () => {
|
const SignUp = () => {
|
||||||
|
const { t } = useTranslation(); // for multilinguality
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@@ -30,12 +32,12 @@ const SignUp = () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (formData.password !== formData.confirmPassword) {
|
if (formData.password !== formData.confirmPassword) {
|
||||||
toast.error("Passwords do not match.");
|
toast.error(t("passwords_do_not_match"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const toastId = toast.loading("Registering...");
|
const toastId = toast.loading(t("registering"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1️⃣ Sign up the user
|
// 1️⃣ Sign up the user
|
||||||
@@ -52,7 +54,7 @@ const SignUp = () => {
|
|||||||
const signupData = await signupRes.json();
|
const signupData = await signupRes.json();
|
||||||
|
|
||||||
if (!signupRes.ok) {
|
if (!signupRes.ok) {
|
||||||
toast.error(signupData.message || "Signup failed.", { id: toastId });
|
toast.error(signupData.message || t("signup_failed"), { id: toastId });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,11 +68,9 @@ const SignUp = () => {
|
|||||||
|
|
||||||
if (!folderRes.ok) {
|
if (!folderRes.ok) {
|
||||||
// you might choose to roll back user creation or just notify
|
// you might choose to roll back user creation or just notify
|
||||||
toast.error("Failed to create user folder.", { id: toastId });
|
toast.error(t("failed_create_folder"), { id: toastId });
|
||||||
} else {
|
} else {
|
||||||
toast.success("Successfully registered and folder created!", {
|
toast.success(t("signup_success"), { id: toastId });
|
||||||
id: toastId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3️⃣ Redirect to login after a short delay
|
// 3️⃣ Redirect to login after a short delay
|
||||||
@@ -79,7 +79,7 @@ const SignUp = () => {
|
|||||||
}, 1500);
|
}, 1500);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast.error("An error occurred. Please try again.", { id: toastId });
|
toast.error(t("an_error_occurred"), { id: toastId });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -89,12 +89,12 @@ const SignUp = () => {
|
|||||||
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-6">
|
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-6">
|
||||||
<Toaster position="top-right" />
|
<Toaster position="top-right" />
|
||||||
<div className="w-full max-w-md bg-white rounded-2xl shadow-lg p-8">
|
<div className="w-full max-w-md bg-white rounded-2xl shadow-lg p-8">
|
||||||
<h1 className="text-2xl font-bold text-gray-900 mb-6">Sign Up</h1>
|
<h1 className="text-2xl font-bold text-gray-900 mb-6">{t("sign_up")}</h1>
|
||||||
<form className="space-y-4" onSubmit={handleSubmit}>
|
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="firstname"
|
name="firstname"
|
||||||
placeholder="First Name"
|
placeholder={t("first_name")}
|
||||||
value={formData.firstname}
|
value={formData.firstname}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
@@ -103,7 +103,7 @@ const SignUp = () => {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="lastname"
|
name="lastname"
|
||||||
placeholder="Last Name"
|
placeholder={t("last_name")}
|
||||||
value={formData.lastname}
|
value={formData.lastname}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
@@ -112,7 +112,7 @@ const SignUp = () => {
|
|||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
placeholder="Enter your email"
|
placeholder={t("email_placeholder")}
|
||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
@@ -124,7 +124,7 @@ const SignUp = () => {
|
|||||||
<input
|
<input
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Enter your password"
|
placeholder={t("password_placeholder")}
|
||||||
value={formData.password}
|
value={formData.password}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
|
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
|
||||||
@@ -144,7 +144,7 @@ const SignUp = () => {
|
|||||||
<input
|
<input
|
||||||
type={showConfirmPassword ? "text" : "password"}
|
type={showConfirmPassword ? "text" : "password"}
|
||||||
name="confirmPassword"
|
name="confirmPassword"
|
||||||
placeholder="Confirm your password"
|
placeholder={t("confirm_password_placeholder")}
|
||||||
value={formData.confirmPassword}
|
value={formData.confirmPassword}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
|
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
|
||||||
@@ -169,18 +169,18 @@ const SignUp = () => {
|
|||||||
: "bg-gradient-to-r from-[#10B981] to-[#07533A] hover:from-[#0E458C] hover:to-[#1877F2]"
|
: "bg-gradient-to-r from-[#10B981] to-[#07533A] hover:from-[#0E458C] hover:to-[#1877F2]"
|
||||||
} text-white font-semibold rounded-lg shadow-md transition duration-300`}
|
} text-white font-semibold rounded-lg shadow-md transition duration-300`}
|
||||||
>
|
>
|
||||||
{loading ? "Signing Up..." : "Sign Up"}
|
{loading ? t("signing_up") : t("sign_up")}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{/* Redirect to Login */}
|
{/* Redirect to Login */}
|
||||||
<p className="text-center mt-4 text-gray-700">
|
<p className="text-center mt-4 text-gray-700">
|
||||||
Already have an account?{" "}
|
{t("already_have_account")}{" "}
|
||||||
<Link
|
<Link
|
||||||
to="/login"
|
to="/login"
|
||||||
className="text-blue-500 hover:underline font-medium"
|
className="text-blue-500 hover:underline font-medium"
|
||||||
>
|
>
|
||||||
Login
|
{t("login")}
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||||
import Sidebar from "../../components/Sidebar";
|
import Sidebar from "../../components/Sidebar";
|
||||||
import FileList from "../../components/FileList";
|
import FileList from "../../components/FileList";
|
||||||
import FileUploadModal from "../../components/FileUploadModal";
|
import FileUploadModal from "../../components/FileUploadModal";
|
||||||
import { FiPlus } from "react-icons/fi";
|
import { FiPlus } from "react-icons/fi";
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
|
const { t } = useTranslation(); // for multilinguality
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
|
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const API_URL = import.meta.env.VITE_API_URL;
|
const API_URL = import.meta.env.VITE_API_URL;
|
||||||
|
|
||||||
const isUserLoggedIn = () => {
|
const isUserLoggedIn = () => {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
const username = localStorage.getItem("username");
|
const username = localStorage.getItem("username");
|
||||||
@@ -36,7 +39,7 @@ const Dashboard = () => {
|
|||||||
setFiles(data);
|
setFiles(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch files:", error);
|
console.error("Failed to fetch files:", error);
|
||||||
setError("Failed to load files. Please try again later.");
|
setError(t("failed_to_load_files"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,6 +49,7 @@ const Dashboard = () => {
|
|||||||
} else {
|
} else {
|
||||||
fetchFiles();
|
fetchFiles();
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -54,7 +58,7 @@ const Dashboard = () => {
|
|||||||
<div className="p-4 sm:ml-64">
|
<div className="p-4 sm:ml-64">
|
||||||
<div className="p-4 border-2 border-gray-200 border-dashed rounded-lg mt-14">
|
<div className="p-4 border-2 border-gray-200 border-dashed rounded-lg mt-14">
|
||||||
<div className="w-full flex justify-between items-center">
|
<div className="w-full flex justify-between items-center">
|
||||||
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
|
<h1 className="text-2xl font-bold mb-4">{t("dashboard")}</h1>
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsUploadModalOpen(true)}
|
onClick={() => setIsUploadModalOpen(true)}
|
||||||
className="block text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-2 text-center"
|
className="block text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-2 text-center"
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import Footer from "../../components/Footer";
|
import Footer from "../../components/Footer";
|
||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||||
|
|
||||||
const DrivethruLandingPage = () => {
|
const DrivethruLandingPage = () => {
|
||||||
|
const { t } = useTranslation(); // for multilinguality
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
{
|
{
|
||||||
title: "Easy Upload & Access",
|
title: t("feature_easy_upload_title"),
|
||||||
description: "Drag & drop, instant access.",
|
description: t("feature_easy_upload_desc"),
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -25,8 +27,8 @@ const DrivethruLandingPage = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Secure & Private",
|
title: t("feature_secure_title"),
|
||||||
description: "End-to-end encryption.",
|
description: t("feature_secure_desc"),
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
@@ -44,8 +46,8 @@ const DrivethruLandingPage = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Seamless Sharing",
|
title: t("feature_sharing_title"),
|
||||||
description: "Share files with one click.",
|
description: t("feature_sharing_desc"),
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
@@ -63,8 +65,8 @@ const DrivethruLandingPage = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Access Anywhere",
|
title: t("feature_access_anywhere_title"),
|
||||||
description: "Works on all devices.",
|
description: t("feature_access_anywhere_desc"),
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
@@ -85,8 +87,8 @@ const DrivethruLandingPage = () => {
|
|||||||
|
|
||||||
const howItWorks = [
|
const howItWorks = [
|
||||||
{
|
{
|
||||||
title: "Create an account",
|
title: t("how_create_account_title"),
|
||||||
description: "Sign up in seconds.",
|
description: t("how_create_account_desc"),
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
@@ -104,8 +106,8 @@ const DrivethruLandingPage = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Upload files",
|
title: t("how_upload_files_title"),
|
||||||
description: "Drag & drop or select from your device.",
|
description: t("how_upload_files_desc"),
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
@@ -123,10 +125,10 @@ const DrivethruLandingPage = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Manage files",
|
title: t("how_manage_files_title"),
|
||||||
description: "Rename, move, or delete easily.",
|
description: t("how_manage_files_desc"),
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -148,8 +150,8 @@ const DrivethruLandingPage = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Access anytime",
|
title: t("how_access_anytime_title"),
|
||||||
description: "Open files from any device.",
|
description: t("how_access_anytime_desc"),
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<svg
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
@@ -168,6 +170,7 @@ const DrivethruLandingPage = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
// UseEffect and handle....click function to handle set and handle the animation of features..
|
// UseEffect and handle....click function to handle set and handle the animation of features..
|
||||||
const [activeIndex, setActiveIndex] = useState(0);
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
const [isPaused, setIsPaused] = useState(false);
|
const [isPaused, setIsPaused] = useState(false);
|
||||||
@@ -234,17 +237,16 @@ const DrivethruLandingPage = () => {
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-4xl md:text-5xl font-bold text-black">
|
<h1 className="text-4xl md:text-5xl font-bold text-black">
|
||||||
Skycrate
|
{t("skycrate")}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="text-xl md:text-2xl font-bold mb-4 md:mb-6 text-black">
|
<h2 className="text-xl md:text-2xl font-bold mb-4 md:mb-6 text-black">
|
||||||
Store, Access & Share Your Files — Anytime, Anywhere!
|
{t("hero_subtitle")}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p className="text-gray-800 mb-6 md:mb-10 text-base md:text-lg">
|
<p className="text-gray-800 mb-6 md:mb-10 text-base md:text-lg">
|
||||||
A simple, secure, and fast cloud storage solution for all your
|
{t("hero_desc")}
|
||||||
files. Upload, organize, and access with ease.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Buttons */}
|
{/* Buttons */}
|
||||||
@@ -253,13 +255,13 @@ const DrivethruLandingPage = () => {
|
|||||||
to="/signup"
|
to="/signup"
|
||||||
className="bg-emerald-500 hover:bg-emerald-600 text-white font-medium rounded-full px-6 py-4 md:px-8 md:py-6 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
|
className="bg-emerald-500 hover:bg-emerald-600 text-white font-medium rounded-full px-6 py-4 md:px-8 md:py-6 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
|
||||||
>
|
>
|
||||||
Get Started
|
{t("get_started")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/login"
|
to="/login"
|
||||||
className="bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-full px-6 py-4 md:px-8 md:py-6 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
|
className="bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-full px-6 py-4 md:px-8 md:py-6 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
|
||||||
>
|
>
|
||||||
Login
|
{t("login")}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -283,7 +285,7 @@ const DrivethruLandingPage = () => {
|
|||||||
id="features"
|
id="features"
|
||||||
className="w-full max-w-5xl mx-auto p-6 sm:p-8 bg-gray-100 rounded-lg shadow-lg"
|
className="w-full max-w-5xl mx-auto p-6 sm:p-8 bg-gray-100 rounded-lg shadow-lg"
|
||||||
>
|
>
|
||||||
<h2 className="text-3xl font-bold text-center mb-8">Key Features</h2>
|
<h2 className="text-3xl font-bold text-center mb-8">{t("key_features")}</h2>
|
||||||
<div className="flex flex-col-reverse md:flex-row items-center gap-8 lg:gap-12">
|
<div className="flex flex-col-reverse md:flex-row items-center gap-8 lg:gap-12">
|
||||||
{/* Left Side - Image */}
|
{/* Left Side - Image */}
|
||||||
<div className="w-full md:w-1/2 flex justify-center">
|
<div className="w-full md:w-1/2 flex justify-center">
|
||||||
@@ -328,7 +330,7 @@ const DrivethruLandingPage = () => {
|
|||||||
id="howItWorks"
|
id="howItWorks"
|
||||||
className="w-full max-w-5xl mx-auto p-6 sm:p-8 bg-gray-100 rounded-lg shadow-lg"
|
className="w-full max-w-5xl mx-auto p-6 sm:p-8 bg-gray-100 rounded-lg shadow-lg"
|
||||||
>
|
>
|
||||||
<h2 className="text-3xl font-bold text-center mb-8">How It Works</h2>
|
<h2 className="text-3xl font-bold text-center mb-8">{t("how_it_works")}</h2>
|
||||||
<div className="flex flex-col md:flex-row items-center gap-8 lg:gap-12">
|
<div className="flex flex-col md:flex-row items-center gap-8 lg:gap-12">
|
||||||
{/* Left Side - Feature List */}
|
{/* Left Side - Feature List */}
|
||||||
<div className="w-full md:w-1/2">
|
<div className="w-full md:w-1/2">
|
||||||
|
|||||||
@@ -1,29 +1,25 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||||
|
|
||||||
const NotFoundPage = () => {
|
const NotFoundPage = () => {
|
||||||
|
const { t } = useTranslation(); // for multilinguality
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center h-screen bg-gray-100 p-4">
|
<div className="flex flex-col items-center justify-center h-screen bg-gray-100 p-4">
|
||||||
{/* Placeholder SVG - Replace this with your SVG */}
|
|
||||||
<img
|
<img
|
||||||
src="/404.png"
|
src="/404.png"
|
||||||
style={{ width: "30%", height: "auto" }}
|
style={{ width: "30%", height: "auto" }}
|
||||||
alt="404 Not Found"
|
alt="404 Not Found"
|
||||||
></img>
|
/>
|
||||||
{/* Page number and title */}
|
<h2 className="text-2xl font-bold mb-4 mt-4">{t("not_found_title")}</h2>
|
||||||
<h2 className="text-2xl font-bold mb-4 mt-4">Page Not Found</h2>
|
|
||||||
|
|
||||||
{/* Description text */}
|
|
||||||
<p className="text-center text-gray-700 mb-6">
|
<p className="text-center text-gray-700 mb-6">
|
||||||
Sorry, we couldn't find the page you were looking for. It may have
|
{t("not_found_description")}
|
||||||
been moved or deleted.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Call-to-action button */}
|
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="px-6 py-2 bg-[#1877F2] text-white rounded hover:bg-blue-600 transition duration-200"
|
className="px-6 py-2 bg-[#1877F2] text-white rounded hover:bg-blue-600 transition duration-200"
|
||||||
>
|
>
|
||||||
Go Home
|
{t("go_home")}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,54 @@
|
|||||||
# Skycrate
|
# Skycrate
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This project is now multilingual. To contribute new languages, please read the [translation guide](./TRANSLATION.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
⚠️⚠️ The `frontend` branch has been locked since all changes have been successfully merged into the `main` branch. Please create a new feature branch derived from the `main` branch and submit a pull request for your changes. ⚠️⚠️
|
## Versions
|
||||||
|
- Hadoop: 3.4.1
|
||||||
|
- Java: 17
|
||||||
|
- Node: 22.14.0
|
||||||
|
- NPM: 10.9.2
|
||||||
|
|
||||||
|
## How to run?
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> You must have [Docker](https://www.docker.com/products/docker-desktop/) and [Git](https://git-scm.com/) installed on your system.
|
||||||
|
|
||||||
|
1. Clone this repository:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git clone https://git.kska.io/notkshitij/Skycrate.git
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Change into the directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd ./Skycrate
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create a `.env` file inside the directory containing the following variables:
|
||||||
|
|
||||||
|
```env
|
||||||
|
MYSQL_PASSWORD=<set-a-strong-password>
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Please choose a strong password, since it will be used for your MySQL database.
|
||||||
|
|
||||||
|
4. Execute the Docker Compose file:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker-compose -f docker-compose.yaml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Use `-d` flag to run in detached mode.
|
||||||
|
|
||||||
|
5. Visit `localhost:8080` to enjoy using Skycrate!
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> To stop and remove all the containers, run `docker compose down`
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# TRANSLATION
|
||||||
|
|
||||||
|
This is a comprehensive guide for translation for those who wish to contribute in any language.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Add Your Language JSON File
|
||||||
|
|
||||||
|
- Go to the `Frontend/src/locales/` directory.
|
||||||
|
- Copy an existing language file (e.g., `en.json`) and rename it to your language code (e.g., `es.json` for Spanish, `de.json` for German).
|
||||||
|
- Translate all the key-value pairs in your new file.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cp Frontend/src/locales/en.json Frontend/src/locales/es.json
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"skycrate": "Skycrate",
|
||||||
|
"hero_subtitle": "Store, Access & Share Your Files — Anytime, Anywhere!",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Register the Language in `Frontend/src/i18n.js`
|
||||||
|
|
||||||
|
- Open `Frontend/src/i18n.js`.
|
||||||
|
- Import your new JSON file:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import en from './locales/en.json';
|
||||||
|
import fr from './locales/fr.json';
|
||||||
|
// import more languages as needed
|
||||||
|
import es from './locales/es.json'; // <-- Add this line
|
||||||
|
|
||||||
|
const resources = {
|
||||||
|
en: { translation: en },
|
||||||
|
fr: { translation: fr },
|
||||||
|
// add other languages here
|
||||||
|
es: { translation: es }, // <-- Add this line
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Update the Language Switcher
|
||||||
|
|
||||||
|
- Open `Frontend/src/components/LanguageSwitcher.jsx`.
|
||||||
|
- Add your language to the `languages` array:
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
const languages = [
|
||||||
|
{ code: 'en', label: 'English' },
|
||||||
|
{ code: 'fr', label: 'Français' },
|
||||||
|
// Add more languages as needed
|
||||||
|
{ code: 'es', label: 'Spanish' }, // <-- Add this line
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Test Your Translation
|
||||||
|
|
||||||
|
- Start the app.
|
||||||
|
- Use the language switcher to select your new language.
|
||||||
|
- Check all pages for missing or untranslated keys.
|
||||||
|
- If you see a key instead of a translation, add it to your JSON file.
|
||||||
|
|
||||||
|
## 5. Submit Your Contribution
|
||||||
|
|
||||||
|
- Double-check your translations for accuracy and completeness.
|
||||||
|
- Commit your changes to:
|
||||||
|
- `Frontend/src/locales/<your_language>.json`
|
||||||
|
- `Frontend/src/i18n.js`
|
||||||
|
- `Frontend/src/components/LanguageSwitcher.jsx`
|
||||||
|
- Open a pull request with a description of your contribution.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Thank you for making Skycrate accessible to more people!
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
services:
|
||||||
|
namenode:
|
||||||
|
image: kshitijka/hadoop-namenode:3.4.1
|
||||||
|
container_name: skycrate-hadoop-namenode
|
||||||
|
restart: on-failure:5
|
||||||
|
ports:
|
||||||
|
- "9870:9870" # Web UI
|
||||||
|
#- "9000:9000" # Hadoop; No need to expose since backend will access internally
|
||||||
|
user: "hdoop:hdoop"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
networks:
|
||||||
|
- skycrate-internal
|
||||||
|
volumes:
|
||||||
|
- skycrate-hadoop_namenode:/hadoop/dfs/name
|
||||||
|
environment:
|
||||||
|
- CLUSTER_NAME=skycreate
|
||||||
|
env_file:
|
||||||
|
- ./hadoop.env
|
||||||
|
|
||||||
|
datanode:
|
||||||
|
image: kshitijka/hadoop-datanode:3.4.1
|
||||||
|
container_name: skycrate-hadoop-datanode-1
|
||||||
|
restart: on-failure:5
|
||||||
|
user: "hdoop:hdoop"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
networks:
|
||||||
|
- skycrate-internal
|
||||||
|
volumes:
|
||||||
|
- skycrate-hadoop_datanode:/hadoop/dfs/data
|
||||||
|
environment:
|
||||||
|
SERVICE_PRECONDITION: "namenode:9870"
|
||||||
|
env_file:
|
||||||
|
- ./hadoop.env
|
||||||
|
# healthcheck:
|
||||||
|
# disable: true
|
||||||
|
|
||||||
|
resourcemanager:
|
||||||
|
image: kshitijka/hadoop-resourcemanager:3.4.1
|
||||||
|
container_name: skycrate-hadoop-resourcemanager
|
||||||
|
restart: on-failure:10
|
||||||
|
user: "hdoop:hdoop"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
networks:
|
||||||
|
- skycrate-internal
|
||||||
|
environment:
|
||||||
|
SERVICE_PRECONDITION: "namenode:9000 namenode:9870 datanode:9864"
|
||||||
|
env_file:
|
||||||
|
- ./hadoop.env
|
||||||
|
# healthcheck:
|
||||||
|
# disable: true
|
||||||
|
|
||||||
|
nodemanager:
|
||||||
|
image: kshitijka/hadoop-nodemanager:3.4.1
|
||||||
|
container_name: skycrate-hadoop-nodemanager
|
||||||
|
restart: on-failure:5
|
||||||
|
user: "hdoop:hdoop"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
networks:
|
||||||
|
- skycrate-internal
|
||||||
|
environment:
|
||||||
|
SERVICE_PRECONDITION: "namenode:9000 namenode:9870 datanode:9864 resourcemanager:8088"
|
||||||
|
env_file:
|
||||||
|
- ./hadoop.env
|
||||||
|
# healthcheck:
|
||||||
|
# disable: true
|
||||||
|
|
||||||
|
historyserver:
|
||||||
|
image: kshitijka/hadoop-historyserver:3.4.1
|
||||||
|
container_name: skycrate-hadoop-historyserver
|
||||||
|
restart: on-failure:5
|
||||||
|
user: "hdoop:hdoop"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
networks:
|
||||||
|
- skycrate-internal
|
||||||
|
environment:
|
||||||
|
SERVICE_PRECONDITION: "namenode:9000 namenode:9870 datanode:9864 resourcemanager:8088"
|
||||||
|
volumes:
|
||||||
|
- skycrate-hadoop_historyserver:/hadoop/yarn/timeline
|
||||||
|
env_file:
|
||||||
|
- ./hadoop.env
|
||||||
|
# healthcheck:
|
||||||
|
# disable: true
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: mysql:8
|
||||||
|
container_name: skycrate-db
|
||||||
|
restart: on-failure:5
|
||||||
|
user: "1000:1000"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
networks:
|
||||||
|
- skycrate-internal
|
||||||
|
environment:
|
||||||
|
- MYSQL_DATABASE=skycrate
|
||||||
|
- MYSQL_USER=skycrateDB
|
||||||
|
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
|
||||||
|
- MYSQL_RANDOM_ROOT_PASSWORD=yes
|
||||||
|
volumes:
|
||||||
|
- skycrate-db:/var/lib/mysql
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: kshitijka/skycrate-frontend:1.0
|
||||||
|
container_name: skycrate-frontend
|
||||||
|
restart: on-failure:5
|
||||||
|
user: "skycrateFront:skycrateFront"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
networks:
|
||||||
|
- skycrate-internal
|
||||||
|
ports:
|
||||||
|
- "80:8080"
|
||||||
|
volumes:
|
||||||
|
- skycrate-frontend:/app
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
backend:
|
||||||
|
image: kshitijka/skycrate-backend:1.0
|
||||||
|
container_name: skycrate-backend
|
||||||
|
restart: on-failure:5
|
||||||
|
user: "skycrateBack:skycrateBack"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
networks:
|
||||||
|
- skycrate-internal
|
||||||
|
ports:
|
||||||
|
- "8081:8081" # If you change, update in Frontend/.env file too and rebuild the image
|
||||||
|
environment:
|
||||||
|
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- skycrate-backend:/app
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
skycrate-hadoop_namenode:
|
||||||
|
skycrate-hadoop_datanode:
|
||||||
|
skycrate-hadoop_historyserver:
|
||||||
|
skycrate-db:
|
||||||
|
skycrate-frontend:
|
||||||
|
skycrate-backend:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
skycrate-internal:
|
||||||
|
external: false
|
||||||
|
driver: bridge
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
CORE_CONF_fs_defaultFS=hdfs://namenode:9000
|
||||||
|
CORE_CONF_hadoop_http_staticuser_user=root
|
||||||
|
CORE_CONF_hadoop_proxyuser_hue_hosts=*
|
||||||
|
CORE_CONF_hadoop_proxyuser_hue_groups=*
|
||||||
|
CORE_CONF_io_compression_codecs=org.apache.hadoop.io.compress.SnappyCodec
|
||||||
|
|
||||||
|
HDFS_CONF_dfs_webhdfs_enabled=true
|
||||||
|
HDFS_CONF_dfs_permissions_enabled=false
|
||||||
|
HDFS_CONF_dfs_namenode_datanode_registration_ip___hostname___check=false
|
||||||
|
|
||||||
|
YARN_CONF_yarn_log___aggregation___enable=true
|
||||||
|
YARN_CONF_yarn_log_server_url=http://historyserver:8188/applicationhistory/logs/
|
||||||
|
YARN_CONF_yarn_resourcemanager_recovery_enabled=true
|
||||||
|
YARN_CONF_yarn_resourcemanager_store_class=org.apache.hadoop.yarn.server.resourcemanager.recovery.FileSystemRMStateStore
|
||||||
|
YARN_CONF_yarn_resourcemanager_scheduler_class=org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler
|
||||||
|
YARN_CONF_yarn_scheduler_capacity_root_default_maximum___allocation___mb=8192
|
||||||
|
YARN_CONF_yarn_scheduler_capacity_root_default_maximum___allocation___vcores=4
|
||||||
|
YARN_CONF_yarn_resourcemanager_fs_state___store_uri=/rmstate
|
||||||
|
YARN_CONF_yarn_resourcemanager_system___metrics___publisher_enabled=true
|
||||||
|
YARN_CONF_yarn_resourcemanager_hostname=resourcemanager
|
||||||
|
YARN_CONF_yarn_resourcemanager_address=resourcemanager:8032
|
||||||
|
YARN_CONF_yarn_resourcemanager_scheduler_address=resourcemanager:8030
|
||||||
|
YARN_CONF_yarn_resourcemanager_resource__tracker_address=resourcemanager:8031
|
||||||
|
YARN_CONF_yarn_timeline___service_enabled=true
|
||||||
|
YARN_CONF_yarn_timeline___service_generic___application___history_enabled=true
|
||||||
|
YARN_CONF_yarn_timeline___service_hostname=historyserver
|
||||||
|
YARN_CONF_mapreduce_map_output_compress=true
|
||||||
|
YARN_CONF_mapred_map_output_compress_codec=org.apache.hadoop.io.compress.SnappyCodec
|
||||||
|
YARN_CONF_yarn_nodemanager_resource_memory___mb=16384
|
||||||
|
YARN_CONF_yarn_nodemanager_resource_cpu___vcores=8
|
||||||
|
YARN_CONF_yarn_nodemanager_disk___health___checker_max___disk___utilization___per___disk___percentage=98.5
|
||||||
|
YARN_CONF_yarn_nodemanager_remote___app___log___dir=/app-logs
|
||||||
|
YARN_CONF_yarn_nodemanager_aux___services=mapreduce_shuffle
|
||||||
|
|
||||||
|
MAPRED_CONF_mapreduce_framework_name=yarn
|
||||||
|
MAPRED_CONF_mapred_child_java_opts=-Xmx4096m
|
||||||
|
MAPRED_CONF_mapreduce_map_memory_mb=4096
|
||||||
|
MAPRED_CONF_mapreduce_reduce_memory_mb=8192
|
||||||
|
MAPRED_CONF_mapreduce_map_java_opts=-Xmx3072m
|
||||||
|
MAPRED_CONF_mapreduce_reduce_java_opts=-Xmx6144m
|
||||||
|
MAPRED_CONF_yarn_app_mapreduce_am_env=HADOOP_MAPRED_HOME=/opt/hadoop-3.4.1/
|
||||||
|
MAPRED_CONF_mapreduce_map_env=HADOOP_MAPRED_HOME=/opt/hadoop-3.4.1/
|
||||||
|
MAPRED_CONF_mapreduce_reduce_env=HADOOP_MAPRED_HOME=/opt/hadoop-3.4.1/
|
||||||
Reference in New Issue
Block a user