Compare commits

...

3 الالتزامات

المؤلف SHA1 الرسالة التاريخ
c2a0dc426c Working 2025-12-01 15:54:00 +03:00
7ae3010b7f no reset password
فشلت بعض الفحوصات
Docker Build and Push (Development) / build-and-push-dev (push) Has been cancelled
Release Please / release-please (push) Has been cancelled
2025-11-25 23:19:11 +03:00
e94d92bf3b added umami 2025-11-17 17:21:43 +03:00
15 ملفات معدلة مع 232 إضافات و68 حذوفات

عرض الملف

@@ -1,23 +1,12 @@
.git/
.gitignore
.dockerignore
docker-compose*
Dockerfile
makefile
htmlcov/
coverage.xml
.coverage*
.vscode/
*.dat
.DS_Store
node_modules
/build
/package
.git
.gitignore
README.md
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.idea
.npm
.cache
dist
*.log
.DS_Store
coverage
.nyc_output

عرض الملف

@@ -9,7 +9,7 @@ AWS.config.update({
const s3 = new AWS.S3({
endpoint: "https://s3-16cd54cc.hosted.cumin.dev", // Example for DigitalOcean Spaces
endpoint: "https://s3.eu-central-003.backblazeb2.com", // Example for DigitalOcean Spaces
s3ForcePathStyle: true, //s Often needed for S3-compatible services
});

عرض الملف

@@ -74,8 +74,13 @@ app.use(middlewareErrorHandler);
//console.log("Node Enviroment Mode:", nodeMode);
if (process.env.NODE_ENV === "production") {
app.get("*", (_: Request, res: Response) => {
res.sendFile(path.join(publicPath, "index.html"));
// Serve React app for specific client routes
const clientRoutes = ['/', '/home', '/login', '/register', '/forgot-password', '/settings'];
clientRoutes.forEach(route => {
app.get(route, (_: Request, res: Response) => {
res.sendFile(path.join(publicPath, "index.html"));
});
});
}

عرض الملف

@@ -3,7 +3,7 @@
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "CommonJS" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
"module": "node16" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
// "lib": [], /* Specify library files to be included in the compilation. */
"allowJs": true /* Allow javascript files to be compiled. */,
// "checkJs": true, /* Report errors in .js files. */
@@ -38,7 +38,7 @@
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
"moduleResolution": "node16" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */

47
debug-resend.ts Normal file
عرض الملف

@@ -0,0 +1,47 @@
// debug-resend.js
require('dotenv').config();
const { Resend } = require('resend');
console.log('🔍 Debugging Resend...');
// Check environment
console.log('RESEND_API_KEY exists:', !!process.env.RESEND_API_KEY);
console.log('RESEND_API_KEY starts with:', process.env.RESEND_API_KEY ? process.env.RESEND_API_KEY.substring(0, 3) : 'N/A');
console.log('FROM_EMAIL:', process.env.FROM_EMAIL);
if (!process.env.RESEND_API_KEY) {
console.log('❌ RESEND_API_KEY is missing!');
process.exit(1);
}
const resend = new Resend(process.env.RESEND_API_KEY);
async function test() {
try {
console.log('\n🚀 Testing Resend API...');
const { data, error } = await resend.emails.send({
from: process.env.FROM_EMAIL || 'myDrive <onboarding@resend.dev>',
to: 'hamodeh.mnzk@gmail.com',
subject: 'Resend Test',
text: 'This is a test email from Resend.',
});
if (error) {
console.log('❌ Resend API Error:');
console.log('Error name:', error.name);
console.log('Error message:', error.message);
console.log('Error status:', error.statusCode);
return;
}
console.log('✅ Email sent!');
console.log('Email ID:', data.id);
} catch (err) {
console.log('❌ Unexpected error:');
console.log(err.message);
}
}
test();

عرض الملف

@@ -2,14 +2,17 @@
<html>
<head>
<title>myDrive</title>
<title>FlowSync | Drive</title>
<link rel="icon" href="/images/icon.png" />
<link rel="shortcut icon" type="image/png" href="/images/icon.png" />
<link rel="shortcut icon" sizes="192x192" href="/images/icon.png" />
<link rel="apple-touch-icon" href="/images/icon.png" />
<link rel="shortcut icon" type="image/png" href="/public/images/favicon-32x32.png" />
<link rel="shortcut icon" sizes="192x192" href="/public/images/favicon-32x32.png" />
<link rel="apple-touch-icon" href="/public/images/favicon-32x32.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script defer src="https://cloud.umami.is/script.js" data-website-id="ce3be986-8a39-489c-8e77-c77c253acbd4"></script>
</head>
<body>

عرض الملف

@@ -27,7 +27,7 @@
"aws-sdk": "^2.657.0",
"axios": "^1.7.2",
"bcryptjs": "^3.0.2",
"body-parser": "^1.20.2",
"body-parser": "^2.2.1",
"bytes": "^3.1.0",
"classnames": "^2.5.1",
"cli-progress": "^3.6.0",
@@ -40,7 +40,7 @@
"cors": "^2.8.5",
"dayjs": "^1.11.13",
"dotenv": "^8.2.0",
"express": "^4.19.2",
"express": "^5.1.0",
"express-validator": "^7.1.0",
"fluent-ffmpeg": "^2.1.3",
"helmet": "^3.21.2",
@@ -49,7 +49,7 @@
"lodash.debounce": "^4.0.8",
"mongodb": "^6.20.0",
"mongoose": "^8.4.1",
"nodemailer": "^6.9.14",
"nodemailer": "^7.0.10",
"normalize.css": "^8.0.1",
"password-prompt": "^1.1.2",
"progress-stream": "^2.0.0",
@@ -64,6 +64,7 @@
"react-toastify": "^10.0.5",
"redux": "^5.0.1",
"regenerator-runtime": "^0.13.3",
"resend": "^6.5.2",
"sharp": "^0.33.4",
"sweetalert2": "^11.15.10",
"temp": "^0.9.1",
@@ -88,6 +89,7 @@
"@types/connect-busboy": "0.0.2",
"@types/cookie-parser": "^1.4.2",
"@types/cors": "^2.8.17",
"@types/dotenv": "^6.1.1",
"@types/express": "^4.17.21",
"@types/fluent-ffmpeg": "^2.1.24",
"@types/helmet": "0.0.45",
@@ -128,7 +130,7 @@
"tailwindcss": "^3.4.4",
"typescript": "^5.4.5",
"typescript-eslint": "^7.14.1",
"vite": "^5.2.13",
"vite-plugin-pwa": "^0.21.1"
"vite": "^6.0.0",
"vite-plugin-pwa": "^1.1.0"
}
}

ثنائية
public/images/favicon-32x32.png Normal file

ملف ثنائي غير معروض.

بعد

العرض:  |  الارتفاع:  |  الحجم: 785 B

ثنائية
public/images/icon.png

ملف ثنائي غير معروض.

قبل

العرض:  |  الارتفاع:  |  الحجم: 6.3 KiB

عرض الملف

@@ -4,7 +4,7 @@ import axios from "../axiosInterceptor";
export const getUserToken = async () => {
const response = await axios.post("/user-service/get-token");
response.data;
return response.data; // FIXED: Added return
};
export const getUserAPI = async () => {
@@ -54,6 +54,12 @@ export const getAccessToken = async (uuid: string) => {
return response.data;
};
export const sendPasswordResetAPI = async (email: string) => {
const response = await axios.post("/api/user-service/send-password-reset", {
email,
});
return response.data;
};
// PATCH
export const changePasswordAPI = async (
@@ -79,13 +85,6 @@ export const verifyEmailAPI = async (emailToken: string) => {
return response.data;
};
export const sendPasswordResetAPI = async (email: string) => {
const response = await axios.patch("/user-service/send-password-reset", {
email,
});
return response.data;
};
export const resetPasswordAPI = async (
password: string,
passwordToken: string
@@ -95,4 +94,4 @@ export const resetPasswordAPI = async (
password,
});
return response.data;
};
};

عرض الملف

@@ -0,0 +1,116 @@
import React, { useState } from 'react';
import { toast, ToastContainer } from 'react-toastify';
import { sendPasswordResetAPI } from '../../api/userAPI';
import { useNavigate } from 'react-router-dom';
import Spinner from '../Spinner/Spinner';
const ForgotPasswordPage = () => {
const [email, setEmail] = useState('');
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
console.log('🔄 Form submitted with email:', email);
if (!email) {
toast.error('Please enter your email');
return;
}
setLoading(true);
try {
console.log('📞 Calling sendPasswordResetAPI...');
await sendPasswordResetAPI(email);
console.log('✅ API call completed');
toast.success('If an account exists, a password reset email has been sent.');
setTimeout(() => {
navigate('/login');
}, 3000);
} catch (error: unknown) {
console.error('💥 Forgot password error:', error);
// Still show success message for security
toast.success('If an account exists, a password reset email has been sent.');
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
<div className="w-full max-w-md">
{/* Logo Section with Infinity Symbol */}
<div className="flex justify-center mb-6">
<div className="flex items-center justify-center w-16 h-16 rounded-2xl bg-gradient-to-br from-blue-500 to-blue-600 shadow-lg">
{!loading ? (
<svg
width="32"
height="32"
viewBox="0 0 24 24"
fill="none"
stroke="white"
strokeWidth="2"
>
{/* Infinity Symbol */}
<path
d="M18.178 8c5.096 0 5.096 8 0 8-5.095 0-7.133-8-12.228-8-5.095 0-5.095 8 0 8 5.095 0 7.133-8 12.228-8z"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
) : (
<Spinner />
)}
</div>
</div>
{/* Card Container */}
<div className="bg-white rounded-2xl shadow-xl p-8">
<h2 className="text-2xl font-bold text-gray-800 text-center mb-3">
Forgot Password
</h2>
<p className="text-gray-600 text-center mb-6">
Enter your email address and we'll send you a link to reset your password.
</p>
<form onSubmit={handleSubmit} className="space-y-4">
<input
type="email"
placeholder="Enter your email address"
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
disabled={loading}
/>
<button
type="submit"
disabled={loading || !email}
className="w-full bg-blue-500 hover:bg-blue-600 disabled:bg-blue-300 text-white font-medium py-3 px-4 rounded-lg transition-colors disabled:cursor-not-allowed"
>
{loading ? 'Sending Reset Link...' : 'Send Reset Link'}
</button>
</form>
<div className="text-center mt-6">
<button
onClick={() => navigate('/login')}
className="text-blue-500 hover:text-blue-600 font-medium text-sm transition-colors"
disabled={loading}
>
Back to Login
</button>
</div>
</div>
</div>
<ToastContainer position="bottom-left" pauseOnFocusLoss={false} />
</div>
);
};
export default ForgotPasswordPage;

عرض الملف

@@ -9,7 +9,7 @@ const LandingPage = () => {
<div className="landing-page">
{/* Header */}
<div className="header">
<div className="logo">🌊 FlowSync</div>
<div className="logo"> FlowSync</div>
<div className="header-buttons">
<button className="btn-secondary" onClick={() => navigate('/login')}>Sign In</button>
<button className="btn-primary" onClick={() => navigate('/register')}>Get Started</button>
@@ -516,8 +516,7 @@ const LandingPage = () => {
<div className="footer">
<div className="footer-content">
<div className="footer-brand">
<div className="logo">🌊 FlowSpace</div>
<p>Innovating the future of digital storage and collaboration</p>
<div className="logo"> FlowSync</div> <p>Innovating the future of digital storage and collaboration</p>
<p className="footer-tagline">Secure. Simple. Seamless.</p>
</div>
<div className="footer-links">

عرض الملف

@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState, useCallback ,FormEvent } from "react";
import {
createAccountAPI,
getUserAPI,
@@ -16,6 +16,8 @@ import Swal from "sweetalert2";
import { AxiosError } from "axios";
import isEmail from "validator/es/lib/isEmail";
// ADD THE defaultView PROP HERE
const LoginPage = ({ defaultView = "login" }) => {
const [email, setEmail] = useState("");
@@ -30,7 +32,7 @@ const LoginPage = ({ defaultView = "login" }) => {
const navigate = useNavigate();
const lastSentPassowordReset = useRef(0);
const attemptLoginWithToken = async () => {
const attemptLoginWithToken = useCallback(async () => {
setAttemptingLogin(true);
try {
@@ -47,7 +49,7 @@ const LoginPage = ({ defaultView = "login" }) => {
window.localStorage.removeItem("hasPreviouslyLoggedIn");
}
}
};
}, [location.state?.from?.pathname, dispatch, navigate]);
const login = async () => {
try {
@@ -147,16 +149,16 @@ const LoginPage = ({ defaultView = "login" }) => {
}
})();
const onSubmit = (e: any) => {
e.preventDefault();
if (mode === "login") {
login();
} else if (mode === "create") {
createAccount();
} else if (mode === "reset") {
resetPassword();
}
};
const onSubmit = (e: FormEvent) => {
e.preventDefault();
if (mode === "login") {
login();
} else if (mode === "create") {
createAccount();
} else if (mode === "reset") {
resetPassword();
}
};
const headerTitle = (() => {
switch (mode) {
@@ -287,13 +289,13 @@ const LoginPage = ({ defaultView = "login" }) => {
/>
{mode === "login" && (
<div className="absolute right-0 top-0 bottom-0 flex items-center justify-center">
<button
type="button"
className="text-[#3182ce] text-[14px] font-medium no-underline mr-4 hover:text-[#2c5aa0] transition-colors duration-200"
onClick={() => setMode("reset")}
>
Forgot?
</button>
<button
type="button"
className="text-[#3182ce] text-[14px] font-medium no-underline mr-4 hover:text-[#2c5aa0] transition-colors duration-200"
onClick={() => navigate("/forgot-password")} // This navigates to the actual page
>
Forgot?
</button>
</div>
)}
</div>

عرض الملف

@@ -13,6 +13,7 @@ import Homepage from "../components/Homepage/Homepage";
import SettingsPage from "../components/SettingsPage/SettingsPage";
import LandingPage from '../components/LandingPage/LandingPage';
import PrivateRoute from "./PrivateRoute";
import ForgotPasswordPage from "../components/ForgotPasswordPage/ForgotPasswordPage";
const AppRouter = () => {
return (
@@ -23,6 +24,7 @@ const AppRouter = () => {
<Route path="/welcome" element={<Navigate to="/" replace />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<LoginPage defaultView="create" />} />
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
<Route path="/public-download/:id/:tempToken" element={<DownloadPage />} />
<Route path="/verify-email/:token" element={<VerifyEmailPage />} />
<Route path="/reset-password/:token" element={<ResetPasswordPage />} />

عرض الملف

@@ -20,6 +20,6 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"include": ["src", "backend/utils/emailService.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}