Committed by
GitHub
Merge pull request #22 from kairos03/feature/user
Feature/user
Showing
26 changed files
with
363 additions
and
62 deletions
... | @@ -38,7 +38,8 @@ INSTALLED_APPS = [ | ... | @@ -38,7 +38,8 @@ INSTALLED_APPS = [ |
38 | 'django.contrib.messages', | 38 | 'django.contrib.messages', |
39 | 'django.contrib.staticfiles', | 39 | 'django.contrib.staticfiles', |
40 | 'rest_framework', | 40 | 'rest_framework', |
41 | - 'restful.apps.RestfulConfig' | 41 | + 'restful.apps.RestfulConfig', |
42 | + 'website' | ||
42 | ] | 43 | ] |
43 | 44 | ||
44 | MIDDLEWARE = [ | 45 | MIDDLEWARE = [ |
... | @@ -105,7 +106,7 @@ AUTH_PASSWORD_VALIDATORS = [ | ... | @@ -105,7 +106,7 @@ AUTH_PASSWORD_VALIDATORS = [ |
105 | # Internationalization | 106 | # Internationalization |
106 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ | 107 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ |
107 | 108 | ||
108 | -LANGUAGE_CODE = 'en-us' | 109 | +LANGUAGE_CODE = 'ko_kr' |
109 | 110 | ||
110 | TIME_ZONE = 'UTC' | 111 | TIME_ZONE = 'UTC' |
111 | 112 | ||
... | @@ -120,3 +121,8 @@ USE_TZ = True | ... | @@ -120,3 +121,8 @@ USE_TZ = True |
120 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ | 121 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ |
121 | 122 | ||
122 | STATIC_URL = '/static/' | 123 | STATIC_URL = '/static/' |
124 | + | ||
125 | + | ||
126 | +# Login redirect | ||
127 | + | ||
128 | +LOGIN_REDIRECT_URL = '/' | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | -"""dcloud URL Configuration | ||
2 | - | ||
3 | -The `urlpatterns` list routes URLs to views. For more information please see: | ||
4 | - https://docs.djangoproject.com/en/2.0/topics/http/urls/ | ||
5 | -Examples: | ||
6 | -Function views | ||
7 | - 1. Add an import: from my_app import views | ||
8 | - 2. Add a URL to urlpatterns: path('', views.home, name='home') | ||
9 | -Class-based views | ||
10 | - 1. Add an import: from other_app.views import Home | ||
11 | - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') | ||
12 | -Including another URLconf | ||
13 | - 1. Import the include() function: from django.urls import include, path | ||
14 | - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) | ||
15 | -""" | ||
16 | from django.contrib import admin | 1 | from django.contrib import admin |
17 | from django.conf.urls import url, include | 2 | from django.conf.urls import url, include |
3 | +from django.contrib.auth import views | ||
18 | 4 | ||
19 | urlpatterns = [ | 5 | urlpatterns = [ |
20 | - url('admin/', admin.site.urls), | 6 | + url(r'^admin/', admin.site.urls), |
21 | - url(r'^', include('restful.urls')), | 7 | + url(r'^restapi/', include('restful.urls')), |
8 | + url(r'^', include('website.urls')), | ||
9 | + | ||
10 | + url(r'^accounts/login/$', views.login, name='login'), | ||
11 | + url(r'^accounts/logout/$', views.logout, name='logout', kwargs={'next_page': '/'}), | ||
12 | + | ||
22 | ] | 13 | ] |
14 | + | ... | ... |
dcloud/rest_mig.sh
0 → 100755
1 | from django.db import models | 1 | from django.db import models |
2 | 2 | ||
3 | - | ||
4 | # Create your models here. | 3 | # Create your models here. |
5 | class File(models.Model): | 4 | class File(models.Model): |
6 | created = models.DateTimeField(auto_now_add=True) | 5 | created = models.DateTimeField(auto_now_add=True) |
7 | modified = models.DateTimeField(auto_now=True) | 6 | modified = models.DateTimeField(auto_now=True) |
8 | title = models.CharField(max_length=100) | 7 | title = models.CharField(max_length=100) |
8 | + # file_name = models.CharField(max_length=100, primary_key=True) | ||
9 | + | ||
9 | object_key = models.CharField(max_length=1025) | 10 | object_key = models.CharField(max_length=1025) |
10 | - size = models.IntegerField() | ||
11 | # owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE) | 11 | # owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE) |
12 | 12 | ||
13 | class Meta: | 13 | class Meta: |
14 | - ordering = ('title',) | 14 | + ordering = ('pk',) |
15 | - | ||
16 | - | ... | ... |
dcloud/restful/s3_interface.py
0 → 100644
1 | +import boto3 | ||
2 | +import json | ||
3 | + | ||
4 | +S3 = boto3.client('s3') | ||
5 | +BUCKET = '2018-dcloud' | ||
6 | + | ||
7 | + | ||
8 | +def list_path(bucket, user, path): | ||
9 | + | ||
10 | + files = [] | ||
11 | + # get list | ||
12 | + objects = S3.list_objects(Bucket=bucket, Prefix='{}/{}'.format(user, path), Delimiter='/') | ||
13 | + | ||
14 | + # get sub directorys | ||
15 | + common_prefixes = objects.get('CommonPrefixes') | ||
16 | + if common_prefixes: | ||
17 | + for obj in common_prefixes: | ||
18 | + files.append({'type':'diretory', 'name':obj.get('Prefix').split('/')[-2]}) | ||
19 | + | ||
20 | + # get files | ||
21 | + contents = objects.get('Contents') | ||
22 | + if contents: | ||
23 | + for obj in contents: | ||
24 | + file = obj.get('Key').split('/')[-1] | ||
25 | + if file != '': | ||
26 | + files.append({'type':'file', 'name':file}) | ||
27 | + | ||
28 | + return {'files':files} | ||
29 | + | ||
30 | +# print(list_path(BUCKET, 'test1', '')) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -3,30 +3,8 @@ from rest_framework import serializers | ... | @@ -3,30 +3,8 @@ from rest_framework import serializers |
3 | from restful.models import File | 3 | from restful.models import File |
4 | 4 | ||
5 | 5 | ||
6 | -class FileSerializer(serializers.Serializer): | 6 | +class FileSerializer(serializers.ModelSerializer): |
7 | - pk = serializers.IntegerField(read_only=True) | ||
8 | - created = serializers.DateTimeField(read_only=True) | ||
9 | - modified = serializers.DateTimeField(read_only=True) | ||
10 | - title = serializers.CharField(max_length=100) | ||
11 | - object_key = serializers.CharField(max_length=1025) | ||
12 | - size = serializers.IntegerField() | ||
13 | 7 | ||
14 | - | 8 | + class Meta: |
15 | - def create(self, validated_data): | 9 | + model = File |
16 | - """ | 10 | + fields = ('created', 'updated', 'object_key') |
17 | - Create and Return new `File` instance. Using validated_data. | ||
18 | - """ | ||
19 | - return File.objects.create(**validated_data) | ||
20 | - | ||
21 | - | ||
22 | - def update(self, instance, validated_data): | ||
23 | - """ | ||
24 | - Update and Return existing `File` instance. Using validated_data. | ||
25 | - """ | ||
26 | - instance.title = validated_data.get('title', instance.title) | ||
27 | - instance.object_key = validated_data.get('object_key', instance.object_key) | ||
28 | - instance.size = validated_data.get('size', instance.size) | ||
29 | - instance.language = validated_data.get('language', instance.language) | ||
30 | - instance.style = validated_data.get('style', instance.style) | ||
31 | - instance.save() | ||
32 | - return instance | ... | ... |
1 | -from django.test import TestCase | 1 | +from rest_framework.test import APITestCase |
2 | +from django.urls import reverse | ||
3 | +from rest_framework import status | ||
4 | +from restful.models import File | ||
5 | + | ||
6 | + | ||
7 | +class FileListTestCase(APITestCase): | ||
8 | + | ||
9 | + def setUp(self): | ||
10 | + self.tearDown() | ||
11 | + | ||
12 | + def tearDown(self): | ||
13 | + pass | ||
14 | + | ||
15 | + def test_upload(self): | ||
16 | + url = reverse('file-list') | ||
17 | + data = {'object_key': 'test_object_key'} | ||
18 | + response = self.client.post(url, data) | ||
19 | + self.assertEqual(response.status_code, status.HTTP_201_CREATED) | ||
20 | + self.assertEqual(File.objects.count(), 1) | ||
21 | + | ||
22 | + def test_list(self): | ||
23 | + url = reverse('file-list') | ||
24 | + response = self.client.get(url) | ||
25 | + self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
26 | + | ||
27 | + | ||
28 | +class FileDetailTestCase(APITestCase): | ||
29 | + | ||
30 | + def setUp(self): | ||
31 | + self.tearDown() | ||
32 | + File.objects.create(object_key='test_object') | ||
33 | + | ||
34 | + def tearDown(self): | ||
35 | + File.objects.all().delete() | ||
36 | + | ||
37 | + def test_delete(self): | ||
38 | + url = reverse('file-detail', kwargs={'pk' : 1 }) | ||
39 | + response = self.client.delete(url) | ||
40 | + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) | ||
41 | + | ||
42 | + def test_update(self): | ||
43 | + url = reverse('file-detail', kwargs={'pk' : 1 }) | ||
44 | + response = self.client.put(url, {"object_key":"test_update"}) | ||
45 | + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) | ||
46 | + | ||
47 | + def test_retrieve(self): | ||
48 | + url = reverse('file-detail', kwargs={'pk' : 1 }) | ||
49 | + response = self.client.get(url) | ||
50 | + self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
51 | + | ||
52 | + | ||
2 | 53 | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
3 | -# Create your tests here. | ... | ... |
1 | from django.conf.urls import url | 1 | from django.conf.urls import url |
2 | +from django.shortcuts import redirect | ||
2 | from rest_framework.urlpatterns import format_suffix_patterns | 3 | from rest_framework.urlpatterns import format_suffix_patterns |
3 | from restful import views | 4 | from restful import views |
4 | 5 | ||
5 | urlpatterns = [ | 6 | urlpatterns = [ |
6 | - url(r'^files/$', views.FileList.as_view()), | 7 | + url(r'^files/(?P<path>([a-zA-z0-9가-힣._-]*/)*)$', views.FileList.as_view(), name='file-list'), |
7 | url(r'^files/(?P<pk>[0-9]+)/$', views.FileDetail.as_view()), | 8 | url(r'^files/(?P<pk>[0-9]+)/$', views.FileDetail.as_view()), |
8 | ] | 9 | ] |
9 | 10 | ... | ... |
1 | -from restful.models import File | ||
2 | -from restful.serializers import FileSerializer | ||
3 | from django.http import Http404 | 1 | from django.http import Http404 |
2 | +from django.contrib.auth.decorators import login_required | ||
4 | from rest_framework.views import APIView | 3 | from rest_framework.views import APIView |
5 | from rest_framework.response import Response | 4 | from rest_framework.response import Response |
6 | from rest_framework import status | 5 | from rest_framework import status |
6 | +from restful import s3_interface | ||
7 | 7 | ||
8 | +from restful.models import File | ||
9 | +from restful.serializers import FileSerializer | ||
8 | 10 | ||
9 | -# Create your views here. | ||
10 | class FileList(APIView): | 11 | class FileList(APIView): |
11 | """ | 12 | """ |
12 | List all file, or create a new snippet. | 13 | List all file, or create a new snippet. |
13 | """ | 14 | """ |
14 | 15 | ||
15 | - def get(self, request, format=None): | 16 | + def get(self, request, path='/', format=None): |
16 | - file = File.objects.all() | 17 | + # file = File.objects.all() |
17 | - serializer = FileSerializer(file, many=True) | 18 | + # serializer = FileSerializer(file, many=True) |
18 | - return Response(serializer.data) | 19 | + # print(serializer.data) |
20 | + # return Response(serializer.data) | ||
21 | + data = s3_interface.list_path(s3_interface.BUCKET, 'test1', path) | ||
22 | + return Response(data) | ||
23 | + | ||
19 | 24 | ||
20 | def post(self, request, format=None): | 25 | def post(self, request, format=None): |
21 | serializer = FileSerializer(data=request.data) | 26 | serializer = FileSerializer(data=request.data) |
... | @@ -38,14 +43,14 @@ class FileDetail(APIView): | ... | @@ -38,14 +43,14 @@ class FileDetail(APIView): |
38 | def get(self, request, pk, format=None): | 43 | def get(self, request, pk, format=None): |
39 | file = self.get_object(pk) | 44 | file = self.get_object(pk) |
40 | serializer = FileSerializer(file) | 45 | serializer = FileSerializer(file) |
41 | - return Response(serializer.data) | 46 | + return Response(serializer.data, status=status.HTTP_200_OK) |
42 | 47 | ||
43 | def put(self, request, pk, format=None): | 48 | def put(self, request, pk, format=None): |
44 | file = self.get_object(pk) | 49 | file = self.get_object(pk) |
45 | serializer = FileSerializer(file, data=request.data) | 50 | serializer = FileSerializer(file, data=request.data) |
46 | if serializer.is_valid(): | 51 | if serializer.is_valid(): |
47 | serializer.save() | 52 | serializer.save() |
48 | - return Response(serializer.data) | 53 | + return Response(serializer.data, status=status.HTTP_204_NO_CONTENT) |
49 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) | 54 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) |
50 | 55 | ||
51 | def delete(self, request, pk, format=None): | 56 | def delete(self, request, pk, format=None): | ... | ... |
dcloud/website/__init__.py
0 → 100644
File mode changed
dcloud/website/admin.py
0 → 100644
dcloud/website/apps.py
0 → 100644
dcloud/website/auth_views.py
0 → 100644
1 | +from django.contrib.auth import login, authenticate, logout | ||
2 | +from django.contrib.auth.forms import UserCreationForm | ||
3 | +from django.shortcuts import render, redirect | ||
4 | +from django.contrib.auth.decorators import login_required | ||
5 | + | ||
6 | +def signup(request): | ||
7 | + if request.method == 'POST': | ||
8 | + form = UserCreationForm(request.POST) | ||
9 | + if form.is_valid(): | ||
10 | + form.save() | ||
11 | + username = form.cleaned_data.get('username') | ||
12 | + raw_password = form.cleaned_data.get('password1') | ||
13 | + user = authenticate(username=username, password=raw_password) | ||
14 | + login(request, user) | ||
15 | + return redirect('/') | ||
16 | + else: | ||
17 | + form = UserCreationForm() | ||
18 | + return render(request, 'registration/signup.html', {'form': form}) | ||
19 | + | ||
20 | + | ||
21 | +@login_required | ||
22 | +def delete_account(request): | ||
23 | + if request.method == 'GET': | ||
24 | + return render(request, 'registration/delete_account.html') | ||
25 | + elif request.method == 'POST': | ||
26 | + if request.POST.get('yes'): | ||
27 | + return redirect('delete_account_success') | ||
28 | + else: | ||
29 | + return redirect('/') | ||
30 | + | ||
31 | +@login_required | ||
32 | +def delete_account_success(request): | ||
33 | + if request.method == 'GET': | ||
34 | + # TODO Add delete account | ||
35 | + logout(request) | ||
36 | + return render(request, 'registration/delete_account_success.html') | ||
37 | + |
dcloud/website/forms.py
0 → 100644
dcloud/website/models.py
0 → 100644
dcloud/website/static/css/posts.css
0 → 100644
1 | +h1 a { | ||
2 | + color: #FCA205; | ||
3 | + font-family: 'Lobster'; | ||
4 | +} | ||
5 | + | ||
6 | +body { | ||
7 | + padding-left: 15px; | ||
8 | +} | ||
9 | + | ||
10 | +.page-header { | ||
11 | + background-color: #ff9400; | ||
12 | + margin-top: 0; | ||
13 | + padding: 20px 20px 20px 40px; | ||
14 | +} | ||
15 | + | ||
16 | +.page-header h1, .page-header h1 a, .page-header h1 a:visited, .page-header h1 a:active { | ||
17 | + color: #ffffff; | ||
18 | + font-size: 36pt; | ||
19 | + text-decoration: none; | ||
20 | +} | ||
21 | + | ||
22 | +.content { | ||
23 | + margin-left: 40px; | ||
24 | +} | ||
25 | + | ||
26 | +h1, h2, h3, h4 { | ||
27 | + font-family: 'Lobster', cursive; | ||
28 | +} | ||
29 | + | ||
30 | +.date { | ||
31 | + color: #828282; | ||
32 | +} | ||
33 | + | ||
34 | +.save { | ||
35 | + float: right; | ||
36 | +} | ||
37 | + | ||
38 | +.post-form textarea, .post-form input { | ||
39 | + width: 100%; | ||
40 | +} | ||
41 | + | ||
42 | +.top-menu, .top-menu:hover, .top-menu:visited { | ||
43 | + color: #ffffff; | ||
44 | + float: right; | ||
45 | + font-size: 26pt; | ||
46 | + margin-right: 20px; | ||
47 | +} | ||
48 | + | ||
49 | +.post { | ||
50 | + margin-bottom: 70px; | ||
51 | +} | ||
52 | + | ||
53 | +.post h1 a, .post h1 a:visited { | ||
54 | + color: #000000; | ||
55 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{% extends "website/baseline.html" %} | ||
2 | + | ||
3 | +{% block content %} | ||
4 | +{% if user.is_authenticated %} | ||
5 | +<h1> {{user.username}} really want to delete your account? </h1> | ||
6 | +<form action='#' method="POST"> | ||
7 | + {% csrf_token %} | ||
8 | + <input type="submit" value="yes" name="yes"> | ||
9 | + <input type="submit" value="no" name="no"> | ||
10 | +</form> | ||
11 | +{% endif %} | ||
12 | +{% endblock %} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{% extends "website/baseline.html" %} | ||
2 | + | ||
3 | +{% block content %} | ||
4 | + {% if form.errors %} | ||
5 | + <p>이름과 비밀번호가 일치하지 않습니다. 다시 시도해주세요.</p> | ||
6 | + {% endif %} | ||
7 | + | ||
8 | + <form method="post" action="{% url 'login' %}"> | ||
9 | + {% csrf_token %} | ||
10 | + <table> | ||
11 | + <tr> | ||
12 | + <td>{{ form.username.label_tag }}</td> | ||
13 | + <td>{{ form.username }}</td> | ||
14 | + </tr> | ||
15 | + <tr> | ||
16 | + <td>{{ form.password.label_tag }}</td> | ||
17 | + <td>{{ form.password }}</td> | ||
18 | + </tr> | ||
19 | + </table> | ||
20 | + | ||
21 | + <input type="submit" value="login" /> | ||
22 | + <input type="hidden" name="next" value="{{ next }}" /> | ||
23 | + </form> | ||
24 | +{% endblock %} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{% extends 'website/baseline.html' %} {% block content %} | ||
2 | +<h2>Sign up</h2> | ||
3 | +<form method="post"> | ||
4 | + {% csrf_token %} {% for field in form %} | ||
5 | + <p> | ||
6 | + {{ field.label_tag }} | ||
7 | + <br> {{ field }} {% if field.help_text %} | ||
8 | + <small style="color: grey">{% autoescape off %}{{ field.help_text }}{% endautoescape %}</small> | ||
9 | + {% endif %} {% for error in field.errors %} | ||
10 | + <p style="color: red">{{ error }}</p> | ||
11 | + {% endfor %} | ||
12 | + </p> | ||
13 | + {% endfor %} | ||
14 | + <button type="submit">Sign up</button> | ||
15 | +</form> | ||
16 | +{% endblock %} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +<html> | ||
2 | + <header> | ||
3 | + <title>D.cloud</title> | ||
4 | + </header> | ||
5 | + <body> | ||
6 | + <div class="page-header"> | ||
7 | + {% if user.is_authenticated %} | ||
8 | + <p class="top-menu">Hello {{ user.username }} <small>(<a href="{% url 'logout' %}">Log out</a>)</small></p> | ||
9 | + {% else %} | ||
10 | + <a href="{% url 'login' %}" class="top-menu">Log in<span class="glyphicon glyphicon-lock"></span></a> | ||
11 | + {% endif %} | ||
12 | + <h1><a href="/">D.cloud</a></h1> | ||
13 | + </div> | ||
14 | + <div class="content"> | ||
15 | + {% block content %} | ||
16 | + {% endblock %} | ||
17 | + </div> | ||
18 | + <div class="page-footer"> | ||
19 | + | ||
20 | + </div> | ||
21 | + </body> | ||
22 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
dcloud/website/templates/website/home.html
0 → 100644
dcloud/website/tests.py
0 → 100644
dcloud/website/urls.py
0 → 100644
1 | +from django.conf.urls import url | ||
2 | +from django.shortcuts import redirect | ||
3 | +from website import views, auth_views | ||
4 | + | ||
5 | +urlpatterns = [ | ||
6 | + | ||
7 | + url(r'^accounts/signup/$', auth_views.signup, name='signup'), | ||
8 | + url(r'^accounts/delete_account/$', auth_views.delete_account, name='delete_account'), | ||
9 | + url(r'^accounts/delete_account_success/$', auth_views.delete_account_success, name='delete_account_success'), | ||
10 | + | ||
11 | + # blog | ||
12 | + url(r'^$', views.home), | ||
13 | + url(r'^files/', views.file_list, name='file_list'), | ||
14 | +] | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
dcloud/website/views.py
0 → 100644
1 | +from django.shortcuts import render, get_object_or_404, redirect, Http404 | ||
2 | +from django.utils import timezone | ||
3 | +from django.contrib.auth.decorators import login_required | ||
4 | +from restful.models import File | ||
5 | +import requests | ||
6 | + | ||
7 | + | ||
8 | +def home(request): | ||
9 | + return render(request, 'website/home.html') | ||
10 | + | ||
11 | + | ||
12 | +@login_required | ||
13 | +def file_list(request): | ||
14 | + files = requests.get('http://localhost:8000/restapi/files') | ||
15 | + files = files.json() | ||
16 | + return render(request, 'website/file_list.html', files) |
-
Please register or login to post a comment