27 Commits

Author SHA1 Message Date
Kshitij 17046735da Removed unused packages, updated exising packages and added missing depedencies for frontend. 2025-06-27 00:42:44 +05:30
Kshitij 5cd80fe27e Moved translation guide from Frontend/src to root dir. Also added link in README for the guide. 2025-06-27 00:35:19 +05:30
Kshitij d2e9f80c30 Merged frontend-multilingual branch. This project is now multilingual!
Currently supports the following languages:
- English
- Hindi
- Marathi
- French
2025-06-27 00:28:34 +05:30
Kshitij 43d04c7f93 Added Marathi (मराठी) translation! 2025-06-27 00:27:35 +05:30
Kshitij 1cbd74b6a5 Added Hindi (हिंदी) translation! 2025-06-27 00:25:35 +05:30
Kshitij d88f1b6e0b Added guide for contributing in other languages! 2025-06-27 00:16:06 +05:30
Kshitij 73a1c521d5 Performed translation for some components in ./src
- Footer.jsx
- Sidebar.jsx

Also, added key:value pairs (English + French for now) for all the text in src/locales. Need to perform translation for FileList.jsx & FileUploadModal.jsx. Will do once backend is accessible again.
2025-06-27 00:00:33 +05:30
Kshitij 9050bbc5cf Added .vite/ to .gitignore 2025-06-26 23:32:23 +05:30
Kshitij 2cce8d89ca Performed translation for Authentication in src/pages/
- Login.jsx
- SignUp.jsx

Also, added key:value pairs (English + French for now) for all the text in src/locales.
2025-06-26 23:29:24 +05:30
Kshitij 41435aa4fc Performed translation for UserPages in src/pages/
- Dashboard.jsx
- DrivethruLandingPage.jsx
- NotFoundPage.jsx

Also, added key:value pairs (English + French for now) for all the text in src/locales.
2025-06-26 23:20:50 +05:30
Kshitij 7dc8a49a8d Added the foundation for making the frontend multilingual. Now only need to import useTranslation and use t('key') for all user-facing text for all pages (+add key:value pairs in src/locales for each language ofc)
- package.json -> Imported packages i18next & react-i18next for multilingual functionality.
- src/App.jsx -> Imported LanguageSwitcher from ./components/LanguageSwitcher and added <LanguageSwitcher /> component at the beginning of layout so it's always visible.
- src/components/LanguageSwitcher.jsx -> LanguageSwitcher component, consists of a dropdown menu that always appears at top right corner for choosing language.
- src/i18n.js -> Initialize and configure i18next for app-wide multilingual support.
- src/locales/en.json + src/locales/fr.json -> Empty json files that will soon contain translation key:value pairs for each page.
- src/main.jsx -> Imported src/i18n.js for multilingual functionality.
2025-06-26 23:06:25 +05:30
Kshitij 816d115fbc Updated frontend port and added note to stop and remove all containers. 2025-04-21 10:41:45 +05:30
Kshitij 715f3a9d96 Updated backend submodule. 2025-04-21 10:34:27 +05:30
Kshitij 3febc68b4e fixed typo 2025-04-20 22:43:32 +05:30
Kshitij f06967708d Added hadoop env file. 2025-04-20 22:16:55 +05:30
Kshitij f79435d64f Added instructions to deploy the app. 2025-04-20 21:11:12 +05:30
Kshitij 04ac930900 Added docker-compose file. 2025-04-20 21:00:21 +05:30
Kshitij d8193f8174 Updated backend submodule. 2025-04-20 20:54:24 +05:30
Kshitij d08b0d6f90 Ref updated backend submodule. 2025-04-20 04:27:23 +05:30
Kshitij 3b027e4a39 Changed backend:8080 to localhost:8081, fixed cors issue! 2025-04-20 04:26:05 +05:30
Kshitij 49f57b5c10 Ref updated backend submodule 2025-04-20 03:57:50 +05:30
Kshitij ac75a64ec8 Added Backend as submodule to https://git.kska.io/notkshitij/SkycrateBackend.git 2025-04-20 03:41:39 +05:30
Kshitij 0e195ac079 Deleted old Backend/Skycrate submodule, planning to move it to Backend dir. directly. 2025-04-20 03:40:57 +05:30
Kshitij 6358e7e72d Updated backend url in .env and removed or operator in API URL var in Frontend/src/components/FileList.jsx 2025-04-20 02:34:54 +05:30
Kshitij 97be5d1b93 Added Dockerfile to build frontend Docker image. 2025-04-20 02:32:09 +05:30
Kshitij c42a9dacf0 Added skycrate as module in backend directory. 2025-04-19 23:34:49 +05:30
Kshitij 4b929bb272 Updated info file. 2025-04-19 23:31:29 +05:30
27 changed files with 932 additions and 134 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "Backend"]
path = Backend
url = https://git.kska.io/notkshitij/SkycrateBackend.git
+3 -2
View File
@@ -5,10 +5,11 @@
## Work distribution ## Work distribution
- Design: Kapil - Design: Kapil
- Frontend: Shivani, Shriniwas, Ombase, Tejas, Sonali, Dinesh - Frontend: Ombase, Shriniwas, Dinesh, Lalit, Shivani, Pracheta, Vaibhavi
- Backend: Vedang, Lalit - Backend: Vedang, Sonali, Lalit
- DBMS: Lalit - DBMS: Lalit
- HDFS: Sonali, Prajakta, Poonam - HDFS: Sonali, Prajakta, Poonam
- Deployment: Kshitij, Sahil
--- ---
Submodule
+1
Submodule Backend added at d3e1aff0fb
+1 -1
View File
@@ -1 +1 @@
VITE_API_URL=http://192.168.29.61:8081 VITE_API_URL=http://localhost:8081
+4
View File
@@ -23,3 +23,7 @@ package-lock.json
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
#########
.vite/
+28
View File
@@ -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
View File
@@ -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"
} }
} }
+2
View File
@@ -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 />} />
+1 -1
View File
@@ -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
+21 -20
View File
@@ -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;
+10 -8
View File
@@ -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... */}
+30
View File
@@ -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;
+94
View File
@@ -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": "Dont 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"
}
+92
View File
@@ -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"
}
+92
View File
@@ -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": "चिह्नित"
}
+92
View File
@@ -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": "आवडते"
}
+2
View File
@@ -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";
+13 -12
View File
@@ -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">
Dont 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>
+17 -17
View File
@@ -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>
+6 -2
View File
@@ -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">
+7 -11
View File
@@ -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&apos;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>
); );
+43 -15
View File
@@ -1,26 +1,54 @@
# CC-MINI (2025) # Skycrate
> [!NOTE]
> This project is now multilingual. To contribute new languages, please read the [translation guide](./TRANSLATION.md).
--- ---
## Git config ## Versions
- Hadoop: 3.4.1
- Java: 17
- Node: 22.14.0
- NPM: 10.9.2
Create a new directory for this project, and run these following commands for initalizing git: ## 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 ```shell
git clone https://github.com/kshitij-ka/cc-mini.git git clone https://git.kska.io/notkshitij/Skycrate.git
cd cc-mini
git config --local user.name "Your name"
git config --local user.email "your@ema.il"
git config --local core.autocrlf input # For Linux/MacOS users
git config --local core.autocrlf true # For Windows users
git checkout frontend # If you're working on frontend
git checkout backend # If you're working on backend
``` ```
## Where to push? 2. Change into the directory:
- For frontend, please push to [Frontend](https://github.com/kshitij-ka/cc-mini/tree/frontend/Frontend) folder in the [frontend branch](https://github.com/kshitij-ka/cc-mini/tree/frontend). ```shell
- For backend, please push to [Backend](https://github.com/kshitij-ka/cc-mini/tree/backend/Backend) folder in the [backend branch](https://github.com/kshitij-ka/cc-mini/tree/backend/). cd ./Skycrate
- I will be merging changes from both the branches in the main branch for deploying. ```
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`
--- ---
+78
View File
@@ -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!
+151
View File
@@ -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
View File
@@ -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/