Skip to content

Commit c32e016

Browse files
committed
Updated core files
1 parent cb21075 commit c32e016

File tree

138 files changed

+549
-4199
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

138 files changed

+549
-4199
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
env/

Procfile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: gunicorn devsearch.wsgi --log-file -

api/__init__.py

Whitespace-only changes.
144 Bytes
Binary file not shown.
1.89 KB
Binary file not shown.

api/__pycache__/urls.cpython-39.pyc

652 Bytes
Binary file not shown.

api/__pycache__/views.cpython-39.pyc

1.9 KB
Binary file not shown.

api/serializers.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from rest_framework import serializers
2+
from projects.models import Project, Tag, Review
3+
from users.models import Profile
4+
5+
6+
class ReviewSerializer(serializers.ModelSerializer):
7+
class Meta:
8+
model = Review
9+
fields = '__all__'
10+
11+
12+
class ProfileSerializer(serializers.ModelSerializer):
13+
class Meta:
14+
model = Profile
15+
fields = '__all__'
16+
17+
18+
class TagSerializer(serializers.ModelSerializer):
19+
class Meta:
20+
model = Tag
21+
fields = '__all__'
22+
23+
24+
class ProjectSerializer(serializers.ModelSerializer):
25+
owner = ProfileSerializer(many=False)
26+
tags = TagSerializer(many=True)
27+
reviews = serializers.SerializerMethodField()
28+
29+
class Meta:
30+
model = Project
31+
fields = '__all__'
32+
33+
def get_reviews(self, obj):
34+
reviews = obj.review_set.all()
35+
serializer = ReviewSerializer(reviews, many=True)
36+
return serializer.data

api/urls.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from django.urls import path
2+
from . import views
3+
4+
from rest_framework_simplejwt.views import (
5+
TokenObtainPairView,
6+
TokenRefreshView,
7+
)
8+
9+
urlpatterns = [
10+
path('users/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
11+
path('users/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
12+
13+
path('', views.getRoutes),
14+
path('projects/', views.getProjects),
15+
path('projects/<str:pk>/', views.getProject),
16+
path('projects/<str:pk>/vote/', views.projectVote),
17+
18+
path('remove-tag/', views.removeTag)
19+
]

api/views.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from rest_framework.decorators import api_view, permission_classes
2+
from rest_framework.permissions import IsAuthenticated, IsAdminUser
3+
from rest_framework.response import Response
4+
from .serializers import ProjectSerializer
5+
from projects.models import Project, Review, Tag
6+
7+
8+
@api_view(['GET'])
9+
def getRoutes(request):
10+
11+
routes = [
12+
{'GET': '/api/projects'},
13+
{'GET': '/api/projects/id'},
14+
{'POST': '/api/projects/id/vote'},
15+
16+
{'POST': '/api/users/token'},
17+
{'POST': '/api/users/token/refresh'},
18+
]
19+
return Response(routes)
20+
21+
22+
@api_view(['GET'])
23+
def getProjects(request):
24+
projects = Project.objects.all()
25+
serializer = ProjectSerializer(projects, many=True)
26+
return Response(serializer.data)
27+
28+
29+
@api_view(['GET'])
30+
def getProject(request, pk):
31+
project = Project.objects.get(id=pk)
32+
serializer = ProjectSerializer(project, many=False)
33+
return Response(serializer.data)
34+
35+
36+
@api_view(['POST'])
37+
@permission_classes([IsAuthenticated])
38+
def projectVote(request, pk):
39+
project = Project.objects.get(id=pk)
40+
user = request.user.profile
41+
data = request.data
42+
43+
review, created = Review.objects.get_or_create(
44+
owner=user,
45+
project=project,
46+
)
47+
48+
review.value = data['value']
49+
review.save()
50+
project.getVoteCount
51+
52+
serializer = ProjectSerializer(project, many=False)
53+
return Response(serializer.data)
54+
55+
56+
@api_view(['DELETE'])
57+
def removeTag(request):
58+
tagId = request.data['tag']
59+
projectId = request.data['project']
60+
61+
project = Project.objects.get(id=projectId)
62+
tag = Tag.objects.get(id=tagId)
63+
64+
project.tags.remove(tag)
65+
66+
return Response('Tag was deleted!')

db.sqlite3

4 KB
Binary file not shown.
1.02 KB
Binary file not shown.
30 Bytes
Binary file not shown.

devsearch/settings.py

+72-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
For the full list of settings and their values, see
1010
https://docs.djangoproject.com/en/3.2/ref/settings/
1111
"""
12-
12+
from datetime import timedelta
1313
from pathlib import Path
1414
import os
1515
# Build paths inside the project like this: BASE_DIR / 'subdir'.
@@ -41,9 +41,52 @@
4141
'projects.apps.ProjectsConfig',
4242
'users.apps.UsersConfig',
4343

44+
'rest_framework',
45+
'corsheaders',
46+
'storages'
47+
4448
]
4549

50+
REST_FRAMEWORK = {
51+
'DEFAULT_AUTHENTICATION_CLASSES': (
52+
'rest_framework_simplejwt.authentication.JWTAuthentication',
53+
)
54+
}
55+
56+
SIMPLE_JWT = {
57+
'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
58+
'REFRESH_TOKEN_LIFETIME': timedelta(days=30),
59+
'ROTATE_REFRESH_TOKENS': False,
60+
'BLACKLIST_AFTER_ROTATION': True,
61+
'UPDATE_LAST_LOGIN': False,
62+
63+
'ALGORITHM': 'HS256',
64+
65+
'VERIFYING_KEY': None,
66+
'AUDIENCE': None,
67+
'ISSUER': None,
68+
69+
'AUTH_HEADER_TYPES': ('Bearer',),
70+
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
71+
'USER_ID_FIELD': 'id',
72+
'USER_ID_CLAIM': 'user_id',
73+
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
74+
75+
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
76+
'TOKEN_TYPE_CLAIM': 'token_type',
77+
78+
'JTI_CLAIM': 'jti',
79+
80+
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
81+
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
82+
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
83+
}
84+
85+
86+
# TESTING
4687
MIDDLEWARE = [
88+
'corsheaders.middleware.CorsMiddleware',
89+
4790
'django.middleware.security.SecurityMiddleware',
4891

4992
'whitenoise.middleware.WhiteNoiseMiddleware',
@@ -81,6 +124,17 @@
81124

82125
# Database
83126
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
127+
# DATABASES = {
128+
# 'default': {
129+
# 'ENGINE': 'django.db.backends.postgresql',
130+
# 'NAME': 'devsearch',
131+
# 'USER': os.environ.get('DB_USER'),
132+
# 'PASSWORD': os.environ.get('DB_PASS'),
133+
# 'HOST': os.environ.get('DB_HOST'),
134+
# 'PORT': '5432',
135+
# }
136+
# }
137+
84138

85139
DATABASES = {
86140
'default': {
@@ -122,13 +176,14 @@
122176

123177
USE_TZ = True
124178

179+
CORS_ALLOW_ALL_ORIGINS = True
125180

126181
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
127182
EMAIL_HOST = 'smtp.gmail.com'
128183
EMAIL_PORT = 587
129184
EMAIL_USE_TLS = True
130-
EMAIL_HOST_USER = 'youremail'
131-
EMAIL_HOST_PASSWORD = 'youremailpassword'
185+
EMAIL_HOST_USER = 'YOUR-EMAIL'
186+
EMAIL_HOST_PASSWORD = 'YOUR-EMAIL-PASSWORD'
132187

133188

134189
# Static files (CSS, JavaScript, Images)
@@ -148,3 +203,17 @@
148203
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
149204

150205
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
206+
207+
208+
# DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
209+
210+
# AWS_QUERYSTRING_AUTH = False
211+
# AWS_S3_FILE_OVERWRITE = False
212+
213+
# AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
214+
# AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')
215+
# AWS_STORAGE_BUCKET_NAME = 'devsearch-bucket'
216+
217+
218+
if os.getcwd() == '/app':
219+
DEBUG = False

devsearch/urls.py

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
path('admin/', admin.site.urls),
1111
path('projects/', include('projects.urls')),
1212
path('', include('users.urls')),
13+
path('api/', include('api.urls')),
1314

1415
path('reset_password/', auth_views.PasswordResetView.as_view(template_name="reset_password.html"),
1516
name="reset_password"),

frontend/login.html

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<meta charset='utf-8'>
6+
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
7+
<title>Page Title</title>
8+
<meta name='viewport' content='width=device-width, initial-scale=1'>
9+
10+
<link rel='stylesheet' type='text/css' media='screen' href='main.css'>
11+
12+
</head>
13+
14+
<body>
15+
16+
<div id="form-wrapper">
17+
<h1>Login</h1>
18+
<hr>
19+
20+
<form id="login-form">
21+
<label>Username:</label>
22+
<input type="text" name="username" />
23+
24+
<label>Password:</label>
25+
<input type="password" name="password" />
26+
27+
<input type="submit" value="Login" />
28+
</form>
29+
</div>
30+
31+
</body>
32+
<script src='login.js'></script>
33+
34+
</html>

frontend/login.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
let form = document.getElementById('login-form')
2+
3+
form.addEventListener('submit', (e) => {
4+
e.preventDefault()
5+
6+
let formData = {
7+
'username': form.username.value,
8+
'password': form.password.value
9+
}
10+
11+
fetch('http://127.0.0.1:8000/api/users/token/', {
12+
method: 'POST',
13+
headers: {
14+
'Content-Type': 'application/json',
15+
},
16+
body: JSON.stringify(formData)
17+
})
18+
.then(response => response.json())
19+
.then(data => {
20+
console.log('DATA:', data.access)
21+
if (data.access) {
22+
localStorage.setItem('token', data.access)
23+
window.location = 'file:///C:/Users/Dennis%20Ivy/Desktop/frontend/projects-list.html'
24+
} else {
25+
alert('Username OR password did not work')
26+
}
27+
})
28+
})

frontend/main.css

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
body{
2+
background-color: #f8fafb;
3+
margin: 0;
4+
}
5+
6+
img{
7+
width:100%
8+
}
9+
10+
#projects--wrapper{
11+
max-width:600px;
12+
margin:0 auto;
13+
margin-top:2em;
14+
border:1px solid #eaeaeb;
15+
background-color: #fff;
16+
border-radius: 5px;
17+
}
18+
19+
.project--card{
20+
display: grid;
21+
grid-template-columns:1fr 3fr;
22+
gap:2em;
23+
margin-top:2em;
24+
padding: 1em;
25+
align-items: center;
26+
27+
}
28+
29+
.card--header{
30+
display: flex;
31+
align-items: center;
32+
gap:2em;
33+
}
34+
35+
.project--card p{
36+
color: #737373;
37+
}
38+
39+
.vote--option{
40+
font-size: 24px;
41+
color:#737373 ;
42+
cursor: pointer;
43+
}
44+
45+
.vote--option:hover{
46+
color:#000;
47+
}
48+
49+
#form-wrapper{
50+
max-width:600px;
51+
margin:0 auto;
52+
margin-top:2em;
53+
border:1px solid #eaeaeb;
54+
background-color: #fff;
55+
border-radius: 5px;
56+
padding:2em;
57+
}

0 commit comments

Comments
 (0)