forked from 00dani/lemoncurry
Create a basic but functional micropub media endpoint :o
This commit is contained in:
parent
0d5387823d
commit
4d974a5364
8 changed files with 103 additions and 38 deletions
1
Pipfile
1
Pipfile
|
@ -46,5 +46,6 @@ django-super-favicon = "*"
|
||||||
django-redis = "*"
|
django-redis = "*"
|
||||||
gevent = "*"
|
gevent = "*"
|
||||||
django-extensions = "*"
|
django-extensions = "*"
|
||||||
|
python-magic = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
72
Pipfile.lock
generated
72
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "d62130799f04a1649c0e6bd978dca2816500d94e27af5d21d1ec328dc795a2f8"
|
"sha256": "0347a58bdf44bc71022adb2ced6ae592ff1f7fd44520caee906f3e8e0e3730e4"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -571,44 +571,29 @@
|
||||||
],
|
],
|
||||||
"version": "==1.5.3"
|
"version": "==1.5.3"
|
||||||
},
|
},
|
||||||
|
"pyasn1": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0d7f6e959fe53f3960a23d73f35e1fce61348b30915b6664309ca756de7c1f89",
|
||||||
|
"sha256:5a0db897b311d265cde49615cf783f1c78613138605cdd0f907ecfa5b2aba3ee",
|
||||||
|
"sha256:758cb50abddc03e4563fd9e7f03db56e3e87b58c0bd01247360326e5c0c7ffa5",
|
||||||
|
"sha256:7d626683e3d792cccc608da02498aff37ab4f3dafd8905d6bf755d11f9b26b43",
|
||||||
|
"sha256:a7efe807c4b83a859e2735c692b92ed7b567cfddc4163763412920041d876c2b",
|
||||||
|
"sha256:b5a9ca48055b9a20f6d1b3d68e38692e5431c86a0f99ea602e61294e891fee5b",
|
||||||
|
"sha256:c07d6e587b2f928366b1f67c09bda026a3e6fcc99e80a744dc67f8fca3895626",
|
||||||
|
"sha256:d258b0a71994f7770599835249cece1caef3c70def868c4915e6e5ca49b67d15",
|
||||||
|
"sha256:d5cd6ed995dba16fad0c521cfe31cd2d68400b53fcc2bce93326829be73ab6d1",
|
||||||
|
"sha256:d84c2aea3cf43780e9e6a19f4e4dddee9f6976519020e64e47c57e5c7a8c3dd2",
|
||||||
|
"sha256:e85895087905c65b5b594eb91f7522664c85545b147d5f4d4e7b1b07da8dcbdc",
|
||||||
|
"sha256:f81c96761fca60d64b1c9b79ec2e40cf9495a745cf570613079ef324aeb9672b"
|
||||||
|
],
|
||||||
|
"version": "==0.4.2"
|
||||||
|
},
|
||||||
"pycparser": {
|
"pycparser": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:99a8ca03e29851d96616ad0404b4aad7d9ee16f25c9f9708a11faf2810f7b226"
|
"sha256:99a8ca03e29851d96616ad0404b4aad7d9ee16f25c9f9708a11faf2810f7b226"
|
||||||
],
|
],
|
||||||
"version": "==2.18"
|
"version": "==2.18"
|
||||||
},
|
},
|
||||||
"pycryptodome": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:043c82cd3dd3120286a1b325ace93000cf52abb13a067c3ecb6220f874fe4c30",
|
|
||||||
"sha256:0cdd73492859d853f60b8185715312dbca465879661e28d354d1cf5ea11860e7",
|
|
||||||
"sha256:15013007e393d0cc0e69f4329a47c4c8597b7f3d02c12c03f805405542f70c71",
|
|
||||||
"sha256:19d81b92bff837cdade735b9023808556bb4868e1ce194dad4d5ec4e2b2851f3",
|
|
||||||
"sha256:1ceb3e87605c4f0080115a8a00abf45f5df27b0166a37fd669fbff4523273cfc",
|
|
||||||
"sha256:2354a77051ed4a2959ce2aac508071eb3e42fc348ea39228b2eac335990bf508",
|
|
||||||
"sha256:27bd2878200690b050dca34f505b5c623532324b3de40267c1484784063134df",
|
|
||||||
"sha256:322f239e51fda80233762400a8975ab728639b571fa58545b95b9c44042af010",
|
|
||||||
"sha256:49a71eb990af30ff6276cfe201eb83ed3640ae989c1b5973f7b55a46c94232d1",
|
|
||||||
"sha256:4b5a2680008da3ac0cef2d3661597e0cbf8a3eb19eed35b859fd67e2de63eb85",
|
|
||||||
"sha256:6d34fe5134eb5d62368e21e6f203ac1770bc7273e9536c4a280121312c2de53a",
|
|
||||||
"sha256:733d5eb7e5ceed8b9d0b3c24c81f52c04cb5de6786461388204fceefe4456aa5",
|
|
||||||
"sha256:7c73d3798fe2946953768b788ce554c0d4b390780f5e73d63bd833241af27bfe",
|
|
||||||
"sha256:97af76f5200f15e97cac58d77f319dec40b4bada98de697c91a9517e63b41d1a",
|
|
||||||
"sha256:97cc46ff02b99dafdc2e0385b325cec0f8a15bf8b285d6ed1d7e4a3bc2067ce1",
|
|
||||||
"sha256:a561b59e0c3548eb649af381b7c38c6fd8392bbd4d0a8214794b2b761f405af4",
|
|
||||||
"sha256:ab2c633bfc23cf41be9281228517cb6f87879f4f1aeb154ed72bd53ab7cc83e9",
|
|
||||||
"sha256:adb54316998337f315520bbd8ef4d8bbd940b4ddfaef8ba1db3c137c5e499399",
|
|
||||||
"sha256:b4a3b710287eb1fc3e2cc1af018063f003530dff00c9ea4c55ae19bc1f3923cc",
|
|
||||||
"sha256:bcfdb66d6604882c3f96eea922552c2487cc0aec4b883cd217b9d341d2f8fad0",
|
|
||||||
"sha256:c08c053eb8716bbbd5e13e38f453b9e46a063e68df8659f3c421dcb7519fd381",
|
|
||||||
"sha256:e51da4ef9d9e2695a04044152f380c2db17adc9fc6fad8e24d863ead9cd548ed",
|
|
||||||
"sha256:e850e07f54dc3de9a1efdd59d227fcd1cb30cdd307dafdc647c79e8f30cf5032",
|
|
||||||
"sha256:ebc579c41fe26748dc1bad4f9105f08740ee28826293a28103b3875968695a5e",
|
|
||||||
"sha256:ed94cb1b4bf24be734f2bf2db3e8ea75f3914d2f8e684291bee54bbe4a5a9151",
|
|
||||||
"sha256:f5e19802295e63bdf83bb92849285c01f7167840efb1c1e08507a50b10ba7efa",
|
|
||||||
"sha256:fc569682f012b1f62f8d28d8f9bc71f1de67648cd1bc124ef8ccf8db4edfc28a"
|
|
||||||
],
|
|
||||||
"version": "==3.6.1"
|
|
||||||
},
|
|
||||||
"pytest": {
|
"pytest": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:54713b26c97538db6ff0703a12b19aeaeb60b5e599de542e7fca0ec83b9038e8",
|
"sha256:54713b26c97538db6ff0703a12b19aeaeb60b5e599de542e7fca0ec83b9038e8",
|
||||||
|
@ -633,11 +618,19 @@
|
||||||
},
|
},
|
||||||
"python-jose": {
|
"python-jose": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:391f860dbe274223d73dd87de25e4117bf09e8fe5f93a417663b1f2d7b591165",
|
"sha256:e06dd2e5e9125da79b519ff2652b8c666d64a5ea228fcd9862e0b29a534ccc53",
|
||||||
"sha256:3b35cdb0e55a88581ff6d3f12de753aa459e940b50fe7ca5aa25149bc94cb37b"
|
"sha256:e8255fb3cc524c04f4c790547a6215468f2a32d3a866424175523359e69f3aeb"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.0.2"
|
"version": "==3.0.0"
|
||||||
|
},
|
||||||
|
"python-magic": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:f2674dcfad52ae6c49d4803fa027809540b130db1dec928cfbb9240316831375",
|
||||||
|
"sha256:f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.4.15"
|
||||||
},
|
},
|
||||||
"python-memcached": {
|
"python-memcached": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -730,6 +723,13 @@
|
||||||
],
|
],
|
||||||
"version": "==0.10.0"
|
"version": "==0.10.0"
|
||||||
},
|
},
|
||||||
|
"rsa": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:25df4e10c263fb88b5ace923dd84bf9aa7f5019687b5e55382ffcdb8bede9db5",
|
||||||
|
"sha256:43f682fea81c452c98d09fc316aae12de6d30c4b5c84226642cf8f8fd1c93abd"
|
||||||
|
],
|
||||||
|
"version": "==3.4.2"
|
||||||
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||||
|
|
BIN
lemoncurry.paw
BIN
lemoncurry.paw
Binary file not shown.
|
@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/1.11/ref/settings/
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
|
APPEND_SLASH = False
|
||||||
|
|
||||||
ADMINS = [
|
ADMINS = [
|
||||||
('dani', 'dani@00dani.me'),
|
('dani', 'dani@00dani.me'),
|
||||||
|
|
|
@ -26,6 +26,10 @@ def origin(request):
|
||||||
return '{0}://{1}'.format(request.scheme, request.site.domain)
|
return '{0}://{1}'.format(request.scheme, request.site.domain)
|
||||||
|
|
||||||
|
|
||||||
|
def absolute_url(request, url):
|
||||||
|
return urljoin(origin(request), url)
|
||||||
|
|
||||||
|
|
||||||
def uri(request):
|
def uri(request):
|
||||||
return origin(request) + request.path
|
return origin(request) + request.path
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from . import views
|
from .views import micropub
|
||||||
|
from .views.media import media
|
||||||
|
|
||||||
app_name = 'micropub'
|
app_name = 'micropub'
|
||||||
urlpatterns = (
|
urlpatterns = (
|
||||||
path('', views.micropub, name='micropub'),
|
path('', micropub, name='micropub'),
|
||||||
|
path('/media', media, name='media'),
|
||||||
)
|
)
|
||||||
|
|
54
micropub/views/media.py
Normal file
54
micropub/views/media.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from django.core.files.storage import default_storage as store
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from django.views.decorators.http import require_POST
|
||||||
|
import magic
|
||||||
|
|
||||||
|
from lemonauth import tokens
|
||||||
|
from lemoncurry.utils import absolute_url
|
||||||
|
from . import error
|
||||||
|
|
||||||
|
ACCEPTED_MEDIA_TYPES = (
|
||||||
|
'image/jpeg',
|
||||||
|
'image/png',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@require_POST
|
||||||
|
def media(request):
|
||||||
|
token = tokens.auth(request)
|
||||||
|
if hasattr(token, 'content'):
|
||||||
|
return token
|
||||||
|
if 'file' not in request.FILES:
|
||||||
|
return error.bad_req(
|
||||||
|
"a file named 'file' must be provided to the media endpoint"
|
||||||
|
)
|
||||||
|
file = request.FILES['file']
|
||||||
|
if file.content_type not in ACCEPTED_MEDIA_TYPES:
|
||||||
|
return error.bad_req(
|
||||||
|
'unacceptable file type {0}'.format(file.content_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
mime = None
|
||||||
|
sha = hashlib.sha256()
|
||||||
|
for chunk in file.chunks():
|
||||||
|
if mime is None:
|
||||||
|
mime = magic.from_buffer(chunk, mime=True)
|
||||||
|
sha.update(chunk)
|
||||||
|
|
||||||
|
if mime != file.content_type:
|
||||||
|
return error.bad_req(
|
||||||
|
'detected file type {0} did not match specified file type {1}'
|
||||||
|
.format(mime, file.content_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
path = 'mp/{0[0]}/{2}.{1}'.format(*mime.split('/'), sha.hexdigest())
|
||||||
|
path = store.save(path, file)
|
||||||
|
url = absolute_url(request, store.url(path))
|
||||||
|
|
||||||
|
res = HttpResponse(status=201)
|
||||||
|
res['Location'] = url
|
||||||
|
return res
|
|
@ -1,10 +1,13 @@
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
|
from django.urls import reverse
|
||||||
from lemoncurry import requests
|
from lemoncurry import requests
|
||||||
|
from lemoncurry.utils import absolute_url
|
||||||
from . import error
|
from . import error
|
||||||
|
|
||||||
|
|
||||||
def config(request):
|
def config(request):
|
||||||
config = syndicate_to(request)
|
config = syndicate_to(request)
|
||||||
|
config['media-endpoint'] = absolute_url(request, reverse('micropub:media'))
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue