Showing
7 changed files
with
267 additions
and
22 deletions
| ... | @@ -125,6 +125,7 @@ STATIC_URL = '/static/' | ... | @@ -125,6 +125,7 @@ STATIC_URL = '/static/' |
| 125 | 125 | ||
| 126 | 126 | ||
| 127 | # Custom Settings | 127 | # Custom Settings |
| 128 | +S3_BUCKET = 'khubox-files' | ||
| 128 | CDN_PATH = 'https://khubox-files.khunet.net' | 129 | CDN_PATH = 'https://khubox-files.khunet.net' |
| 129 | CLOUDFRONT_KEY_ID = 'APKAJ3FOBWI34OZJTXJQ' | 130 | CLOUDFRONT_KEY_ID = 'APKAJ3FOBWI34OZJTXJQ' |
| 130 | CLOUDFRONT_KEY_PRIVATE = '-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA18VtzURs+fQev5L00LRwRbJaObQI4kfJCIsOE7eWSOqq4Akh\nA7fI6vs3z8orXBvgc+k6GgAHrIdNwckxoQuTsCxrTDm1104qy1T4JkVxkIBYHJgH\nGzKUloK5IqdmcYbOK7IQeHJ2gR9Mv/3oKUytJSsrbM9k4oLrsxGpyEuJeHIg28aP\nwhoVWmBGcPu48l4aYAZEVY7LZRJSOQ9y7Lf8FS1u7Xtw1P91gEaqrqVXqRWY02C8\nsixpJJuiAPnM3rpcpVNlAaPdDkWmaWYJoJDOlce7Dmx1a9Ckr24krM//vpEljurC\nGml0AsHpL8LE9msM5VA+miCxCz/K+wDgm2xvvQIDAQABAoIBAFmP3pLceyuJVBYK\n5smWjB+x91eKTkG2sFB2f8JZau0bUxApWeXULHa1DiaW8UaLX7BdN7vBFW5cvz7X\nx1zklEoFNghuz/btwD+kJlikbI4hZ/F+fTyh0yFiY3xp5dDrtrpWcBW+1UeleVMc\nDnjOFfSepajFsUeANlue0k2MZSRz34s2T2scV5ZkooqdXddYUF/wDhefYm6uvCgI\nPyvY/mbJTyhte/xagY/m6yzk5gxgad0qP2ZZrHhLLMlJ/GEZToWDxD2xUei61NQT\nFFc5ZutkAE6fVb3I4SJUBSX5fl0tTMz4Aak1GP2phMhjZyjYnQMq8kvL4BNFb7gp\nary8W/UCgYEA+eKkfjjlPsEx6yHMhD7pAwy/MpUqJmF5LMxIG+qfd+GZMQ2oucn5\npUAwBHP7BD6E9H7/7jdjnCiO+iPrzM9vNLfqsdCtPWzoFYJp/6Fv002uX8seNYvJ\nQyQqrM85LYIghhnkcmJMA8GR/Iu5ZEeE2BkAl9T2EKclzmB62d/ki6MCgYEA3Q0V\nz08IEwSJW+jEsOM+XGg2YkNqCVKGQD9n4CPx0TFVJxfqFl2nVwlN2hfrlJLUQ9+l\nfXnS5AW3tE88t9we+ea0saJZEqqlm/rGsfTV/twS9cWSgvG5fTzhUbu9/ElMU29L\nmydQfWTvCup7zCuQtgwM5ZRtPwuKsI8urUg6zR8CgYAt0coZvvMCI8i0dbkbkrGF\nNqQkcUeOTBc9CKQ8QjRFdh9x6DBFCOz2ySNE3cNsTs5wSo1BL/Ta4HD/GvEU2ABr\nKUImor3xYnPX5dbr4b0wgLD1rbf3V49q+Um98C1q086E6GCEPNP1aFwNc81lvtt0\nCHmcXZdVDGEZS4WbR7uPgwKBgBO/moY12lPQoPDsH75p3uVkjg9DVJLWo5XT1FTr\nASyeSqw+b7Rl05BsDV+BqZNRdtNFhMRsANJMTHg4aAVJDh9nZBdGmMyZIEiKI/w8\nEm49fRgl+YvnSpoMuViS/EswxTfjBo8q+P7q6IxCHKNF9Ry+gNx14TizsEVL1XC3\ntkEjAoGBAMyp7wdPobJMXcclRVq6rqHs9OMcnZAveVKyxNgDbZu4OB5X4xTxGEYT\nNZQ0MFf/HcwlnH7797gVQeqF9dlqUJYe+Fc8lc/Rcwta/4R5uMgri9t8RKN91YKF\nUUFBsDEkWlkoAmfPkcrrq9cLJlmSNt3ehQj4p5iAJwoVBXXa++PO\n-----END RSA PRIVATE KEY-----' | 131 | CLOUDFRONT_KEY_PRIVATE = '-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA18VtzURs+fQev5L00LRwRbJaObQI4kfJCIsOE7eWSOqq4Akh\nA7fI6vs3z8orXBvgc+k6GgAHrIdNwckxoQuTsCxrTDm1104qy1T4JkVxkIBYHJgH\nGzKUloK5IqdmcYbOK7IQeHJ2gR9Mv/3oKUytJSsrbM9k4oLrsxGpyEuJeHIg28aP\nwhoVWmBGcPu48l4aYAZEVY7LZRJSOQ9y7Lf8FS1u7Xtw1P91gEaqrqVXqRWY02C8\nsixpJJuiAPnM3rpcpVNlAaPdDkWmaWYJoJDOlce7Dmx1a9Ckr24krM//vpEljurC\nGml0AsHpL8LE9msM5VA+miCxCz/K+wDgm2xvvQIDAQABAoIBAFmP3pLceyuJVBYK\n5smWjB+x91eKTkG2sFB2f8JZau0bUxApWeXULHa1DiaW8UaLX7BdN7vBFW5cvz7X\nx1zklEoFNghuz/btwD+kJlikbI4hZ/F+fTyh0yFiY3xp5dDrtrpWcBW+1UeleVMc\nDnjOFfSepajFsUeANlue0k2MZSRz34s2T2scV5ZkooqdXddYUF/wDhefYm6uvCgI\nPyvY/mbJTyhte/xagY/m6yzk5gxgad0qP2ZZrHhLLMlJ/GEZToWDxD2xUei61NQT\nFFc5ZutkAE6fVb3I4SJUBSX5fl0tTMz4Aak1GP2phMhjZyjYnQMq8kvL4BNFb7gp\nary8W/UCgYEA+eKkfjjlPsEx6yHMhD7pAwy/MpUqJmF5LMxIG+qfd+GZMQ2oucn5\npUAwBHP7BD6E9H7/7jdjnCiO+iPrzM9vNLfqsdCtPWzoFYJp/6Fv002uX8seNYvJ\nQyQqrM85LYIghhnkcmJMA8GR/Iu5ZEeE2BkAl9T2EKclzmB62d/ki6MCgYEA3Q0V\nz08IEwSJW+jEsOM+XGg2YkNqCVKGQD9n4CPx0TFVJxfqFl2nVwlN2hfrlJLUQ9+l\nfXnS5AW3tE88t9we+ea0saJZEqqlm/rGsfTV/twS9cWSgvG5fTzhUbu9/ElMU29L\nmydQfWTvCup7zCuQtgwM5ZRtPwuKsI8urUg6zR8CgYAt0coZvvMCI8i0dbkbkrGF\nNqQkcUeOTBc9CKQ8QjRFdh9x6DBFCOz2ySNE3cNsTs5wSo1BL/Ta4HD/GvEU2ABr\nKUImor3xYnPX5dbr4b0wgLD1rbf3V49q+Um98C1q086E6GCEPNP1aFwNc81lvtt0\nCHmcXZdVDGEZS4WbR7uPgwKBgBO/moY12lPQoPDsH75p3uVkjg9DVJLWo5XT1FTr\nASyeSqw+b7Rl05BsDV+BqZNRdtNFhMRsANJMTHg4aAVJDh9nZBdGmMyZIEiKI/w8\nEm49fRgl+YvnSpoMuViS/EswxTfjBo8q+P7q6IxCHKNF9Ry+gNx14TizsEVL1XC3\ntkEjAoGBAMyp7wdPobJMXcclRVq6rqHs9OMcnZAveVKyxNgDbZu4OB5X4xTxGEYT\nNZQ0MFf/HcwlnH7797gVQeqF9dlqUJYe+Fc8lc/Rcwta/4R5uMgri9t8RKN91YKF\nUUFBsDEkWlkoAmfPkcrrq9cLJlmSNt3ehQj4p5iAJwoVBXXa++PO\n-----END RSA PRIVATE KEY-----' | ... | ... |
| 1 | +import boto3 | ||
| 1 | import datetime | 2 | import datetime |
| 2 | from botocore.signers import CloudFrontSigner | 3 | from botocore.signers import CloudFrontSigner |
| 3 | from cryptography.hazmat.backends import default_backend | 4 | from cryptography.hazmat.backends import default_backend |
| ... | @@ -15,8 +16,39 @@ def rsa_signer(message): | ... | @@ -15,8 +16,39 @@ def rsa_signer(message): |
| 15 | return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1()) | 16 | return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1()) |
| 16 | 17 | ||
| 17 | 18 | ||
| 18 | -def sign(url): | 19 | +def sign_download(url): |
| 19 | - expire_date = datetime.datetime(2020, 6, 10) | 20 | + expire_date = datetime.datetime.utcnow() + datetime.timedelta(hours=1) |
| 20 | cloudfront_signer = CloudFrontSigner(settings.CLOUDFRONT_KEY_ID, rsa_signer) | 21 | cloudfront_signer = CloudFrontSigner(settings.CLOUDFRONT_KEY_ID, rsa_signer) |
| 21 | signed_url = cloudfront_signer.generate_presigned_url(url, date_less_than=expire_date) | 22 | signed_url = cloudfront_signer.generate_presigned_url(url, date_less_than=expire_date) |
| 22 | return signed_url | 23 | return signed_url |
| 24 | + | ||
| 25 | + | ||
| 26 | +def sign_upload(file_id): | ||
| 27 | + s3 = boto3.client('s3') | ||
| 28 | + signed_url = s3.generate_presigned_url( | ||
| 29 | + 'put_object', | ||
| 30 | + Params={'Bucket': settings.S3_BUCKET, 'Key': file_id}, | ||
| 31 | + ExpiresIn=3600, | ||
| 32 | + HttpMethod='PUT' | ||
| 33 | + ) | ||
| 34 | + return signed_url | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +def s3_delete(del_list): | ||
| 38 | + s3 = boto3.resource('s3') | ||
| 39 | + bucket = s3.Bucket(settings.S3_BUCKET) | ||
| 40 | + del_s3_list = [] | ||
| 41 | + for key in del_list: | ||
| 42 | + del_s3_list.append({'Key': key}) | ||
| 43 | + bucket.delete_objects(Delete={'Objects': del_s3_list}) | ||
| 44 | + | ||
| 45 | + | ||
| 46 | +def s3_copy(file_id, new_file_id): | ||
| 47 | + s3 = boto3.resource('s3') | ||
| 48 | + bucket = s3.Bucket(settings.S3_BUCKET) | ||
| 49 | + copy_source = { | ||
| 50 | + 'Bucket': settings.S3_BUCKET, | ||
| 51 | + 'Key': file_id | ||
| 52 | + } | ||
| 53 | + obj = bucket.Object(str(new_file_id)) | ||
| 54 | + obj.copy(copy_source) | ... | ... |
| ... | @@ -5,7 +5,7 @@ from . import files, groups, users | ... | @@ -5,7 +5,7 @@ from . import files, groups, users |
| 5 | urlpatterns = [ | 5 | urlpatterns = [ |
| 6 | url(r'^files$', files.index), # 폴더 생성, 파일 업로드, 폴더/파일 목록 | 6 | url(r'^files$', files.index), # 폴더 생성, 파일 업로드, 폴더/파일 목록 |
| 7 | url(r'^files/trash$', files.trash), # 휴지통 비우기 | 7 | url(r'^files/trash$', files.trash), # 휴지통 비우기 |
| 8 | - url(r'^files/(?P<file_id>[-\w]+)$', files.item), # 폴더/파일 조회, 파일 다운로드, 폴더/파일 수정 | 8 | + url(r'^files/(?P<file_id>[-\w]+)$', files.item), # 폴더/파일 조회, 폴더/파일 수정 |
| 9 | url(r'^files/(?P<file_id>[-\w]+)/copy$', files.copy), # 파일 복제 | 9 | url(r'^files/(?P<file_id>[-\w]+)/copy$', files.copy), # 파일 복제 |
| 10 | url(r'^groups$', groups.index), # 그룹 생성 | 10 | url(r'^groups$', groups.index), # 그룹 생성 |
| 11 | url(r'^groups/invite/(?P<invite_code>[-\w]+)$', groups.invite), # 그룹 초대장 조회, 그룹 초대장 사용 | 11 | url(r'^groups/invite/(?P<invite_code>[-\w]+)$', groups.invite), # 그룹 초대장 조회, 그룹 초대장 사용 | ... | ... |
| ... | @@ -20,7 +20,7 @@ def trash(request): | ... | @@ -20,7 +20,7 @@ def trash(request): |
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | def item(request, file_id): | 22 | def item(request, file_id): |
| 23 | - # 폴더/파일 조회, 파일 다운로드 | 23 | + # 폴더/파일 조회 |
| 24 | if request.method == 'GET': | 24 | if request.method == 'GET': |
| 25 | return JsonResponse(files.find_item(request, file_id)) | 25 | return JsonResponse(files.find_item(request, file_id)) |
| 26 | # 폴더/파일 수정 | 26 | # 폴더/파일 수정 | ... | ... |
| ... | @@ -26,7 +26,7 @@ class Migration(migrations.Migration): | ... | @@ -26,7 +26,7 @@ class Migration(migrations.Migration): |
| 26 | ('size', models.BigIntegerField()), | 26 | ('size', models.BigIntegerField()), |
| 27 | ('is_public', models.IntegerField(default=0)), | 27 | ('is_public', models.IntegerField(default=0)), |
| 28 | ('is_starred', models.IntegerField(default=0)), | 28 | ('is_starred', models.IntegerField(default=0)), |
| 29 | - ('is_trahsed', models.IntegerField(default=0)), | 29 | + ('is_trashed', models.IntegerField(default=0)), |
| 30 | ('created_at', models.DateTimeField()), | 30 | ('created_at', models.DateTimeField()), |
| 31 | ('deleted_at', models.DateTimeField(blank=True, null=True)), | 31 | ('deleted_at', models.DateTimeField(blank=True, null=True)), |
| 32 | ], | 32 | ], | ... | ... |
| ... | @@ -12,7 +12,7 @@ class File(models.Model): | ... | @@ -12,7 +12,7 @@ class File(models.Model): |
| 12 | size = models.BigIntegerField() | 12 | size = models.BigIntegerField() |
| 13 | is_public = models.IntegerField(default=0) | 13 | is_public = models.IntegerField(default=0) |
| 14 | is_starred = models.IntegerField(default=0) | 14 | is_starred = models.IntegerField(default=0) |
| 15 | - is_trahsed = models.IntegerField(default=0) | 15 | + is_trashed = models.IntegerField(default=0) |
| 16 | created_at = models.DateTimeField() | 16 | created_at = models.DateTimeField() |
| 17 | deleted_at = models.DateTimeField(blank=True, null=True) | 17 | deleted_at = models.DateTimeField(blank=True, null=True) |
| 18 | 18 | ... | ... |
| 1 | import json | 1 | import json |
| 2 | import uuid | 2 | import uuid |
| 3 | +from django.conf import settings | ||
| 3 | from django.utils import timezone | 4 | from django.utils import timezone |
| 4 | -from ..aws import sign | 5 | +from ..aws import sign_upload, sign_download, s3_copy, s3_delete |
| 5 | from ..models import File, GroupUser | 6 | from ..models import File, GroupUser |
| 6 | 7 | ||
| 7 | 8 | ||
| 8 | -# TODO: 폴더/파일 목록 | 9 | +# 폴더/파일 목록 |
| 9 | def list_item(request): | 10 | def list_item(request): |
| 10 | - return {'result': True} | 11 | + # TODO: Auth |
| 12 | + request.user_id = 1 | ||
| 13 | + | ||
| 14 | + # Validate | ||
| 15 | + if request.GET.get('is_public') != 'true' \ | ||
| 16 | + and request.GET.get('is_starred') != 'true' \ | ||
| 17 | + and request.GET.get('is_trashed') != 'true': | ||
| 18 | + return {'result': False, 'error': '입력이 누락되었습니다.'} | ||
| 19 | + | ||
| 20 | + # Query Files | ||
| 21 | + files = None | ||
| 22 | + if request.GET.get('is_public') == 'true': | ||
| 23 | + files = File.objects.filter(owner_user_id=request.user_id, is_public=1, deleted_at__isnull=True) | ||
| 24 | + elif request.GET.get('is_starred') == 'true': | ||
| 25 | + files = File.objects.filter(owner_user_id=request.user_id, is_starred=1, deleted_at__isnull=True) | ||
| 26 | + elif request.GET.get('is_trashed') == 'true': | ||
| 27 | + files = File.objects.filter(owner_user_id=request.user_id, is_trashed=1, deleted_at__isnull=True) | ||
| 28 | + | ||
| 29 | + # Structure | ||
| 30 | + data = [] | ||
| 31 | + for file in files: | ||
| 32 | + data.append({ | ||
| 33 | + 'id': file.id, | ||
| 34 | + 'type': file.type, | ||
| 35 | + 'name': file.name, | ||
| 36 | + 'size': file.size, | ||
| 37 | + 'is_public': file.is_public, | ||
| 38 | + 'is_starred': file.is_starred, | ||
| 39 | + 'is_trashed': file.is_trashed, | ||
| 40 | + 'created_at': file.created_at, | ||
| 41 | + }) | ||
| 42 | + | ||
| 43 | + return {'result': True, 'data': data} | ||
| 11 | 44 | ||
| 12 | 45 | ||
| 13 | # 폴더 생성, 파일 업로드 | 46 | # 폴더 생성, 파일 업로드 |
| ... | @@ -16,7 +49,10 @@ def create(request): | ... | @@ -16,7 +49,10 @@ def create(request): |
| 16 | request.user_id = 1 | 49 | request.user_id = 1 |
| 17 | 50 | ||
| 18 | # Load | 51 | # Load |
| 19 | - received = json.loads(request.body.decode('utf-8')) | 52 | + try: |
| 53 | + received = json.loads(request.body.decode('utf-8')) | ||
| 54 | + except json.decoder.JSONDecodeError: | ||
| 55 | + return {'result': False, 'error': '입력이 잘못되었습니다.'} | ||
| 20 | 56 | ||
| 21 | # Validate | 57 | # Validate |
| 22 | if 'parent_id' not in received \ | 58 | if 'parent_id' not in received \ |
| ... | @@ -28,7 +64,7 @@ def create(request): | ... | @@ -28,7 +64,7 @@ def create(request): |
| 28 | return {'result': False, 'error': '입력이 잘못되었습니다.'} | 64 | return {'result': False, 'error': '입력이 잘못되었습니다.'} |
| 29 | 65 | ||
| 30 | # Get Parent | 66 | # Get Parent |
| 31 | - parent = File.objects.filter(id=received['parent_id'], is_trahsed=0, deleted_at__isnull=True) | 67 | + parent = File.objects.filter(id=received['parent_id'], is_trashed=0, deleted_at__isnull=True) |
| 32 | 68 | ||
| 33 | # Check Exists | 69 | # Check Exists |
| 34 | if len(parent) == 0: | 70 | if len(parent) == 0: |
| ... | @@ -63,18 +99,17 @@ def create(request): | ... | @@ -63,18 +99,17 @@ def create(request): |
| 63 | return {'result': True, 'file_id': file_id} | 99 | return {'result': True, 'file_id': file_id} |
| 64 | 100 | ||
| 65 | # Return File | 101 | # Return File |
| 66 | - upload_url = 'https://khubox-files.khunet.net/%s' % file_id | 102 | + upload_url = sign_upload(str(file_id)) |
| 67 | - upload_url = sign(upload_url) | ||
| 68 | return {'result': True, 'file_id': file_id, 'upload_url': upload_url} | 103 | return {'result': True, 'file_id': file_id, 'upload_url': upload_url} |
| 69 | 104 | ||
| 70 | 105 | ||
| 71 | -# TODO: 휴지통 비우기 | 106 | +# 휴지통 비우기 |
| 72 | def empty_trash(request): | 107 | def empty_trash(request): |
| 73 | # TODO: Auth | 108 | # TODO: Auth |
| 74 | request.user_id = 1 | 109 | request.user_id = 1 |
| 75 | 110 | ||
| 76 | # Query Files | 111 | # Query Files |
| 77 | - files = File.objects.filter(owner_user_id=request.user_id, is_trahsed=1, deleted_at__isnull=True) | 112 | + files = File.objects.filter(owner_user_id=request.user_id, is_trashed=1, deleted_at__isnull=True) |
| 78 | 113 | ||
| 79 | # First Depth | 114 | # First Depth |
| 80 | del_list = [] | 115 | del_list = [] |
| ... | @@ -92,24 +127,201 @@ def empty_trash(request): | ... | @@ -92,24 +127,201 @@ def empty_trash(request): |
| 92 | for del_file in child_files: | 127 | for del_file in child_files: |
| 93 | del_check.append(del_file.id) | 128 | del_check.append(del_file.id) |
| 94 | 129 | ||
| 95 | - # TODO: S3 Delete | 130 | + # S3 Delete |
| 131 | + s3_delete(del_list) | ||
| 96 | 132 | ||
| 97 | # Update | 133 | # Update |
| 98 | - File.objects.filter(id__in=del_list).update(is_trahsed=1, deleted_at=timezone.now()) | 134 | + File.objects.filter(id__in=del_list).update(is_trashed=1, deleted_at=timezone.now()) |
| 99 | 135 | ||
| 100 | return {'result': True, 'affected': del_list} | 136 | return {'result': True, 'affected': del_list} |
| 101 | 137 | ||
| 102 | 138 | ||
| 103 | -# TODO: 폴더/파일 조회, 파일 다운로드 | 139 | +# 폴더/파일 조회 |
| 104 | def find_item(request, file_id): | 140 | def find_item(request, file_id): |
| 105 | - return {'result': True} | 141 | + # TODO: Auth |
| 142 | + request.user_id = 1 | ||
| 143 | + | ||
| 144 | + # Query | ||
| 145 | + file = File.objects.filter(id=file_id, deleted_at__isnull=True) | ||
| 146 | + | ||
| 147 | + # Check Exists | ||
| 148 | + if len(file) == 0: | ||
| 149 | + return {'result': False, 'error': '잘못된 요청입니다.'} | ||
| 150 | + | ||
| 151 | + # Check Owner | ||
| 152 | + is_auth = False | ||
| 153 | + if file[0].owner_user_id == request.user_id: | ||
| 154 | + is_auth = True | ||
| 155 | + is_my_group = GroupUser.objects.filter(group_id=file[0].owner_group_id, user_id=request.user_id) | ||
| 156 | + if len(is_my_group) != 0: | ||
| 157 | + is_auth = True | ||
| 158 | + | ||
| 159 | + # Check Public | ||
| 160 | + if file[0].is_public == 1: | ||
| 161 | + is_auth = True | ||
| 162 | + parent_id = file[0].parent_id | ||
| 163 | + while True: | ||
| 164 | + if parent_id is None or is_auth: | ||
| 165 | + break | ||
| 166 | + parent_file = File.objects.filter(id=parent_id) | ||
| 167 | + if parent_file[0].is_public == 1: | ||
| 168 | + is_auth = True | ||
| 169 | + parent_id = parent_file[0].parent_id | ||
| 170 | + | ||
| 171 | + # Check Auth | ||
| 172 | + if is_auth is False: | ||
| 173 | + return {'result': False, 'error': '잘못된 요청입니다.'} | ||
| 174 | + | ||
| 175 | + # Return File | ||
| 176 | + if file[0].type == 'file': | ||
| 177 | + download_url = '%s/%s' % (settings.CDN_PATH, file[0].id) | ||
| 178 | + download_url = sign_download(download_url) | ||
| 179 | + data = { | ||
| 180 | + 'id': file[0].id, | ||
| 181 | + 'parent_id': file[0].parent_id, | ||
| 182 | + 'uploader_id': file[0].uploader_id, | ||
| 183 | + 'name': file[0].name, | ||
| 184 | + 'size': file[0].size, | ||
| 185 | + 'is_public': file[0].is_public, | ||
| 186 | + 'is_starred': file[0].is_starred, | ||
| 187 | + 'is_trashed': file[0].is_trashed, | ||
| 188 | + 'created_at': file[0].created_at, | ||
| 189 | + 'download_url': download_url, | ||
| 190 | + } | ||
| 191 | + return {'result': True, 'data': data} | ||
| 192 | + | ||
| 193 | + # Query | ||
| 194 | + files = File.objects.filter(parent_id=file[0].id, is_trashed=0, deleted_at__isnull=True) | ||
| 195 | + | ||
| 196 | + # Structure | ||
| 197 | + data = [] | ||
| 198 | + for file in files: | ||
| 199 | + data.append({ | ||
| 200 | + 'id': file.id, | ||
| 201 | + 'type': file.type, | ||
| 202 | + 'name': file.name, | ||
| 203 | + 'size': file.size, | ||
| 204 | + 'is_public': file.is_public, | ||
| 205 | + 'is_starred': file.is_starred, | ||
| 206 | + 'is_trashed': file.is_trashed, | ||
| 207 | + 'created_at': file.created_at, | ||
| 208 | + }) | ||
| 209 | + | ||
| 210 | + # Return Folder | ||
| 211 | + return {'result': True, 'data': data} | ||
| 106 | 212 | ||
| 107 | 213 | ||
| 108 | -# TODO: 폴더/파일 수정 | 214 | +# 폴더/파일 수정 |
| 109 | def update_item(request, file_id): | 215 | def update_item(request, file_id): |
| 216 | + # TODO: Auth | ||
| 217 | + request.user_id = 1 | ||
| 218 | + | ||
| 219 | + # Load | ||
| 220 | + try: | ||
| 221 | + received = json.loads(request.body.decode('utf-8')) | ||
| 222 | + except json.decoder.JSONDecodeError: | ||
| 223 | + return {'result': False, 'error': '입력이 잘못되었습니다.'} | ||
| 224 | + | ||
| 225 | + # Validate | ||
| 226 | + if 'name' not in received \ | ||
| 227 | + and 'parent_id' not in received \ | ||
| 228 | + and 'is_public' not in received \ | ||
| 229 | + and 'is_starred' not in received \ | ||
| 230 | + and 'is_trashed' not in received: | ||
| 231 | + return {'result': False, 'error': '입력이 누락되었습니다.'} | ||
| 232 | + | ||
| 233 | + # Query | ||
| 234 | + file = File.objects.filter(id=file_id, deleted_at__isnull=True) | ||
| 235 | + | ||
| 236 | + # Check Exists | ||
| 237 | + if len(file) == 0: | ||
| 238 | + return {'result': False, 'error': '잘못된 요청입니다.'} | ||
| 239 | + | ||
| 240 | + # Check Owner | ||
| 241 | + is_auth = False | ||
| 242 | + if file[0].owner_user_id == request.user_id: | ||
| 243 | + is_auth = True | ||
| 244 | + is_my_group = GroupUser.objects.filter(group_id=file[0].owner_group_id, user_id=request.user_id) | ||
| 245 | + if len(is_my_group) != 0 \ | ||
| 246 | + and 'is_public' not in received \ | ||
| 247 | + and 'is_starred' not in received \ | ||
| 248 | + and 'is_trashed' not in received: | ||
| 249 | + is_auth = True | ||
| 250 | + | ||
| 251 | + # Check Parent | ||
| 252 | + if 'parent_id' in received: | ||
| 253 | + parent = File.objects.filter(id=received['parent_id'], type='folder', deleted_at__isnull=True) | ||
| 254 | + if len(parent) == 0: | ||
| 255 | + return {'result': False, 'error': '잘못된 요청입니다.'} | ||
| 256 | + if (is_auth is True or len(is_my_group) != 0) \ | ||
| 257 | + and parent[0].owner_user_id == file[0].owner_user_id \ | ||
| 258 | + and parent[0].owner_group_id == file[0].owner_group_id \ | ||
| 259 | + and file_id != received['parent_id']: | ||
| 260 | + is_auth = True | ||
| 261 | + else: | ||
| 262 | + is_auth = False | ||
| 263 | + | ||
| 264 | + # Check Auth | ||
| 265 | + if is_auth is False: | ||
| 266 | + return {'result': False, 'error': '잘못된 요청입니다.'} | ||
| 267 | + | ||
| 268 | + # Update | ||
| 269 | + if 'name' in received: | ||
| 270 | + file[0].name = received['name'] | ||
| 271 | + if 'parent_id' in received: | ||
| 272 | + file[0].parent_id = received['parent_id'] | ||
| 273 | + if 'is_public' in received: | ||
| 274 | + file[0].is_public = 1 if received['is_public'] is True else 0 | ||
| 275 | + if 'is_starred' in received: | ||
| 276 | + file[0].is_starred = 1 if received['is_starred'] is True else 0 | ||
| 277 | + if 'is_trashed' in received: | ||
| 278 | + if file[0].parent_id is None: | ||
| 279 | + return {'result': False, 'error': '잘못된 요청입니다.'} | ||
| 280 | + file[0].is_trashed = 1 if received['is_trashed'] is True else 0 | ||
| 281 | + file[0].save() | ||
| 282 | + | ||
| 110 | return {'result': True} | 283 | return {'result': True} |
| 111 | 284 | ||
| 112 | 285 | ||
| 113 | -# TODO: 파일 복제 | 286 | +# 파일 복제 |
| 114 | def copy(request, file_id): | 287 | def copy(request, file_id): |
| 115 | - return {'result': True} | 288 | + # TODO: Auth |
| 289 | + request.user_id = 1 | ||
| 290 | + | ||
| 291 | + # Get File | ||
| 292 | + file = File.objects.filter(id=file_id, type='file', is_trashed=0, deleted_at__isnull=True) | ||
| 293 | + | ||
| 294 | + # Check Exists | ||
| 295 | + if len(file) == 0: | ||
| 296 | + return {'result': False, 'error': '잘못된 요청입니다.'} | ||
| 297 | + | ||
| 298 | + # Check Owner | ||
| 299 | + is_auth = False | ||
| 300 | + if file[0].owner_user_id == request.user_id: | ||
| 301 | + is_auth = True | ||
| 302 | + is_my_group = GroupUser.objects.filter(group_id=file[0].owner_group_id, user_id=request.user_id) | ||
| 303 | + if len(is_my_group) != 0: | ||
| 304 | + is_auth = True | ||
| 305 | + if is_auth is False: | ||
| 306 | + return {'result': False, 'error': '경로가 잘못되었습니다.'} | ||
| 307 | + | ||
| 308 | + # Create UUID | ||
| 309 | + new_file_id = uuid.uuid4() | ||
| 310 | + | ||
| 311 | + # S3 Copy | ||
| 312 | + s3_copy(file_id, new_file_id) | ||
| 313 | + | ||
| 314 | + # Create | ||
| 315 | + File.objects.create( | ||
| 316 | + id=new_file_id, | ||
| 317 | + parent_id=file[0].parent_id, | ||
| 318 | + owner_user_id=file[0].owner_user_id, | ||
| 319 | + owner_group_id=file[0].owner_group_id, | ||
| 320 | + uploader_id=request.user_id, | ||
| 321 | + type=file[0].type, | ||
| 322 | + name='%s의 사본' % file[0].name, | ||
| 323 | + size=file[0].size, | ||
| 324 | + created_at=timezone.now() | ||
| 325 | + ) | ||
| 326 | + | ||
| 327 | + return {'result': True, 'file_id': file_id} | ... | ... |
-
Please register or login to post a comment