Skip to content
Snippets Groups Projects
Commit d6dbd5d9 authored by Maximilian Sjöström's avatar Maximilian Sjöström
Browse files

merge main into categories

parents 9b103edf af636b7e
Branches categories
No related tags found
1 merge request!8Categories
Showing with 401 additions and 53 deletions
......@@ -19,6 +19,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
......@@ -28,14 +29,13 @@ SECRET_KEY = "django-insecure-%o-mg(_ev*4ua26xa_e6xyc$$@1a6(+mvp=&fs_b^06uz4uj7=
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS = ["*"]
# AUTH_USER_MODEL = 'themeApp.models.CustomUser'
# Application definition
INSTALLED_APPS = [
"polls.apps.PollsConfig",
# "themeApp.apps.ThemeappConfig",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
......@@ -47,16 +47,15 @@ INSTALLED_APPS = [
'corsheaders'
]
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
'corsheaders.middleware.CorsMiddleware',
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
'django.middleware.common.CommonMiddleware',
]
ROOT_URLCONF = "mysite.urls"
......@@ -132,14 +131,14 @@ STATIC_URL = "static/"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
CORS_ALLOWED_ORIGINS = [
'http://localhost',
'http://localhost:3000',
'http://127.0.0.1',
'http://0.0.0.0',
]
#CORS_ALLOWED_ORIGINS = [
# "http://localhost:8000",0
# Add other allowed origins here
#]*/
CORS_ALLOW_ALL_ORIGINS = True
# but not recommended
CORS_ALLOW_CREDENTIALS = True
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
......@@ -148,4 +147,10 @@ REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
],
}
\ No newline at end of file
}
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'my-react-app', 'build', 'static'),
]
File added
File added
......@@ -2,6 +2,9 @@
from rest_framework import serializers
from .models import Product, Profile, Category
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, get_user_model
from django.core.exceptions import ValidationError
class ProductSerializer(serializers.ModelSerializer):
......@@ -23,22 +26,47 @@ class ProfileSerializer(serializers.ModelSerializer):
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
# password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = "__all__"
def create(self, validated_data):
"""
Function to create a User, send a JSON object containing
the attributes: username, email, first_name, last_name, password.
"""
user = User.objects.create_user(
username=validated_data['username'],
email=validated_data['email'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
password=validated_data['password']
)
# def create(self, validated_data):
# """
# Function to create a User, send a JSON object containing
# the attributes: username, email, first_name, last_name, password.
# """
# user = User.objects.create_user(
# username=validated_data['username'],
# email=validated_data['email'],
# first_name=validated_data['first_name'],
# last_name=validated_data['last_name'],
# password=validated_data['password']
# )
# return user
# {"email":"test@example.com",
# "username":"tester",
# "password":"testing123"
# }
class UserLoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def check_user(self, clean_data):
user = authenticate(username=clean_data['username'], password=clean_data['password'])
if not user:
raise ValidationError('user not found')
return user
class UserRegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
def create(self, clean_data):
user_obj = User.objects.create_user(username=clean_data['username'], email=clean_data['email'], password=clean_data['password'])
user_obj.username = clean_data['username']
user_obj.save()
return user_obj
\ No newline at end of file
<!-- myapp/templates/myapp/home.html -->
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
</head>
<body>
<h2>Welcome, {{ request.user.username }}!</h2>
<a href="{% url 'logout' %}">Logout</a>
</body>
</html>
<!-- myapp/templates/myapp/login.html -->
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h2>Login</h2>
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<form method="post">
{% csrf_token %}
<label for="username">Username:</label>
<input type="text" id="username" name="username"><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password"><br>
<button type="submit">Login</button>
</form>
</body>
</html>
# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet, ProfileViewSet, UserViewSet, UserRegistrationView
from .views import CategoryViewSet
from .views import ProductViewSet, ProfileViewSet, UserViewSet, CategoryViewSet
from . import views
router = DefaultRouter()
router.register(r'products', ProductViewSet)
......@@ -11,6 +11,10 @@ router.register(r'profile', ProfileViewSet)
urlpatterns = [
path('', include(router.urls)),
path('register/', UserRegistrationView.as_view(), name='user-register')
path('home/', views.home_view, name='home'), # Home pag
path('register/', views.UserRegister.as_view(), name='register'),
path('login/', views.UserLogin.as_view(), name='login'),
path('logout/', views.UserLogout.as_view(), name='logout'),
path('user/', views.UserView.as_view(), name='user'),
path('csrf/', views.csrf),
]
from django.core.exceptions import ValidationError
from django.contrib.auth import get_user_model
UserModel = get_user_model()
def custom_validation(data):
email = data['email'].strip()
username = data['username'].strip()
password = data['password'].strip()
##
if not email or UserModel.objects.filter(email=email).exists():
raise ValidationError('choose another email')
##
if not password or len(password) < 8:
raise ValidationError('choose another password, min 8 characters')
##
if not username:
raise ValidationError('choose another username')
return data
def validate_email(data):
email = data['email'].strip()
if not email:
raise ValidationError('an email is needed')
return True
def validate_username(data):
username = data['username'].strip()
if not username:
raise ValidationError('choose another username')
return True
def validate_password(data):
password = data['password'].strip()
if not password:
raise ValidationError('a password is needed')
return True
\ No newline at end of file
# myapp/views.py
from rest_framework import viewsets, status
from rest_framework import viewsets, status, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication
from .models import Product, Category, Profile
from .serializers import ProductSerializer, CategorySerializer, ProfileSerializer, UserSerializer
from .serializers import ProductSerializer, CategorySerializer, ProfileSerializer, UserSerializer, UserLoginSerializer, UserRegisterSerializer
from django.contrib.auth.models import User
from .validations import *
# login/logout import
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.http import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie
from django.middleware.csrf import get_token
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
......@@ -25,10 +36,82 @@ class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserRegistrationView(APIView):
def post(self, request, *args, **kwargs):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
# class UserRegistrationView(APIView):
# def post(self, request, *args, **kwargs):
# serializer = UserSerializer(data=request.data)
# if serializer.is_valid():
# serializer.save()
# return Response(serializer.data, status=status.HTTP_201_CREATED)
# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class UserRegister(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request):
clean_data = custom_validation(request.data)
serializer = UserRegisterSerializer(data=clean_data)
if serializer.is_valid(raise_exception=True):
user = serializer.create(clean_data)
if user:
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_400_BAD_REQUEST)
class UserLogin(APIView):
permission_classes = (permissions.AllowAny,)
authentication_classes = (SessionAuthentication,)
##
def post(self, request):
data = request.data
assert validate_username(data)
assert validate_password(data)
serializer = UserLoginSerializer(data=data)
if serializer.is_valid(raise_exception=True):
user = serializer.check_user(data)
login(request, user)
return Response(serializer.data, status=status.HTTP_200_OK)
class UserLogout(APIView):
permission_classes = (permissions.AllowAny,)
authentication_classes = ()
def post(self, request):
logout(request)
return Response(status=status.HTTP_200_OK)
class UserView(APIView):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
##
def get(self, request):
serializer = UserSerializer(request.user)
return Response({'user': serializer.data}, status=status.HTTP_200_OK)
# def login_view(request):
# if request.method == 'POST':
# username = request.POST['username']
# password = request.POST['password']
# user = authenticate(request, username=username, password=password)
# if user is not None:
# login(request, user)
# return HttpResponseRedirect('http://localhost:3000/profile') # Redirect to a success page
# else:
# messages.error(request, 'Invalid username or password')
# return render(request, 'themeApp/login.html')
# def logout_view(request):
# logout(request)
# return redirect('login')
def home_view(request):
return render(request, 'themeApp/home.html')
@ensure_csrf_cookie
def csrf(request):
token = get_token(request)
return JsonResponse({'csrfToken': token})
\ No newline at end of file
......@@ -18,7 +18,7 @@
"axios": "^1.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"react-router-dom": "^6.23.1",
"react-scripts": "5.0.1",
"react-slick": "^0.30.2",
"slick-carousel": "^1.8.1",
......@@ -3832,9 +3832,9 @@
}
},
"node_modules/@remix-run/router": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
"integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
"integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==",
"engines": {
"node": ">=14.0.0"
}
......@@ -15676,11 +15676,11 @@
}
},
"node_modules/react-router": {
"version": "6.22.3",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
"integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
"version": "6.23.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz",
"integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==",
"dependencies": {
"@remix-run/router": "1.15.3"
"@remix-run/router": "1.16.1"
},
"engines": {
"node": ">=14.0.0"
......@@ -15690,12 +15690,12 @@
}
},
"node_modules/react-router-dom": {
"version": "6.22.3",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
"integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
"version": "6.23.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz",
"integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==",
"dependencies": {
"@remix-run/router": "1.15.3",
"react-router": "6.22.3"
"@remix-run/router": "1.16.1",
"react-router": "6.23.1"
},
"engines": {
"node": ">=14.0.0"
......
......@@ -13,7 +13,7 @@
"axios": "^1.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"react-router-dom": "^6.23.1",
"react-scripts": "5.0.1",
"react-slick": "^0.30.2",
"slick-carousel": "^1.8.1",
......
......@@ -5,6 +5,7 @@ import About from './About';
import Profile from './Profile';
import Charts from './Charts';
import Dashboard from './Dashboard';
import Login from './Login';
import ResponsiveAppBar from './components/ResponsiveAppBar';
import CategoriesPage from './CategoriesPage';
......@@ -37,6 +38,7 @@ function App() {
<Route path="/Topplistan" element={<Charts />} />
<Route path="/Categories" element={<CategoriesPage />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/login" element={<Login />} />
</Routes>
</div>
......
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import CssBaseline from '@mui/material/CssBaseline';
import TextField from '@mui/material/TextField';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import Link from '@mui/material/Link';
import Grid from '@mui/material/Grid';
import Box from '@mui/material/Box';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import theme from './styles/theme'
import { login, getCsrfToken } from './utils/api'
import { useNavigate } from 'react-router-dom';
const defaultTheme = theme
export default function SignIn() {
const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState('');
const navigate = useNavigate();
const handleUsername = (event) => {
setUsername(event.target.value);
};
const handlePassword = (event) => {
setPassword(event.target.value);
};
const handleSubmit = async (event) => {
event.preventDefault();
try {
console.log(username, password)
const result = await login(username, password);
console.log('Success:', result);
// Redirect or update the UI after successful login
if(result === 200) {
navigate("/profile");
}
} catch (error) {
console.error('Error:', error);
}
};
return (
<ThemeProvider theme={defaultTheme}>
<Container component="main" maxWidth="xs">
<CssBaseline />
<Box
sx={{
marginTop: 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Avatar sx={{ m: 1, bgcolor: defaultTheme }}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<Box component="form" onSubmit={handleSubmit} noValidate sx={{ mt: 1 }}>
<TextField
margin="normal"
required
fullWidth
id="username"
label="Username"
name="username"
autoComplete="username"
autoFocus
value={username}
onChange={handleUsername}
/>
<TextField
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
value={password}
onChange={handlePassword}
/>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
Sign In
</Button>
<Grid container>
<Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Link href="#" variant="body2">
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>
</Box>
</Box>
</Container>
</ThemeProvider>
);
}
\ No newline at end of file
// src/utils/api.js
import axios from 'axios';
const API_BASE_URL = 'http://127.0.0.1:8000';
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
axios.defaults.withCredentials = true;
export const login = async (username, password) => {
try {
const response = await axios.post('http://127.0.0.1:8000/themeApp/login/', {
username: username,
password: password
}, {
headers: {
'Content-Type': 'application/json'
},
withCredentials: true // Include cookies in the request
});
console.log('Login success:', response.data);
return response.status
} catch (error) {
console.error('Login error:', error);
}
}
// Other API calls can be added here
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment