diff --git a/backend/envs/prod/initialization/setup_prod_environment.py b/backend/envs/prod/initialization/setup_prod_environment.py
index b1eba04..a4c3059 100644
--- a/backend/envs/prod/initialization/setup_prod_environment.py
+++ b/backend/envs/prod/initialization/setup_prod_environment.py
@@ -15,6 +15,7 @@ def create_admin_user():
email="admin@docshow.ai",
organization_id=1,
role="admin",
+ requires_password_update=True,
)
with DatabaseManager() as session:
diff --git a/backend/models/user.py b/backend/models/user.py
index 74143f5..786aeaf 100644
--- a/backend/models/user.py
+++ b/backend/models/user.py
@@ -1,6 +1,6 @@
from enum import Enum
from pydantic import BaseModel, EmailStr, Field, validator
-from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text
+from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.sql import func
from typing import Optional
@@ -29,6 +29,7 @@ class User(Base):
hashed_password = Column(String)
organization_id = Column(Integer, ForeignKey("organizations.id"), nullable=True)
role = Column(String, nullable=True)
+ requires_password_update = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), default=func.now())
refresh_token = Column(Text, nullable=True)
@@ -50,6 +51,7 @@ class UserCreate(BaseModel):
password: str
organization_id: Optional[int] = None
role: Optional[str] = None
+ requires_password_update: Optional[bool] = False
@validator("password")
def validate_password(cls, v):
@@ -85,6 +87,7 @@ class UserOut(BaseModel):
email: EmailStr
organization_id: Optional[int]
role: Optional[str]
+ requires_password_update: bool
class UserRole(str, Enum):
@@ -109,6 +112,7 @@ class UserUpdate(BaseModel):
username: str
organization_id: Optional[int]
role: Optional[UserRole]
+ requires_password_update: Optional[bool] = False
class ChangePassword(BaseModel):
diff --git a/backend/routes/user_routes.py b/backend/routes/user_routes.py
index 2b595cd..70eea99 100644
--- a/backend/routes/user_routes.py
+++ b/backend/routes/user_routes.py
@@ -25,7 +25,15 @@ async def get_users(current_admin_user: User = Depends(get_current_admin_user)):
@user_router.get("/users/me/", response_model=UserOut)
async def read_users_me(current_user: User = Depends(get_current_user)):
- return current_user
+ user_out = UserOut(
+ id=current_user.id,
+ username=current_user.username,
+ email=current_user.email,
+ organization_id=current_user.organization_id,
+ role=current_user.role,
+ requires_password_update=current_user.requires_password_update,
+ )
+ return user_out
@user_router.get("/users/roles/")
@@ -57,10 +65,8 @@ async def update_user(
async def change_user_password(
change_password: ChangePassword, current_user: User = Depends(get_current_user)
):
- print("change_password", change_password)
# Verify old password
if not verify_password(change_password.old_password, current_user.hashed_password):
- print("HELLO")
raise HTTPException(status_code=400, detail="Invalid old password")
with DatabaseManager() as session:
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 42745c0..507186f 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -15,6 +15,7 @@ import AboutPage from './pages/about/AboutPage';
import AdminPage from './pages/admin/AdminPage';
import AnalyticsPage from './pages/analytics/AnalyticsPage';
import BlogPage from './pages/blog/BlogPage';
+import ChangePasswordPage from './pages/change-password/ChangePasswordPage';
import CreateChartPage from './pages/charts/CreateChartPage';
import CreateDashboardPage from './pages/dashboards/CreateDashboard';
import DashboardMenuPage from './pages/dashboards/DashboardsMenuPage';
@@ -60,6 +61,7 @@ function App() {
element={isAuthenticated ? :
}
/>
+ } />
} />
} />
} />
diff --git a/frontend/src/pages/user/ChangePassword.jsx b/frontend/src/components/change-password/ChangePassword.jsx
similarity index 97%
rename from frontend/src/pages/user/ChangePassword.jsx
rename to frontend/src/components/change-password/ChangePassword.jsx
index 86a5418..91b1a24 100644
--- a/frontend/src/pages/user/ChangePassword.jsx
+++ b/frontend/src/components/change-password/ChangePassword.jsx
@@ -9,6 +9,7 @@ const ChangePassword = ({ handleChangePassword, errorMessage, successMessage })
const handleSubmit = async (event) => {
event.preventDefault();
const success = await handleChangePassword(oldPassword, newPassword, confirmPassword);
+ console.log('handleChangePassword result:', success);
if (success) {
setOldPassword('');
setNewPassword('');
diff --git a/frontend/src/pages/change-password/ChangePasswordPage.jsx b/frontend/src/pages/change-password/ChangePasswordPage.jsx
new file mode 100644
index 0000000..6858164
--- /dev/null
+++ b/frontend/src/pages/change-password/ChangePasswordPage.jsx
@@ -0,0 +1,40 @@
+import React, { useState } from 'react'
+import { useNavigate } from 'react-router-dom';
+import { Box, Container, Typography } from '@mui/material'
+import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
+import ChangePassword from '../../components/change-password/ChangePassword';
+import { updateUserPassword } from '../../utils/updateUserPassword';
+
+function ChangePasswordPage() {
+ const navigate = useNavigate();
+ const [errorMessage, setErrorMessage] = useState('');
+ const [successMessage, setSuccessMessage] = useState('');
+
+ const handleChangePassword = async (oldPassword, newPassword, confirmPassword) => {
+ const success = await updateUserPassword(oldPassword, newPassword, confirmPassword, setErrorMessage, setSuccessMessage);
+ if (success) {
+ navigate('/dashboards');
+ }
+ };
+
+ return (
+
+
+
+ {/*
+ Change Password
+ */}
+
+
+
+ )
+}
+
+export default ChangePasswordPage
\ No newline at end of file
diff --git a/frontend/src/pages/login/LoginPage.jsx b/frontend/src/pages/login/LoginPage.jsx
index 4e31375..e7faecf 100644
--- a/frontend/src/pages/login/LoginPage.jsx
+++ b/frontend/src/pages/login/LoginPage.jsx
@@ -33,7 +33,16 @@ function LoginPage({ onLogin }) {
if (response.status === 200) {
updateAuth(true);
- navigate('/dashboards');
+ const userResponse = await axios.get(`${API_URL}users/me/`, {
+ headers: {
+ 'Authorization': `Bearer ${response.data.access_token}`
+ }
+ });
+ if (userResponse.data.requires_password_update) {
+ navigate('/change-password');
+ } else {
+ navigate('/dashboards');
+ }
}
} catch (error) {
if (error.response && error.response.status === 401) {
diff --git a/frontend/src/pages/user/UserPage.jsx b/frontend/src/pages/user/UserPage.jsx
index 9188274..dab0f49 100644
--- a/frontend/src/pages/user/UserPage.jsx
+++ b/frontend/src/pages/user/UserPage.jsx
@@ -4,9 +4,10 @@ import EmailIcon from '@mui/icons-material/Email';
import BusinessIcon from '@mui/icons-material/Business';
import AssignmentIndIcon from '@mui/icons-material/AssignmentInd';
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
-import { Alert, Box, CircularProgress, Grid, Snackbar, Typography } from '@mui/material';
+import { Alert, Box, CircularProgress, Grid, Typography } from '@mui/material';
import InfoCard from './InfoCard';
-import ChangePassword from './ChangePassword';
+import ChangePassword from '../../components/change-password/ChangePassword';
+import { updateUserPassword } from '../../utils/updateUserPassword';
import { API_URL } from '../../utils/constants';
const UserPage = () => {
@@ -43,42 +44,7 @@ const UserPage = () => {
}, []);
const handleChangePassword = async (oldPassword, newPassword, confirmPassword) => {
- if (newPassword !== confirmPassword) {
- setErrorMessage('Passwords do not match!');
- return;
- }
-
- try {
- const response = await axios.put(`${API_URL}users/change-password/`, { old_password: oldPassword, new_password: newPassword });
-
- if (response.status === 200) {
- setSuccessMessage('Password changed successfully');
- setErrorMessage('');
- return true;
- } else {
- setErrorMessage('Failed to change password!');
- return false;
- }
- } catch (error) {
- if (error.response) {
- if (error.response.status === 400) {
- setErrorMessage(error.response.data.detail);
- } else if (error.response.data && error.response.data.detail && error.response.data.detail[0]) {
- let errorMessage = error.response.data.detail[0].msg;
- if (typeof errorMessage === 'string') {
- errorMessage = errorMessage.replace('Value error, ', '');
- setErrorMessage(errorMessage);
- }
- }
- } else if (error.request) {
- // The request was made but no response was received
- console.log(error.request);
- } else {
- // Something happened in setting up the request that triggered an Error
- console.log('Error', error.message)
- }
- return false;
- }
+ return await updateUserPassword(oldPassword, newPassword, confirmPassword, setErrorMessage, setSuccessMessage);
};
return (
diff --git a/frontend/src/utils/updateUserPassword.jsx b/frontend/src/utils/updateUserPassword.jsx
new file mode 100644
index 0000000..964b293
--- /dev/null
+++ b/frontend/src/utils/updateUserPassword.jsx
@@ -0,0 +1,40 @@
+import axios from 'axios';
+import { API_URL } from './constants';
+
+export const updateUserPassword = async (oldPassword, newPassword, confirmPassword, setErrorMessage, setSuccessMessage) => {
+ if (newPassword !== confirmPassword) {
+ setErrorMessage('Passwords do not match!');
+ return false;
+ }
+
+ try {
+ const response = await axios.put(`${API_URL}users/change-password/`, { old_password: oldPassword, new_password: newPassword });
+
+ console.log('Server response status:', response.status); // Add this line
+
+ if (response.status === 200) {
+ console.log('Password changed successfully')
+ setSuccessMessage('Password changed successfully');
+ setErrorMessage('');
+ return true;
+ } else {
+ setErrorMessage('Failed to change password!');
+ setSuccessMessage('');
+ return false;
+ }
+ } catch (error) {
+ if (error.response) {
+ if (error.response.status === 400) {
+ setErrorMessage(error.response.data.detail);
+ } else if (error.response.data && error.response.data.detail && error.response.data.detail[0]) {
+ let errorMessage = error.response.data.detail[0].msg;
+ if (typeof errorMessage === 'string') {
+ setErrorMessage(errorMessage);
+ }
+ }
+ }
+ }
+ setErrorMessage(errorMessage);
+ setSuccessMessage('');
+ return false;
+};
\ No newline at end of file