Skip to content

Commit 4e470b4

Browse files
committed
Fix login issue: npm update
1 parent 4e2c99d commit 4e470b4

File tree

14 files changed

+1909
-1544
lines changed

14 files changed

+1909
-1544
lines changed

backend/api/views.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,17 @@ def post(self, request, *args, **kwargs):
5757

5858
if user is not None:
5959
auth_login(request, user)
60-
return JsonResponse({"detail": "Successfully logged in."})
60+
return JsonResponse({
61+
"detail": "Successfully logged in.",
62+
"user": {
63+
"id": user.id,
64+
"username": user.username,
65+
"email": user.email,
66+
"is_staff": user.is_staff,
67+
"is_superuser": user.is_superuser,
68+
},
69+
"token": None # Session-based auth, no token needed
70+
})
6171
else:
6272
return JsonResponse(
6373
{"detail": "Invalid login credentials."},

backend/users/serializers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,24 @@ def get_last_name(self, obj):
6868
return None
6969

7070

71+
class UserProfileUpdateSerializer(serializers.ModelSerializer):
72+
"""Serializer for updating user profile information (first_name, last_name)"""
73+
74+
class Meta:
75+
model = User
76+
fields = ["first_name", "last_name"]
77+
78+
def validate_first_name(self, value):
79+
if value and len(value.strip()) > 150:
80+
raise serializers.ValidationError("First name cannot exceed 150 characters.")
81+
return value.strip() if value else ""
82+
83+
def validate_last_name(self, value):
84+
if value and len(value.strip()) > 150:
85+
raise serializers.ValidationError("Last name cannot exceed 150 characters.")
86+
return value.strip() if value else ""
87+
88+
7189
class ProfileBioSerializer(serializers.ModelSerializer):
7290
class Meta:
7391
model = Profile

backend/users/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
UserBioRetrieveUpdateView,
1616
UserFollowersListView,
1717
UserFollowingListView,
18+
UserProfileUpdateView,
1819
UserRegistrationView,
1920
ProfileDetailByIdView,
2021
)
@@ -23,6 +24,7 @@
2324

2425
urlpatterns = [
2526
path("me/", CurrentUserView.as_view(), name="current_user"),
27+
path("me/update/", UserProfileUpdateView.as_view(), name="user-profile-update"),
2628
path("me/delete/", ProfileDeleteView.as_view(), name="profile-delete"),
2729
path("me/change-password/", ChangePasswordView.as_view(), name="change-password"),
2830
path(

backend/users/views.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
ProfileBioSerializer,
2929
ProfileImageSerializer,
3030
ProfileSerializer,
31+
UserProfileUpdateSerializer,
3132
UserRegistrationSerializer,
3233
)
3334
from .utils import process_profile_image
@@ -94,6 +95,32 @@ def get_object(self):
9495
return self.request.user
9596

9697

98+
class UserProfileUpdateView(generics.UpdateAPIView):
99+
"""Update user profile information (first_name, last_name)"""
100+
permission_classes = [IsAuthenticated]
101+
serializer_class = UserProfileUpdateSerializer
102+
103+
def get_object(self):
104+
return self.request.user
105+
106+
def update(self, request, *args, **kwargs):
107+
response = super().update(request, *args, **kwargs)
108+
# Return the updated user data in the same format as the login response
109+
user = self.get_object()
110+
return Response({
111+
"detail": "Profile updated successfully.",
112+
"user": {
113+
"id": user.id,
114+
"username": user.username,
115+
"email": user.email,
116+
"first_name": user.first_name,
117+
"last_name": user.last_name,
118+
"is_staff": user.is_staff,
119+
"is_superuser": user.is_superuser,
120+
}
121+
})
122+
123+
97124
class UserBioRetrieveUpdateView(generics.RetrieveUpdateAPIView):
98125
"""
99126
Retrieve or update the bio of the specified user.

frontend/Dockerfile.dev

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ COPY package*.json .npmrc ./
88
# Install dependencies with CI-optimized flags
99
RUN npm ci --prefer-offline --no-audit --no-fund
1010

11-
# Copy entrypoint script and make it executable
12-
COPY docker-entrypoint.sh /usr/local/bin/
13-
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
14-
11+
# Copy application files
1512
COPY . .
1613

14+
# Copy entrypoint script, convert Windows line endings to Unix, and make it executable
15+
COPY docker-entrypoint.sh /usr/local/bin/
16+
RUN sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh && chmod +x /usr/local/bin/docker-entrypoint.sh
17+
1718
EXPOSE 3000
1819

1920
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

frontend/app/components/Login.tsx

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,27 @@
11
import * as React from 'react';
22
import type { FC, ChangeEvent } from "react";
33
import { Container, Grid } from '@mui/material';
4-
import { useNavigate, Form, useSubmit, useActionData } from '@remix-run/react';
4+
import { useNavigate } from '@remix-run/react';
55
import { TextField, Typography, Button, CircularProgress } from '@mui/material';
66
import ReCAPTCHA from 'react-google-recaptcha';
77
import type { ReCAPTCHA as ReCAPTCHAType } from 'react-google-recaptcha';
88
import UserContext from '../contexts/UserContext';
99

1010
interface LoginProps {
11-
onLogin: () => void;
11+
onLogin: (username: string, password: string, recaptcha: string) => Promise<{ success: boolean }>;
1212
recaptchaSiteKey: string;
1313
}
1414

15-
export const Login: FC<LoginProps> = ({ recaptchaSiteKey }) => {
15+
export const Login: FC<LoginProps> = ({ recaptchaSiteKey, onLogin }) => {
1616
const [username, setUsername] = React.useState<string>("");
1717
const [password, setPassword] = React.useState<string>("");
1818
const [usernameInvalid, setUsernameInvalid] = React.useState<boolean>(false);
1919
const [passwordInvalid, setPasswordInvalid] = React.useState<boolean>(false);
2020
const [isLoading, setIsLoading] = React.useState<boolean>(false);
2121

2222
const navigate = useNavigate();
23-
const submit = useSubmit();
2423
const recaptchaRef = React.useRef<ReCAPTCHAType>(null);
25-
const formRef = React.useRef<HTMLFormElement>(null);
26-
const actionData = useActionData<{ success?: boolean; error?: string; user?: any }>();
27-
const { updateUser } = React.useContext(UserContext);
28-
29-
React.useEffect(() => {
30-
if (actionData?.success && actionData.user) {
31-
updateUser(actionData.user);
32-
navigate("/");
33-
}
34-
if (actionData?.error) {
35-
setIsLoading(false);
36-
}
37-
}, [actionData, navigate, updateUser]);
24+
const [error, setError] = React.useState<string>("");
3825

3926
const validateForm = (): boolean => {
4027
const isUsernameInvalid = !username;
@@ -65,23 +52,20 @@ export const Login: FC<LoginProps> = ({ recaptchaSiteKey }) => {
6552
return;
6653
}
6754

68-
if (formRef.current) {
69-
const formData = new FormData(formRef.current);
70-
formData.set('username', username);
71-
formData.set('password', password);
72-
formData.set('recaptcha', token);
73-
74-
console.log('Submitting form with data:', {
75-
username,
76-
recaptcha: 'present'
77-
});
78-
79-
submit(formData, {
80-
method: 'post'
81-
});
55+
console.log('Calling onLogin with data:', {
56+
username,
57+
recaptcha: 'present'
58+
});
59+
60+
// Call the client-side login handler
61+
const result = await onLogin(username, password, token);
62+
if (result.success) {
63+
setIsLoading(false);
8264
}
65+
8366
} catch (error) {
84-
console.error('Error during form submission:', error);
67+
console.error('Error during login:', error);
68+
setError(error instanceof Error ? error.message : 'Login failed');
8569
setIsLoading(false);
8670
}
8771
};
@@ -101,12 +85,12 @@ export const Login: FC<LoginProps> = ({ recaptchaSiteKey }) => {
10185
<Grid container justifyContent="center">
10286
<Grid item xs={12} md={8} lg={3}>
10387
<Typography variant="h4" component="h2" sx={{ mb: 3 }}>Login</Typography>
104-
{actionData?.error && (
88+
{error && (
10589
<Typography color="error" sx={{ mb: 2 }}>
106-
{actionData.error}
90+
{error}
10791
</Typography>
10892
)}
109-
<Form ref={formRef} method="post">
93+
<form>
11094
<input type="hidden" name="recaptcha" value="" />
11195
<TextField
11296
id="username"
@@ -168,7 +152,7 @@ export const Login: FC<LoginProps> = ({ recaptchaSiteKey }) => {
168152
>
169153
Forgot Password?
170154
</Button>
171-
</Form>
155+
</form>
172156
</Grid>
173157
</Grid>
174158
</Container>

0 commit comments

Comments
 (0)