simplify user register flow to avoid inconsistent state
This commit is contained in:
parent
09870e9b50
commit
f1010531b0
6 changed files with 406 additions and 350 deletions
254
users/account.py
Normal file
254
users/account.py
Normal file
|
@ -0,0 +1,254 @@
|
|||
from django.shortcuts import reverse, redirect, render, get_object_or_404
|
||||
from django.http import HttpResponseBadRequest, HttpResponse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth import authenticate
|
||||
from django.core.paginator import Paginator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models import Count
|
||||
from .models import User, Report, Preference
|
||||
from .forms import ReportForm
|
||||
from mastodon.api import *
|
||||
from mastodon import mastodon_request_included
|
||||
from common.config import *
|
||||
from common.models import MarkStatusEnum
|
||||
from common.utils import PageLinksGenerator
|
||||
from management.models import Announcement
|
||||
from books.models import *
|
||||
from movies.models import *
|
||||
from music.models import *
|
||||
from games.models import *
|
||||
from books.forms import BookMarkStatusTranslator
|
||||
from movies.forms import MovieMarkStatusTranslator
|
||||
from music.forms import MusicMarkStatusTranslator
|
||||
from games.forms import GameMarkStatusTranslator
|
||||
from mastodon.models import MastodonApplication
|
||||
from mastodon.api import verify_account
|
||||
from django.conf import settings
|
||||
from urllib.parse import quote
|
||||
import django_rq
|
||||
from .account import *
|
||||
from .tasks import *
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
import json
|
||||
from django.contrib import messages
|
||||
from books.models import BookMark, BookReview
|
||||
from movies.models import MovieMark, MovieReview
|
||||
from games.models import GameMark, GameReview
|
||||
from music.models import AlbumMark, SongMark, AlbumReview, SongReview
|
||||
from collection.models import Collection
|
||||
from common.importers.goodreads import GoodreadsImporter
|
||||
from common.importers.douban import DoubanImporter
|
||||
|
||||
|
||||
# the 'login' page that user can see
|
||||
def login(request):
|
||||
if request.method == 'GET':
|
||||
selected_site = request.GET.get('site', default='')
|
||||
|
||||
sites = MastodonApplication.objects.all().order_by("domain_name")
|
||||
|
||||
# store redirect url in the cookie
|
||||
if request.GET.get('next'):
|
||||
request.session['next_url'] = request.GET.get('next')
|
||||
|
||||
return render(
|
||||
request,
|
||||
'users/login.html',
|
||||
{
|
||||
'sites': sites,
|
||||
'scope': quote(settings.MASTODON_CLIENT_SCOPE),
|
||||
'selected_site': selected_site,
|
||||
'allow_any_site': settings.MASTODON_ALLOW_ANY_SITE,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
# connect will redirect to mastodon server
|
||||
def connect(request):
|
||||
if not settings.MASTODON_ALLOW_ANY_SITE:
|
||||
return redirect(reverse("users:login"))
|
||||
login_domain = request.session['swap_domain'] if request.session.get('swap_login') else request.GET.get('domain')
|
||||
if not login_domain:
|
||||
return render(request, 'common/error.html', {'msg': '未指定实例域名', 'secondary_msg': "", })
|
||||
login_domain = login_domain.strip().lower().split('//')[-1].split('/')[0].split('@')[-1]
|
||||
domain, version = get_instance_info(login_domain)
|
||||
app, error_msg = get_mastodon_application(domain)
|
||||
if app is None:
|
||||
return render(request, 'common/error.html', {'msg': error_msg, 'secondary_msg': "", })
|
||||
else:
|
||||
login_url = get_mastodon_login_url(app, login_domain, version, request)
|
||||
resp = redirect(login_url)
|
||||
resp.set_cookie('mastodon_domain', domain)
|
||||
return resp
|
||||
|
||||
|
||||
# mastodon server redirect back to here
|
||||
@mastodon_request_included
|
||||
def OAuth2_login(request):
|
||||
if request.method != 'GET':
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
code = request.GET.get('code')
|
||||
site = request.COOKIES.get('mastodon_domain')
|
||||
try:
|
||||
token, refresh_token = obtain_token(site, request, code)
|
||||
except ObjectDoesNotExist:
|
||||
return HttpResponseBadRequest("Mastodon site not registered")
|
||||
if not token:
|
||||
return render(
|
||||
request,
|
||||
'common/error.html',
|
||||
{
|
||||
'msg': _("认证失败😫")
|
||||
}
|
||||
)
|
||||
|
||||
if request.session.get('swap_login', False) and request.user.is_authenticated: # swap login for existing user
|
||||
return swap_login(request, token, site, refresh_token)
|
||||
|
||||
user = authenticate(request, token=token, site=site)
|
||||
if user: # existing user
|
||||
user.mastodon_token = token
|
||||
user.mastodon_refresh_token = refresh_token
|
||||
user.save(update_fields=['mastodon_token', 'mastodon_refresh_token'])
|
||||
auth_login(request, user)
|
||||
if request.session.get('next_url') is not None:
|
||||
response = redirect(request.session.get('next_url'))
|
||||
del request.session['next_url']
|
||||
else:
|
||||
response = redirect(reverse('common:home'))
|
||||
return response
|
||||
else: # newly registered user
|
||||
code, user_data = verify_account(site, token)
|
||||
if code != 200 or user_data is None:
|
||||
return render(
|
||||
request,
|
||||
'common/error.html',
|
||||
{
|
||||
'msg': _("联邦网络访问失败😫")
|
||||
}
|
||||
)
|
||||
new_user = User(
|
||||
username=user_data['username'],
|
||||
mastodon_id=user_data['id'],
|
||||
mastodon_site=site,
|
||||
mastodon_token=token,
|
||||
mastodon_refresh_token=refresh_token,
|
||||
mastodon_account=user_data,
|
||||
)
|
||||
new_user.save()
|
||||
request.session['new_user'] = True
|
||||
auth_login(request, new_user)
|
||||
return redirect(reverse('users:register'))
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def logout(request):
|
||||
if request.method == 'GET':
|
||||
# revoke_token(request.user.mastodon_site, request.user.mastodon_token)
|
||||
auth_logout(request)
|
||||
return redirect(reverse("users:login"))
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def reconnect(request):
|
||||
if request.method == 'POST':
|
||||
request.session['swap_login'] = True
|
||||
request.session['swap_domain'] = request.POST['domain']
|
||||
return connect(request)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
def register(request):
|
||||
if request.session.get('new_user'):
|
||||
del request.session['new_user']
|
||||
return render(request, 'users/register.html')
|
||||
else:
|
||||
return redirect(reverse('common:home'))
|
||||
|
||||
|
||||
def swap_login(request, token, site, refresh_token):
|
||||
del request.session['swap_login']
|
||||
del request.session['swap_domain']
|
||||
code, data = verify_account(site, token)
|
||||
current_user = request.user
|
||||
if code == 200 and data is not None:
|
||||
username = data['username']
|
||||
if username == current_user.username and site == current_user.mastodon_site:
|
||||
messages.add_message(request, messages.ERROR, _(f'该身份 {username}@{site} 与当前账号相同。'))
|
||||
else:
|
||||
try:
|
||||
existing_user = User.objects.get(username=username, mastodon_site=site)
|
||||
messages.add_message(request, messages.ERROR, _(f'该身份 {username}@{site} 已被用于其它账号。'))
|
||||
except ObjectDoesNotExist:
|
||||
current_user.username = username
|
||||
current_user.mastodon_id = data['id']
|
||||
current_user.mastodon_site = site
|
||||
current_user.mastodon_token = token
|
||||
current_user.mastodon_refresh_token = refresh_token
|
||||
current_user.mastodon_account = data
|
||||
current_user.save(update_fields=['username', 'mastodon_id', 'mastodon_site', 'mastodon_token', 'mastodon_refresh_token', 'mastodon_account'])
|
||||
django_rq.get_queue('mastodon').enqueue(refresh_mastodon_data_task, current_user, token)
|
||||
messages.add_message(request, messages.INFO, _(f'账号身份已更新为 {username}@{site}。'))
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('连接联邦网络获取身份信息失败。'))
|
||||
return redirect(reverse('users:data'))
|
||||
|
||||
|
||||
def auth_login(request, user):
|
||||
""" Decorates django ``login()``. Attach token to session."""
|
||||
auth.login(request, user)
|
||||
if user.mastodon_last_refresh < timezone.now() - timedelta(hours=1) or user.mastodon_account == {}:
|
||||
django_rq.get_queue('mastodon').enqueue(refresh_mastodon_data_task, user)
|
||||
|
||||
|
||||
def auth_logout(request):
|
||||
""" Decorates django ``logout()``. Release token in session."""
|
||||
auth.logout(request)
|
||||
|
||||
|
||||
@login_required
|
||||
def clear_data(request):
|
||||
if request.method == 'POST':
|
||||
if request.POST.get('verification') == request.user.mastodon_username:
|
||||
BookMark.objects.filter(owner=request.user).delete()
|
||||
MovieMark.objects.filter(owner=request.user).delete()
|
||||
GameMark.objects.filter(owner=request.user).delete()
|
||||
AlbumMark.objects.filter(owner=request.user).delete()
|
||||
SongMark.objects.filter(owner=request.user).delete()
|
||||
BookReview.objects.filter(owner=request.user).delete()
|
||||
MovieReview.objects.filter(owner=request.user).delete()
|
||||
GameReview.objects.filter(owner=request.user).delete()
|
||||
AlbumReview.objects.filter(owner=request.user).delete()
|
||||
SongReview.objects.filter(owner=request.user).delete()
|
||||
request.user.first_name = request.user.username
|
||||
request.user.last_name = request.user.mastodon_site
|
||||
request.user.is_active = False
|
||||
request.user.username = 'removed_' + str(request.user.id)
|
||||
request.user.mastodon_id = 0
|
||||
request.user.mastodon_site = 'removed'
|
||||
request.user.mastodon_token = ''
|
||||
request.user.mastodon_locked = False
|
||||
request.user.mastodon_followers = []
|
||||
request.user.mastodon_following = []
|
||||
request.user.mastodon_mutes = []
|
||||
request.user.mastodon_blocks = []
|
||||
request.user.mastodon_domain_blocks = []
|
||||
request.user.mastodon_account = {}
|
||||
request.user.save()
|
||||
auth_logout(request)
|
||||
return redirect(reverse("users:login"))
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('验证信息不符。'))
|
||||
return redirect(reverse("users:data"))
|
132
users/data.py
Normal file
132
users/data.py
Normal file
|
@ -0,0 +1,132 @@
|
|||
from django.shortcuts import reverse, redirect, render, get_object_or_404
|
||||
from django.http import HttpResponseBadRequest, HttpResponse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth import authenticate
|
||||
from django.core.paginator import Paginator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models import Count
|
||||
from .models import User, Report, Preference
|
||||
from .forms import ReportForm
|
||||
from mastodon.api import *
|
||||
from mastodon import mastodon_request_included
|
||||
from common.config import *
|
||||
from common.models import MarkStatusEnum
|
||||
from common.utils import PageLinksGenerator
|
||||
from management.models import Announcement
|
||||
from books.models import *
|
||||
from movies.models import *
|
||||
from music.models import *
|
||||
from games.models import *
|
||||
from books.forms import BookMarkStatusTranslator
|
||||
from movies.forms import MovieMarkStatusTranslator
|
||||
from music.forms import MusicMarkStatusTranslator
|
||||
from games.forms import GameMarkStatusTranslator
|
||||
from mastodon.models import MastodonApplication
|
||||
from mastodon.api import verify_account
|
||||
from django.conf import settings
|
||||
from urllib.parse import quote
|
||||
import django_rq
|
||||
from .account import *
|
||||
from .tasks import *
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
import json
|
||||
from django.contrib import messages
|
||||
from books.models import BookMark, BookReview
|
||||
from movies.models import MovieMark, MovieReview
|
||||
from games.models import GameMark, GameReview
|
||||
from music.models import AlbumMark, SongMark, AlbumReview, SongReview
|
||||
from collection.models import Collection
|
||||
from common.importers.goodreads import GoodreadsImporter
|
||||
from common.importers.douban import DoubanImporter
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def preferences(request):
|
||||
if request.method == 'POST':
|
||||
request.user.preference.mastodon_publish_public = bool(request.POST.get('mastodon_publish_public'))
|
||||
request.user.preference.mastodon_append_tag = request.POST.get('mastodon_append_tag', '').strip()
|
||||
request.user.preference.save()
|
||||
return render(request, 'users/preferences.html')
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def data(request):
|
||||
return render(request, 'users/data.html', {
|
||||
'latest_task': request.user.user_synctasks.order_by("-id").first(),
|
||||
'import_status': request.user.preference.import_status,
|
||||
'export_status': request.user.preference.export_status
|
||||
})
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def export_reviews(request):
|
||||
if request.method != 'POST':
|
||||
return redirect(reverse("users:data"))
|
||||
return render(request, 'users/data.html')
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def export_marks(request):
|
||||
if request.method == 'POST':
|
||||
if not request.user.preference.export_status.get('marks_pending'):
|
||||
django_rq.get_queue('export').enqueue(export_marks_task, request.user)
|
||||
request.user.preference.export_status['marks_pending'] = True
|
||||
request.user.preference.save()
|
||||
messages.add_message(request, messages.INFO, _('导出已开始。'))
|
||||
return redirect(reverse("users:data"))
|
||||
else:
|
||||
with open(request.user.preference.export_status['marks_file'], 'rb') as fh:
|
||||
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
|
||||
response['Content-Disposition'] = 'attachment;filename="marks.xlsx"'
|
||||
return response
|
||||
|
||||
|
||||
@login_required
|
||||
def sync_mastodon(request):
|
||||
if request.method == 'POST':
|
||||
django_rq.get_queue('mastodon').enqueue(refresh_mastodon_data_task, request.user)
|
||||
messages.add_message(request, messages.INFO, _('同步已开始。'))
|
||||
return redirect(reverse("users:data"))
|
||||
|
||||
|
||||
@login_required
|
||||
def reset_visibility(request):
|
||||
if request.method == 'POST':
|
||||
visibility = int(request.POST.get('visibility'))
|
||||
visibility = visibility if visibility >= 0 and visibility <= 2 else 0
|
||||
BookMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
MovieMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
GameMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
AlbumMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
SongMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
messages.add_message(request, messages.INFO, _('已重置。'))
|
||||
return redirect(reverse("users:data"))
|
||||
|
||||
|
||||
@login_required
|
||||
def import_goodreads(request):
|
||||
if request.method == 'POST':
|
||||
raw_url = request.POST.get('url')
|
||||
if GoodreadsImporter.import_from_url(raw_url, request.user):
|
||||
messages.add_message(request, messages.INFO, _('链接已保存,等待后台导入。'))
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('无法识别链接。'))
|
||||
return redirect(reverse("users:data"))
|
||||
|
||||
|
||||
@login_required
|
||||
def import_douban(request):
|
||||
if request.method == 'POST':
|
||||
importer = DoubanImporter(request.user, request.POST.get('visibility'))
|
||||
if importer.import_from_file(request.FILES['file']):
|
||||
messages.add_message(request, messages.INFO, _('文件上传成功,等待后台导入。'))
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('无法识别文件。'))
|
||||
return redirect(reverse("users:data"))
|
|
@ -32,6 +32,16 @@ from datetime import datetime
|
|||
import os
|
||||
|
||||
|
||||
def refresh_mastodon_data_task(user, token=None):
|
||||
if token:
|
||||
user.mastodon_token = token
|
||||
if user.refresh_mastodon_data():
|
||||
user.save()
|
||||
print(f"{user} mastodon data refreshed")
|
||||
else:
|
||||
print(f"{user} mastodon data refresh failed")
|
||||
|
||||
|
||||
def export_marks_task(user):
|
||||
user.preference.export_status['marks_pending'] = True
|
||||
user.preference.save()
|
|
@ -30,8 +30,7 @@
|
|||
此外,{{ site_name }}现处于测试阶段,疏漏在所难免,请妥善备份您的数据。
|
||||
使用过程中遇到的问题或者错误欢迎向<a href="{{ support_link }}">维护者</a>提出。感谢理解和支持!
|
||||
</p>
|
||||
<form action="{% url 'users:register' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<form action="{% url 'common:home' %}">
|
||||
<input type="submit" class="button" value="{% trans 'Cut the sh*t and get me in!' %}">
|
||||
</form>
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ urlpatterns = [
|
|||
path('data/clear_data', clear_data, name='clear_data'),
|
||||
path('preferences/', preferences, name='preferences'),
|
||||
path('logout/', logout, name='logout'),
|
||||
path('delete/', delete, name='delete'),
|
||||
path('layout/', set_layout, name='set_layout'),
|
||||
path('OAuth2_login/', OAuth2_login, name='OAuth2_login'),
|
||||
path('<int:id>/', home_redirect, name='home_redirect'),
|
||||
|
|
356
users/views.py
356
users/views.py
|
@ -28,7 +28,8 @@ from mastodon.api import verify_account
|
|||
from django.conf import settings
|
||||
from urllib.parse import quote
|
||||
import django_rq
|
||||
from .export import *
|
||||
from .account import *
|
||||
from .data import *
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
import json
|
||||
|
@ -42,196 +43,6 @@ from common.importers.goodreads import GoodreadsImporter
|
|||
from common.importers.douban import DoubanImporter
|
||||
|
||||
|
||||
# Views
|
||||
########################################
|
||||
def swap_login(request, token, site, refresh_token):
|
||||
del request.session['swap_login']
|
||||
del request.session['swap_domain']
|
||||
code, data = verify_account(site, token)
|
||||
current_user = request.user
|
||||
if code == 200 and data is not None:
|
||||
username = data['username']
|
||||
if username == current_user.username and site == current_user.mastodon_site:
|
||||
messages.add_message(request, messages.ERROR, _(f'该身份 {username}@{site} 与当前账号相同。'))
|
||||
else:
|
||||
try:
|
||||
existing_user = User.objects.get(username=username, mastodon_site=site)
|
||||
messages.add_message(request, messages.ERROR, _(f'该身份 {username}@{site} 已被用于其它账号。'))
|
||||
except ObjectDoesNotExist:
|
||||
current_user.username = username
|
||||
current_user.mastodon_id = data['id']
|
||||
current_user.mastodon_site = site
|
||||
current_user.mastodon_token = token
|
||||
current_user.mastodon_refresh_token = refresh_token
|
||||
current_user.mastodon_account = data
|
||||
current_user.save(update_fields=['username', 'mastodon_id', 'mastodon_site', 'mastodon_token', 'mastodon_refresh_token', 'mastodon_account'])
|
||||
django_rq.get_queue('mastodon').enqueue(refresh_mastodon_data_task, current_user, token)
|
||||
messages.add_message(request, messages.INFO, _(f'账号身份已更新为 {username}@{site}。'))
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('连接联邦网络获取身份信息失败。'))
|
||||
return redirect(reverse('users:data'))
|
||||
|
||||
|
||||
# no page rendered
|
||||
@mastodon_request_included
|
||||
def OAuth2_login(request):
|
||||
""" oauth authentication and logging user into django system """
|
||||
if request.method == 'GET':
|
||||
code = request.GET.get('code')
|
||||
site = request.COOKIES.get('mastodon_domain')
|
||||
|
||||
# Network IO
|
||||
try:
|
||||
token, refresh_token = obtain_token(site, request, code)
|
||||
except ObjectDoesNotExist:
|
||||
return HttpResponseBadRequest("Mastodon site not registered")
|
||||
if token:
|
||||
if request.session.get('swap_login', False) and request.user.is_authenticated: # swap login for existing user
|
||||
return swap_login(request, token, site, refresh_token)
|
||||
user = authenticate(request, token=token, site=site)
|
||||
if user:
|
||||
user.mastodon_token = token
|
||||
user.mastodon_refresh_token = refresh_token
|
||||
user.save(update_fields=['mastodon_token', 'mastodon_refresh_token'])
|
||||
auth_login(request, user)
|
||||
if request.session.get('next_url') is not None:
|
||||
response = redirect(request.session.get('next_url'))
|
||||
del request.session['next_url']
|
||||
else:
|
||||
response = redirect(reverse('common:home'))
|
||||
|
||||
response.delete_cookie('mastodon_domain')
|
||||
return response
|
||||
else:
|
||||
# will be passed to register page
|
||||
request.session['new_user_token'] = token
|
||||
request.session['new_user_refresh_token'] = refresh_token
|
||||
return redirect(reverse('users:register'))
|
||||
else:
|
||||
return render(
|
||||
request,
|
||||
'common/error.html',
|
||||
{
|
||||
'msg': _("认证失败😫")
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
# the 'login' page that user can see
|
||||
def login(request):
|
||||
if request.method == 'GET':
|
||||
selected_site = request.GET.get('site', default='')
|
||||
|
||||
sites = MastodonApplication.objects.all().order_by("domain_name")
|
||||
|
||||
# store redirect url in the cookie
|
||||
if request.GET.get('next'):
|
||||
request.session['next_url'] = request.GET.get('next')
|
||||
|
||||
return render(
|
||||
request,
|
||||
'users/login.html',
|
||||
{
|
||||
'sites': sites,
|
||||
'scope': quote(settings.MASTODON_CLIENT_SCOPE),
|
||||
'selected_site': selected_site,
|
||||
'allow_any_site': settings.MASTODON_ALLOW_ANY_SITE,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
def connect(request):
|
||||
if not settings.MASTODON_ALLOW_ANY_SITE:
|
||||
return redirect(reverse("users:login"))
|
||||
login_domain = request.session['swap_domain'] if request.session.get('swap_login') else request.GET.get('domain')
|
||||
if not login_domain:
|
||||
return render(request, 'common/error.html', {'msg': '未指定实例域名', 'secondary_msg': "", })
|
||||
login_domain = login_domain.strip().lower().split('//')[-1].split('/')[0].split('@')[-1]
|
||||
domain, version = get_instance_info(login_domain)
|
||||
app, error_msg = get_mastodon_application(domain)
|
||||
if app is None:
|
||||
return render(request, 'common/error.html', {'msg': error_msg, 'secondary_msg': "", })
|
||||
else:
|
||||
login_url = get_mastodon_login_url(app, login_domain, version, request)
|
||||
resp = redirect(login_url)
|
||||
resp.set_cookie("mastodon_domain", domain)
|
||||
return resp
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def reconnect(request):
|
||||
if request.method == 'POST':
|
||||
request.session['swap_login'] = True
|
||||
request.session['swap_domain'] = request.POST['domain']
|
||||
return connect(request)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def logout(request):
|
||||
if request.method == 'GET':
|
||||
# revoke_token(request.user.mastodon_site, request.user.mastodon_token)
|
||||
auth_logout(request)
|
||||
return redirect(reverse("users:login"))
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
def register(request):
|
||||
""" register confirm page """
|
||||
if request.method == 'GET':
|
||||
if request.user.is_authenticated:
|
||||
return redirect(reverse('common:home'))
|
||||
elif request.session.get('new_user_token'):
|
||||
return render(
|
||||
request,
|
||||
'users/register.html'
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
elif request.method == 'POST':
|
||||
token = request.session['new_user_token']
|
||||
refresh_token = request.session['new_user_refresh_token']
|
||||
code, user_data = verify_account(request.COOKIES['mastodon_domain'], token)
|
||||
if code != 200 or user_data is None:
|
||||
return render(
|
||||
request,
|
||||
'common/error.html',
|
||||
{
|
||||
'msg': _("联邦网络访问失败😫")
|
||||
}
|
||||
)
|
||||
new_user = User(
|
||||
username=user_data['username'],
|
||||
mastodon_id=user_data['id'],
|
||||
mastodon_site=request.COOKIES['mastodon_domain'],
|
||||
mastodon_token=token,
|
||||
mastodon_refresh_token=refresh_token,
|
||||
mastodon_account=user_data,
|
||||
)
|
||||
new_user.save()
|
||||
del request.session['new_user_token']
|
||||
del request.session['new_user_refresh_token']
|
||||
auth_login(request, new_user)
|
||||
response = redirect(reverse('common:home'))
|
||||
response.delete_cookie('mastodon_domain')
|
||||
return response
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
def delete(request):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def home_redirect(request, id):
|
||||
try:
|
||||
query_kwargs = {'pk': id}
|
||||
|
@ -247,10 +58,10 @@ def home_anonymous(request, id):
|
|||
username = id.split('@')[0]
|
||||
site = id.split('@')[1]
|
||||
return render(request, 'users/home_anonymous.html', {
|
||||
'login_url': login_url,
|
||||
'username': username,
|
||||
'site': site,
|
||||
})
|
||||
'login_url': login_url,
|
||||
'username': username,
|
||||
'site': site,
|
||||
})
|
||||
except Exception:
|
||||
return redirect(login_url)
|
||||
|
||||
|
@ -349,7 +160,7 @@ def home(request, id):
|
|||
|
||||
# movie marks
|
||||
filtered_movie_marks = filter_marks(movie_marks, MOVIES_PER_SET, 'movie')
|
||||
movie_marks_count= count_marks(movie_marks, "movie")
|
||||
movie_marks_count = count_marks(movie_marks, "movie")
|
||||
|
||||
# game marks
|
||||
filtered_game_marks = filter_marks(game_marks, GAMES_PER_SET, 'game')
|
||||
|
@ -800,7 +611,7 @@ def game_list(request, id, status):
|
|||
if status == 'reviewed':
|
||||
queryset = GameReview.objects.filter(owner=user).order_by("-edited_time")
|
||||
elif status == 'tagged':
|
||||
queryset = GameTag.objects.filter(content=tag, mark__owner=user).order_by("-mark__edited_time")
|
||||
queryset = GameTag.objects.filter(content=tag, mark__owner=user).order_by("-mark__edited_time")
|
||||
else:
|
||||
queryset = GameMark.objects.filter(
|
||||
owner=user, status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
|
||||
|
@ -816,7 +627,7 @@ def game_list(request, id, status):
|
|||
elif status == 'tagged':
|
||||
list_title = str(_(f"标记为「{tag}」的游戏"))
|
||||
else:
|
||||
list_title = str(GameMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("的游戏"))
|
||||
list_title = str(GameMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("的游戏"))
|
||||
return render(
|
||||
request,
|
||||
'users/item_list.html',
|
||||
|
@ -1020,152 +831,3 @@ def collection_list(request, id):
|
|||
}
|
||||
)
|
||||
return list(request, user.id)
|
||||
|
||||
|
||||
# Utils
|
||||
########################################
|
||||
def refresh_mastodon_data_task(user, token=None):
|
||||
if token:
|
||||
user.mastodon_token = token
|
||||
if user.refresh_mastodon_data():
|
||||
user.save()
|
||||
print(f"{user} mastodon data refreshed")
|
||||
else:
|
||||
print(f"{user} mastodon data refresh failed")
|
||||
|
||||
|
||||
def auth_login(request, user):
|
||||
""" Decorates django ``login()``. Attach token to session."""
|
||||
auth.login(request, user)
|
||||
if user.mastodon_last_refresh < timezone.now() - timedelta(hours=1) or user.mastodon_account == {}:
|
||||
django_rq.get_queue('mastodon').enqueue(refresh_mastodon_data_task, user)
|
||||
|
||||
|
||||
def auth_logout(request):
|
||||
""" Decorates django ``logout()``. Release token in session."""
|
||||
auth.logout(request)
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def preferences(request):
|
||||
if request.method == 'POST':
|
||||
request.user.preference.mastodon_publish_public = bool(request.POST.get('mastodon_publish_public'))
|
||||
request.user.preference.mastodon_append_tag = request.POST.get('mastodon_append_tag', '').strip()
|
||||
request.user.preference.save()
|
||||
return render(request, 'users/preferences.html')
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def data(request):
|
||||
return render(request, 'users/data.html', {
|
||||
'latest_task': request.user.user_synctasks.order_by("-id").first(),
|
||||
'import_status': request.user.preference.import_status,
|
||||
'export_status': request.user.preference.export_status
|
||||
})
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def export_reviews(request):
|
||||
if request.method != 'POST':
|
||||
return redirect(reverse("users:data"))
|
||||
return render(request, 'users/data.html')
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def export_marks(request):
|
||||
if request.method == 'POST':
|
||||
if not request.user.preference.export_status.get('marks_pending'):
|
||||
django_rq.get_queue('export').enqueue(export_marks_task, request.user)
|
||||
request.user.preference.export_status['marks_pending'] = True
|
||||
request.user.preference.save()
|
||||
messages.add_message(request, messages.INFO, _('导出已开始。'))
|
||||
return redirect(reverse("users:data"))
|
||||
else:
|
||||
with open(request.user.preference.export_status['marks_file'], 'rb') as fh:
|
||||
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
|
||||
response['Content-Disposition'] = 'attachment;filename="marks.xlsx"'
|
||||
return response
|
||||
|
||||
|
||||
@login_required
|
||||
def sync_mastodon(request):
|
||||
if request.method == 'POST':
|
||||
django_rq.get_queue('mastodon').enqueue(refresh_mastodon_data_task, request.user)
|
||||
messages.add_message(request, messages.INFO, _('同步已开始。'))
|
||||
return redirect(reverse("users:data"))
|
||||
|
||||
|
||||
@login_required
|
||||
def reset_visibility(request):
|
||||
if request.method == 'POST':
|
||||
visibility = int(request.POST.get('visibility'))
|
||||
visibility = visibility if visibility >= 0 and visibility <= 2 else 0
|
||||
BookMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
MovieMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
GameMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
AlbumMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
SongMark.objects.filter(owner=request.user).update(visibility=visibility)
|
||||
messages.add_message(request, messages.INFO, _('已重置。'))
|
||||
return redirect(reverse("users:data"))
|
||||
|
||||
|
||||
@login_required
|
||||
def clear_data(request):
|
||||
if request.method == 'POST':
|
||||
if request.POST.get('verification') == request.user.mastodon_username:
|
||||
BookMark.objects.filter(owner=request.user).delete()
|
||||
MovieMark.objects.filter(owner=request.user).delete()
|
||||
GameMark.objects.filter(owner=request.user).delete()
|
||||
AlbumMark.objects.filter(owner=request.user).delete()
|
||||
SongMark.objects.filter(owner=request.user).delete()
|
||||
BookReview.objects.filter(owner=request.user).delete()
|
||||
MovieReview.objects.filter(owner=request.user).delete()
|
||||
GameReview.objects.filter(owner=request.user).delete()
|
||||
AlbumReview.objects.filter(owner=request.user).delete()
|
||||
SongReview.objects.filter(owner=request.user).delete()
|
||||
request.user.first_name = request.user.username
|
||||
request.user.last_name = request.user.mastodon_site
|
||||
request.user.is_active = False
|
||||
request.user.username = 'removed_' + str(request.user.id)
|
||||
request.user.mastodon_id = 0
|
||||
request.user.mastodon_site = 'removed'
|
||||
request.user.mastodon_token = ''
|
||||
request.user.mastodon_locked = False
|
||||
request.user.mastodon_followers = []
|
||||
request.user.mastodon_following = []
|
||||
request.user.mastodon_mutes = []
|
||||
request.user.mastodon_blocks = []
|
||||
request.user.mastodon_domain_blocks = []
|
||||
request.user.mastodon_account = {}
|
||||
request.user.save()
|
||||
auth_logout(request)
|
||||
return redirect(reverse("users:login"))
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('验证信息不符。'))
|
||||
return redirect(reverse("users:data"))
|
||||
|
||||
|
||||
@login_required
|
||||
def import_goodreads(request):
|
||||
if request.method == 'POST':
|
||||
raw_url = request.POST.get('url')
|
||||
if GoodreadsImporter.import_from_url(raw_url, request.user):
|
||||
messages.add_message(request, messages.INFO, _('链接已保存,等待后台导入。'))
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('无法识别链接。'))
|
||||
return redirect(reverse("users:data"))
|
||||
|
||||
|
||||
@login_required
|
||||
def import_douban(request):
|
||||
if request.method == 'POST':
|
||||
importer = DoubanImporter(request.user, request.POST.get('visibility'))
|
||||
if importer.import_from_file(request.FILES['file']):
|
||||
messages.add_message(request, messages.INFO, _('文件上传成功,等待后台导入。'))
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _('无法识别文件。'))
|
||||
return redirect(reverse("users:data"))
|
||||
|
|
Loading…
Add table
Reference in a new issue