no reset password
هذا الالتزام موجود في:
@@ -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
|
||||
@@ -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
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,11 +2,11 @@
|
||||
|
||||
<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" />
|
||||
|
||||
12
package.json
12
package.json
@@ -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
ثنائية
public/images/favicon-32x32.png
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 785 B |
ثنائية
public/images/icon.png
ثنائية
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
|
||||
|
||||
@@ -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" }]
|
||||
}
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم