Compare commits
2 الالتزامات
1fda64ec43
...
7ae3010b7f
| المؤلف | SHA1 | التاريخ | |
|---|---|---|---|
| 7ae3010b7f | |||
| e94d92bf3b |
@@ -1,23 +1,12 @@
|
|||||||
.git/
|
|
||||||
.gitignore
|
|
||||||
.dockerignore
|
|
||||||
docker-compose*
|
|
||||||
Dockerfile
|
|
||||||
makefile
|
|
||||||
htmlcov/
|
|
||||||
coverage.xml
|
|
||||||
.coverage*
|
|
||||||
|
|
||||||
.vscode/
|
|
||||||
*.dat
|
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
node_modules
|
node_modules
|
||||||
/build
|
.git
|
||||||
/package
|
.gitignore
|
||||||
|
README.md
|
||||||
.env
|
.env
|
||||||
.env.*
|
.npm
|
||||||
!.env.example
|
.cache
|
||||||
vite.config.js.timestamp-*
|
dist
|
||||||
vite.config.ts.timestamp-*
|
*.log
|
||||||
.idea
|
.DS_Store
|
||||||
|
coverage
|
||||||
|
.nyc_output
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/* Basic Options */
|
/* Basic Options */
|
||||||
// "incremental": true, /* Enable incremental compilation */
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
"target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
|
"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. */
|
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||||
"allowJs": true /* Allow javascript files to be compiled. */,
|
"allowJs": true /* Allow javascript files to be compiled. */,
|
||||||
// "checkJs": true, /* Report errors in .js files. */
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
|
||||||
/* Module Resolution Options */
|
/* 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. */
|
// "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'. */
|
// "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. */
|
// "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();
|
||||||
11
index.html
11
index.html
@@ -2,14 +2,17 @@
|
|||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>myDrive</title>
|
<title>FlowSync | Drive</title>
|
||||||
<link rel="icon" href="/images/icon.png" />
|
<link rel="icon" href="/images/icon.png" />
|
||||||
<link rel="shortcut icon" type="image/png" href="/images/icon.png" />
|
<link rel="shortcut icon" type="image/png" href="/public/images/favicon-32x32.png" />
|
||||||
<link rel="shortcut icon" sizes="192x192" href="/images/icon.png" />
|
<link rel="shortcut icon" sizes="192x192" href="/public/images/favicon-32x32.png" />
|
||||||
<link rel="apple-touch-icon" href="/images/icon.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" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -27,7 +27,7 @@
|
|||||||
"aws-sdk": "^2.657.0",
|
"aws-sdk": "^2.657.0",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^2.2.1",
|
||||||
"bytes": "^3.1.0",
|
"bytes": "^3.1.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"cli-progress": "^3.6.0",
|
"cli-progress": "^3.6.0",
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"express": "^4.19.2",
|
"express": "^5.1.0",
|
||||||
"express-validator": "^7.1.0",
|
"express-validator": "^7.1.0",
|
||||||
"fluent-ffmpeg": "^2.1.3",
|
"fluent-ffmpeg": "^2.1.3",
|
||||||
"helmet": "^3.21.2",
|
"helmet": "^3.21.2",
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"mongodb": "^6.20.0",
|
"mongodb": "^6.20.0",
|
||||||
"mongoose": "^8.4.1",
|
"mongoose": "^8.4.1",
|
||||||
"nodemailer": "^6.9.14",
|
"nodemailer": "^7.0.10",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"password-prompt": "^1.1.2",
|
"password-prompt": "^1.1.2",
|
||||||
"progress-stream": "^2.0.0",
|
"progress-stream": "^2.0.0",
|
||||||
@@ -64,6 +64,7 @@
|
|||||||
"react-toastify": "^10.0.5",
|
"react-toastify": "^10.0.5",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
"regenerator-runtime": "^0.13.3",
|
"regenerator-runtime": "^0.13.3",
|
||||||
|
"resend": "^6.5.2",
|
||||||
"sharp": "^0.33.4",
|
"sharp": "^0.33.4",
|
||||||
"sweetalert2": "^11.15.10",
|
"sweetalert2": "^11.15.10",
|
||||||
"temp": "^0.9.1",
|
"temp": "^0.9.1",
|
||||||
@@ -88,6 +89,7 @@
|
|||||||
"@types/connect-busboy": "0.0.2",
|
"@types/connect-busboy": "0.0.2",
|
||||||
"@types/cookie-parser": "^1.4.2",
|
"@types/cookie-parser": "^1.4.2",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
|
"@types/dotenv": "^6.1.1",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"@types/helmet": "0.0.45",
|
"@types/helmet": "0.0.45",
|
||||||
@@ -128,7 +130,7 @@
|
|||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.4",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"typescript-eslint": "^7.14.1",
|
"typescript-eslint": "^7.14.1",
|
||||||
"vite": "^5.2.13",
|
"vite": "^6.0.0",
|
||||||
"vite-plugin-pwa": "^0.21.1"
|
"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 () => {
|
export const getUserToken = async () => {
|
||||||
const response = await axios.post("/user-service/get-token");
|
const response = await axios.post("/user-service/get-token");
|
||||||
response.data;
|
return response.data; // FIXED: Added return
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getUserAPI = async () => {
|
export const getUserAPI = async () => {
|
||||||
@@ -54,6 +54,12 @@ export const getAccessToken = async (uuid: string) => {
|
|||||||
return response.data;
|
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
|
// PATCH
|
||||||
|
|
||||||
export const changePasswordAPI = async (
|
export const changePasswordAPI = async (
|
||||||
@@ -79,13 +85,6 @@ export const verifyEmailAPI = async (emailToken: string) => {
|
|||||||
return response.data;
|
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 (
|
export const resetPasswordAPI = async (
|
||||||
password: string,
|
password: string,
|
||||||
passwordToken: string
|
passwordToken: string
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const LandingPage = () => {
|
|||||||
<div className="landing-page">
|
<div className="landing-page">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<div className="logo">🌊 FlowSync</div>
|
<div className="logo">♾️ FlowSync</div>
|
||||||
<div className="header-buttons">
|
<div className="header-buttons">
|
||||||
<button className="btn-secondary" onClick={() => navigate('/login')}>Sign In</button>
|
<button className="btn-secondary" onClick={() => navigate('/login')}>Sign In</button>
|
||||||
<button className="btn-primary" onClick={() => navigate('/register')}>Get Started</button>
|
<button className="btn-primary" onClick={() => navigate('/register')}>Get Started</button>
|
||||||
@@ -516,8 +516,7 @@ const LandingPage = () => {
|
|||||||
<div className="footer">
|
<div className="footer">
|
||||||
<div className="footer-content">
|
<div className="footer-content">
|
||||||
<div className="footer-brand">
|
<div className="footer-brand">
|
||||||
<div className="logo">🌊 FlowSpace</div>
|
<div className="logo">♾️ FlowSync</div> <p>Innovating the future of digital storage and collaboration</p>
|
||||||
<p>Innovating the future of digital storage and collaboration</p>
|
|
||||||
<p className="footer-tagline">Secure. Simple. Seamless.</p>
|
<p className="footer-tagline">Secure. Simple. Seamless.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="footer-links">
|
<div className="footer-links">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState, useCallback ,FormEvent } from "react";
|
||||||
import {
|
import {
|
||||||
createAccountAPI,
|
createAccountAPI,
|
||||||
getUserAPI,
|
getUserAPI,
|
||||||
@@ -16,6 +16,8 @@ import Swal from "sweetalert2";
|
|||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import isEmail from "validator/es/lib/isEmail";
|
import isEmail from "validator/es/lib/isEmail";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ADD THE defaultView PROP HERE
|
// ADD THE defaultView PROP HERE
|
||||||
const LoginPage = ({ defaultView = "login" }) => {
|
const LoginPage = ({ defaultView = "login" }) => {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
@@ -30,7 +32,7 @@ const LoginPage = ({ defaultView = "login" }) => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const lastSentPassowordReset = useRef(0);
|
const lastSentPassowordReset = useRef(0);
|
||||||
|
|
||||||
const attemptLoginWithToken = async () => {
|
const attemptLoginWithToken = useCallback(async () => {
|
||||||
setAttemptingLogin(true);
|
setAttemptingLogin(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -47,7 +49,7 @@ const LoginPage = ({ defaultView = "login" }) => {
|
|||||||
window.localStorage.removeItem("hasPreviouslyLoggedIn");
|
window.localStorage.removeItem("hasPreviouslyLoggedIn");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}, [location.state?.from?.pathname, dispatch, navigate]);
|
||||||
|
|
||||||
const login = async () => {
|
const login = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -147,16 +149,16 @@ const LoginPage = ({ defaultView = "login" }) => {
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const onSubmit = (e: any) => {
|
const onSubmit = (e: FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (mode === "login") {
|
if (mode === "login") {
|
||||||
login();
|
login();
|
||||||
} else if (mode === "create") {
|
} else if (mode === "create") {
|
||||||
createAccount();
|
createAccount();
|
||||||
} else if (mode === "reset") {
|
} else if (mode === "reset") {
|
||||||
resetPassword();
|
resetPassword();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const headerTitle = (() => {
|
const headerTitle = (() => {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
@@ -287,13 +289,13 @@ const LoginPage = ({ defaultView = "login" }) => {
|
|||||||
/>
|
/>
|
||||||
{mode === "login" && (
|
{mode === "login" && (
|
||||||
<div className="absolute right-0 top-0 bottom-0 flex items-center justify-center">
|
<div className="absolute right-0 top-0 bottom-0 flex items-center justify-center">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="text-[#3182ce] text-[14px] font-medium no-underline mr-4 hover:text-[#2c5aa0] transition-colors duration-200"
|
className="text-[#3182ce] text-[14px] font-medium no-underline mr-4 hover:text-[#2c5aa0] transition-colors duration-200"
|
||||||
onClick={() => setMode("reset")}
|
onClick={() => navigate("/forgot-password")} // This navigates to the actual page
|
||||||
>
|
>
|
||||||
Forgot?
|
Forgot?
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import Homepage from "../components/Homepage/Homepage";
|
|||||||
import SettingsPage from "../components/SettingsPage/SettingsPage";
|
import SettingsPage from "../components/SettingsPage/SettingsPage";
|
||||||
import LandingPage from '../components/LandingPage/LandingPage';
|
import LandingPage from '../components/LandingPage/LandingPage';
|
||||||
import PrivateRoute from "./PrivateRoute";
|
import PrivateRoute from "./PrivateRoute";
|
||||||
|
import ForgotPasswordPage from "../components/ForgotPasswordPage/ForgotPasswordPage";
|
||||||
|
|
||||||
const AppRouter = () => {
|
const AppRouter = () => {
|
||||||
return (
|
return (
|
||||||
@@ -23,6 +24,7 @@ const AppRouter = () => {
|
|||||||
<Route path="/welcome" element={<Navigate to="/" replace />} />
|
<Route path="/welcome" element={<Navigate to="/" replace />} />
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
<Route path="/register" element={<LoginPage defaultView="create" />} />
|
<Route path="/register" element={<LoginPage defaultView="create" />} />
|
||||||
|
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
||||||
<Route path="/public-download/:id/:tempToken" element={<DownloadPage />} />
|
<Route path="/public-download/:id/:tempToken" element={<DownloadPage />} />
|
||||||
<Route path="/verify-email/:token" element={<VerifyEmailPage />} />
|
<Route path="/verify-email/:token" element={<VerifyEmailPage />} />
|
||||||
<Route path="/reset-password/:token" element={<ResetPasswordPage />} />
|
<Route path="/reset-password/:token" element={<ResetPasswordPage />} />
|
||||||
|
|||||||
@@ -20,6 +20,6 @@
|
|||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src", "backend/utils/emailService.ts"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
|||||||
المرجع في مشكلة جديدة
حظر مستخدم