login via twitter.com
This commit is contained in:
parent
4924f5f248
commit
489fe17ebb
7 changed files with 256 additions and 192 deletions
|
@ -7,7 +7,6 @@ from django.core.serializers.json import DjangoJSONEncoder
|
|||
from django.db.models import Q, Count
|
||||
from markdownx.models import MarkdownxField
|
||||
from users.models import User
|
||||
from mastodon.api import get_relationships, get_cross_site_id
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
$(document).ready( function() {
|
||||
$("#userInfoCard .mast-brief").text($("<div>"+$("#userInfoCard .mast-brief").text().replace(/\<br/g,'\n<br').replace(/\<p/g,'\n<p')+"</div>").text());
|
||||
$("#userInfoCard .mast-brief").html($("#userInfoCard .mast-brief").html().replace(/\n/g,'<br/>'));
|
||||
|
||||
let token = $("#oauth2Token").text();
|
||||
let mast_uri = $("#mastodonURI").text();
|
||||
|
@ -7,111 +9,112 @@ $(document).ready( function() {
|
|||
mast_domain = mast_domain.hostname;
|
||||
let id = $("#userMastodonID").text();
|
||||
|
||||
let userInfoSpinner = $("#spinner").clone().removeAttr("hidden");
|
||||
let followersSpinner = $("#spinner").clone().removeAttr("hidden");
|
||||
let followingSpinner = $("#spinner").clone().removeAttr("hidden");
|
||||
$("#userInfoCard").append(userInfoSpinner);
|
||||
$("#followings h5").after(followingSpinner);
|
||||
$("#followers h5").after(followersSpinner);
|
||||
$(".mast-following-more").hide();
|
||||
$(".mast-followers-more").hide();
|
||||
|
||||
getUserInfo(
|
||||
id,
|
||||
mast_uri,
|
||||
token,
|
||||
function(userData) {
|
||||
let userName;
|
||||
if (userData.display_name) {
|
||||
userName = translateEmojis(userData.display_name, userData.emojis, true);
|
||||
} else {
|
||||
userName = userData.username;
|
||||
}
|
||||
//$("#userInfoCard .mast-acct").text(userData.acct);
|
||||
$("#userInfoCard .mast-acct").attr("href", userData.url);
|
||||
$("#userInfoCard .mast-avatar").attr("src", userData.avatar);
|
||||
$("#userInfoCard .mast-displayname").html(userName);
|
||||
$("#userInfoCard .mast-brief").text($("<div>"+userData.note.replace(/\<br/g,'\n<br').replace(/\<p/g,'\n<p')+"</div>").text());
|
||||
$("#userInfoCard .mast-brief").html($("#userInfoCard .mast-brief").html().replace(/\n/g,'<br/>'));
|
||||
$(userInfoSpinner).remove();
|
||||
}
|
||||
);
|
||||
|
||||
getFollowers(
|
||||
id,
|
||||
mast_uri,
|
||||
token,
|
||||
function(userList, request) {
|
||||
if (userList.length == 0) {
|
||||
$(".mast-followers").hide();
|
||||
$(".mast-followers").before('<div style="margin-bottom: 20px;">暂无</div>');
|
||||
|
||||
} else {
|
||||
if (userList.length > 4){
|
||||
userList = userList.slice(0, 4);
|
||||
$(".mast-followers-more").show();
|
||||
if (id && id != 'None' && mast_domain != 'twitter.com') {
|
||||
let userInfoSpinner = $("#spinner").clone().removeAttr("hidden");
|
||||
let followersSpinner = $("#spinner").clone().removeAttr("hidden");
|
||||
let followingSpinner = $("#spinner").clone().removeAttr("hidden");
|
||||
// $("#userInfoCard").append(userInfoSpinner);
|
||||
$("#followings h5").after(followingSpinner);
|
||||
$("#followers h5").after(followersSpinner);
|
||||
$(".mast-following-more").hide();
|
||||
$(".mast-followers-more").hide();
|
||||
getUserInfo(
|
||||
id,
|
||||
mast_uri,
|
||||
token,
|
||||
function(userData) {
|
||||
let userName;
|
||||
if (userData.display_name) {
|
||||
userName = translateEmojis(userData.display_name, userData.emojis, true);
|
||||
} else {
|
||||
userName = userData.username;
|
||||
}
|
||||
let template = $(".mast-followers li").clone();
|
||||
$(".mast-followers").html("");
|
||||
userList.forEach(data => {
|
||||
temp = $(template).clone();
|
||||
temp.find("img").attr("src", data.avatar);
|
||||
if (data.display_name) {
|
||||
temp.find(".mast-displayname").html(translateEmojis(data.display_name, data.emojis));
|
||||
} else {
|
||||
temp.find(".mast-displayname").text(data.username);
|
||||
}
|
||||
let url;
|
||||
if (data.acct.includes('@')) {
|
||||
url = $("#userPageURL").text().replace('0', data.acct);
|
||||
} else {
|
||||
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
|
||||
}
|
||||
temp.find("a").attr('href', url);
|
||||
$(".mast-followers").append(temp);
|
||||
});
|
||||
//$("#userInfoCard .mast-acct").text(userData.acct);
|
||||
$("#userInfoCard .mast-acct").attr("href", userData.url);
|
||||
$("#userInfoCard .mast-avatar").attr("src", userData.avatar);
|
||||
$("#userInfoCard .mast-displayname").html(userName);
|
||||
$("#userInfoCard .mast-brief").text($("<div>"+userData.note.replace(/\<br/g,'\n<br').replace(/\<p/g,'\n<p')+"</div>").text());
|
||||
$("#userInfoCard .mast-brief").html($("#userInfoCard .mast-brief").html().replace(/\n/g,'<br/>'));
|
||||
$(userInfoSpinner).remove();
|
||||
}
|
||||
$(followersSpinner).remove();
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
getFollowing(
|
||||
id,
|
||||
mast_uri,
|
||||
token,
|
||||
function(userList, request) {
|
||||
if (userList.length == 0) {
|
||||
$(".mast-following").hide();
|
||||
$(".mast-following").before('<div style="margin-bottom: 20px;">暂无</div>');
|
||||
} else {
|
||||
if (userList.length > 4){
|
||||
userList = userList.slice(0, 4);
|
||||
$(".mast-following-more").show();
|
||||
getFollowers(
|
||||
id,
|
||||
mast_uri,
|
||||
token,
|
||||
function(userList, request) {
|
||||
if (userList.length == 0) {
|
||||
$(".mast-followers").hide();
|
||||
$(".mast-followers").before('<div style="margin-bottom: 20px;">暂无</div>');
|
||||
|
||||
} else {
|
||||
if (userList.length > 4){
|
||||
userList = userList.slice(0, 4);
|
||||
$(".mast-followers-more").show();
|
||||
}
|
||||
let template = $(".mast-followers li").clone();
|
||||
$(".mast-followers").html("");
|
||||
userList.forEach(data => {
|
||||
temp = $(template).clone();
|
||||
temp.find("img").attr("src", data.avatar);
|
||||
if (data.display_name) {
|
||||
temp.find(".mast-displayname").html(translateEmojis(data.display_name, data.emojis));
|
||||
} else {
|
||||
temp.find(".mast-displayname").text(data.username);
|
||||
}
|
||||
let url;
|
||||
if (data.acct.includes('@')) {
|
||||
url = $("#userPageURL").text().replace('0', data.acct);
|
||||
} else {
|
||||
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
|
||||
}
|
||||
temp.find("a").attr('href', url);
|
||||
$(".mast-followers").append(temp);
|
||||
});
|
||||
}
|
||||
let template = $(".mast-following li").clone();
|
||||
$(".mast-following").html("");
|
||||
userList.forEach(data => {
|
||||
temp = $(template).clone()
|
||||
temp.find("img").attr("src", data.avatar);
|
||||
if (data.display_name) {
|
||||
temp.find(".mast-displayname").html(translateEmojis(data.display_name, data.emojis));
|
||||
} else {
|
||||
temp.find(".mast-displayname").text(data.username);
|
||||
}
|
||||
let url;
|
||||
if (data.acct.includes('@')) {
|
||||
url = $("#userPageURL").text().replace('0', data.acct);
|
||||
} else {
|
||||
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
|
||||
}
|
||||
temp.find("a").attr('href', url);
|
||||
$(".mast-following").append(temp);
|
||||
});
|
||||
$(followersSpinner).remove();
|
||||
}
|
||||
$(followingSpinner).remove();
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
getFollowing(
|
||||
id,
|
||||
mast_uri,
|
||||
token,
|
||||
function(userList, request) {
|
||||
if (userList.length == 0) {
|
||||
$(".mast-following").hide();
|
||||
$(".mast-following").before('<div style="margin-bottom: 20px;">暂无</div>');
|
||||
} else {
|
||||
if (userList.length > 4){
|
||||
userList = userList.slice(0, 4);
|
||||
$(".mast-following-more").show();
|
||||
}
|
||||
let template = $(".mast-following li").clone();
|
||||
$(".mast-following").html("");
|
||||
userList.forEach(data => {
|
||||
temp = $(template).clone()
|
||||
temp.find("img").attr("src", data.avatar);
|
||||
if (data.display_name) {
|
||||
temp.find(".mast-displayname").html(translateEmojis(data.display_name, data.emojis));
|
||||
} else {
|
||||
temp.find(".mast-displayname").text(data.username);
|
||||
}
|
||||
let url;
|
||||
if (data.acct.includes('@')) {
|
||||
url = $("#userPageURL").text().replace('0', data.acct);
|
||||
} else {
|
||||
url = $("#userPageURL").text().replace('0', data.acct + '@' + mast_domain);
|
||||
}
|
||||
temp.find("a").attr('href', url);
|
||||
$(".mast-following").append(temp);
|
||||
});
|
||||
}
|
||||
$(followingSpinner).remove();
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// mobile dropdown
|
||||
$(".relation-dropdown__button").data("collapse", true);
|
||||
|
|
|
@ -45,13 +45,15 @@ API_CREATE_APP = '/api/v1/apps'
|
|||
# GET
|
||||
API_SEARCH = '/api/v2/search'
|
||||
|
||||
TWITTER_DOMAIN = 'twitter.com'
|
||||
TWITTER_API = 'api.twitter.com'
|
||||
|
||||
get = functools.partial(requests.get, timeout=settings.MASTODON_TIMEOUT)
|
||||
post = functools.partial(requests.post, timeout=settings.MASTODON_TIMEOUT)
|
||||
|
||||
|
||||
# low level api below
|
||||
def get_relationships(site, id_list, token):
|
||||
def get_relationships(site, id_list, token): # no longer in use
|
||||
url = 'https://' + site + API_GET_RELATIONSHIPS
|
||||
payload = {'id[]': id_list}
|
||||
headers = {
|
||||
|
@ -63,30 +65,41 @@ def get_relationships(site, id_list, token):
|
|||
|
||||
|
||||
def post_toot(site, content, visibility, token, local_only=False):
|
||||
url = 'https://' + site + API_PUBLISH_TOOT
|
||||
headers = {
|
||||
'User-Agent': 'NeoDB/1.0',
|
||||
'Authorization': f'Bearer {token}',
|
||||
'Idempotency-Key': random_string_generator(16)
|
||||
}
|
||||
payload = {
|
||||
'status': content,
|
||||
'visibility': visibility,
|
||||
'local_only': True,
|
||||
}
|
||||
if not local_only:
|
||||
del payload['local_only']
|
||||
response = post(url, headers=headers, data=payload)
|
||||
if site == TWITTER_DOMAIN:
|
||||
url = 'https://api.twitter.com/2/tweets'
|
||||
payload = {
|
||||
'text': content if len(content) <= 150 else content[0:150] + '...'
|
||||
}
|
||||
response = post(url, headers=headers, json=payload)
|
||||
else:
|
||||
url = 'https://' + site + API_PUBLISH_TOOT
|
||||
payload = {
|
||||
'status': content,
|
||||
'visibility': visibility,
|
||||
}
|
||||
if local_only:
|
||||
payload['local_only'] = True
|
||||
response = post(url, headers=headers, data=payload)
|
||||
if response.status_code == 201:
|
||||
response.status_code = 200
|
||||
return response
|
||||
|
||||
|
||||
def get_instance_domain(domain_name):
|
||||
if domain_name.lower().strip() == TWITTER_DOMAIN:
|
||||
return TWITTER_DOMAIN
|
||||
try:
|
||||
response = get(f'https://{domain_name}/api/v1/instance', headers={'User-Agent': 'NeoDB/1.0'})
|
||||
return response.json()['uri'].lower().split('//')[-1].split('/')[0]
|
||||
except:
|
||||
return domain_name
|
||||
|
||||
|
||||
def create_app(domain_name):
|
||||
# naive protocal strip
|
||||
is_http = False
|
||||
|
@ -154,6 +167,8 @@ def get_cross_site_id(target_user, target_site, token):
|
|||
"""
|
||||
if target_site == target_user.mastodon_site:
|
||||
return target_user.mastodon_id
|
||||
if target_site == TWITTER_DOMAIN:
|
||||
return None
|
||||
|
||||
try:
|
||||
cross_site_info = CrossSiteUserInfo.objects.get(
|
||||
|
@ -182,14 +197,36 @@ def random_string_generator(n):
|
|||
|
||||
|
||||
def verify_account(site, token):
|
||||
if site == TWITTER_DOMAIN:
|
||||
url = 'https://' + TWITTER_API + '/2/users/me?user.fields=id,username,name,description,profile_image_url,created_at,protected'
|
||||
try:
|
||||
response = get(url, headers={'User-Agent': 'NeoDB/1.0', 'Authorization': f'Bearer {token}'})
|
||||
if response.status_code != 200:
|
||||
print(url)
|
||||
print(response.status_code)
|
||||
print(response.text)
|
||||
return response.status_code, None
|
||||
r = response.json()['data']
|
||||
r['display_name'] = r['name']
|
||||
r['note'] = r['description']
|
||||
r['avatar'] = r['profile_image_url']
|
||||
r['avatar_static'] = r['profile_image_url']
|
||||
r['locked'] = r['protected']
|
||||
r['url'] = f'https://{TWITTER_DOMAIN}/{r["username"]}'
|
||||
return 200, r
|
||||
except Exception:
|
||||
return -1, None
|
||||
url = 'https://' + site + API_VERIFY_ACCOUNT
|
||||
try:
|
||||
response = get(url, headers={'User-Agent': 'NeoDB/1.0', 'Authorization': f'Bearer {token}'})
|
||||
return response.status_code, response.json() if response.status_code == 200 else None
|
||||
return response.status_code, (response.json() if response.status_code == 200 else None)
|
||||
except Exception:
|
||||
return -1, None
|
||||
|
||||
|
||||
def get_related_acct_list(site, token, api):
|
||||
if site == TWITTER_DOMAIN:
|
||||
return []
|
||||
url = 'https://' + site + api
|
||||
results = []
|
||||
while url:
|
||||
|
|
124
mastodon/auth.py
124
mastodon/auth.py
|
@ -3,74 +3,115 @@ from django.shortcuts import reverse
|
|||
from .api import *
|
||||
from .models import MastodonApplication
|
||||
from django.conf import settings
|
||||
from urllib.parse import quote
|
||||
|
||||
|
||||
def get_mastodon_application(domain):
|
||||
app = MastodonApplication.objects.filter(domain_name=domain).first()
|
||||
if app is not None:
|
||||
return app, ''
|
||||
if domain == TWITTER_DOMAIN:
|
||||
return None, 'Twitter未配置'
|
||||
error_msg = None
|
||||
try:
|
||||
response = create_app(domain)
|
||||
except (requests.exceptions.Timeout, ConnectionError):
|
||||
error_msg = _("联邦网络请求超时。")
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
else:
|
||||
# fill the form with returned data
|
||||
if response.status_code != 200:
|
||||
error_msg = "实例连接错误,代码: " + str(response.status_code)
|
||||
print(f'Error connecting {domain}: {response.status_code} {response.content.decode("utf-8")}')
|
||||
else:
|
||||
try:
|
||||
data = response.json()
|
||||
except Exception as e:
|
||||
error_msg = "实例返回内容无法识别"
|
||||
print(f'Error connecting {domain}: {response.status_code} {response.content.decode("utf-8")} {e}')
|
||||
else:
|
||||
app = MastodonApplication.objects.create(domain_name=domain, app_id=data['id'], client_id=data['client_id'],
|
||||
client_secret=data['client_secret'], vapid_key=data['vapid_key'] if 'vapid_key' in data else '')
|
||||
return app, error_msg
|
||||
|
||||
|
||||
def get_mastodon_login_url(app, login_domain, request):
|
||||
url = request.scheme + "://" + request.get_host() + reverse('users:OAuth2_login')
|
||||
if login_domain == TWITTER_DOMAIN:
|
||||
return f"https://twitter.com/i/oauth2/authorize?response_type=code&client_id={app.client_id}&redirect_uri={quote(url)}&scope={quote(settings.TWITTER_CLIENT_SCOPE)}&state=state&code_challenge=challenge&code_challenge_method=plain"
|
||||
return "https://" + login_domain + "/oauth/authorize?client_id=" + app.client_id + "&scope=" + quote(settings.MASTODON_CLIENT_SCOPE) + "&redirect_uri=" + url + "&response_type=code"
|
||||
|
||||
|
||||
def obtain_token(site, request, code):
|
||||
""" Returns token if success else None. """
|
||||
mast_app = MastodonApplication.objects.get(domain_name=site)
|
||||
redirect_uri = request.scheme + "://" + request.get_host() + reverse('users:OAuth2_login')
|
||||
payload = {
|
||||
'client_id': mast_app.client_id,
|
||||
'client_secret': mast_app.client_secret,
|
||||
'redirect_uri': f"https://{request.get_host()}{reverse('users:OAuth2_login')}",
|
||||
'redirect_uri': redirect_uri,
|
||||
'grant_type': 'authorization_code',
|
||||
'code': code,
|
||||
'scope': 'read write'
|
||||
'code_verifier': 'challenge'
|
||||
}
|
||||
if settings.DEBUG:
|
||||
payload['redirect_uri'] = f"http://{request.get_host()}{reverse('users:OAuth2_login')}",
|
||||
headers = {'User-Agent': 'NeoDB/1.0'}
|
||||
auth = None
|
||||
if mast_app.is_proxy:
|
||||
url = 'https://' + mast_app.proxy_to + API_OBTAIN_TOKEN
|
||||
elif site == TWITTER_DOMAIN:
|
||||
url = 'https://api.twitter.com/2/oauth2/token'
|
||||
auth = (mast_app.client_id, mast_app.client_secret)
|
||||
del payload['client_secret']
|
||||
else:
|
||||
url = 'https://' + mast_app.domain_name + API_OBTAIN_TOKEN
|
||||
response = post(url, data=payload, headers={'User-Agent': 'NeoDB/1.0'})
|
||||
response = post(url, data=payload, headers=headers, auth=auth)
|
||||
# {"token_type":"bearer","expires_in":7200,"access_token":"VGpkOEZGR3FQRDJ5NkZ0dmYyYWIwS0dqeHpvTnk4eXp0NV9nWDJ2TEpmM1ZTOjE2NDg3ODMxNTU4Mzc6MToxOmF0OjE","scope":"block.read follows.read offline.access tweet.write users.read mute.read","refresh_token":"b1pXbGEzeUF1WE5yZHJOWmxTeWpvMTBrQmZPd0czLU0tQndZQTUyU3FwRDVIOjE2NDg3ODMxNTU4Mzg6MToxOnJ0OjE"}
|
||||
if response.status_code != 200:
|
||||
return
|
||||
print(url)
|
||||
print(response.status_code)
|
||||
print(response.text)
|
||||
return None, None
|
||||
data = response.json()
|
||||
return data.get('access_token'), data.get('refresh_token', '')
|
||||
|
||||
|
||||
def refresh_access_token(site, refresh_token):
|
||||
if site != TWITTER_DOMAIN:
|
||||
return None
|
||||
mast_app = MastodonApplication.objects.get(domain_name=site)
|
||||
url = 'https://api.twitter.com/2/oauth2/token'
|
||||
payload = {
|
||||
'client_id': mast_app.client_id,
|
||||
'refresh_token': refresh_token,
|
||||
'grant_type': 'refresh_token',
|
||||
}
|
||||
headers = {'User-Agent': 'NeoDB/1.0'}
|
||||
auth = (mast_app.client_id, mast_app.client_secret)
|
||||
response = post(url, data=payload, headers=headers, auth=auth)
|
||||
if response.status_code != 200:
|
||||
print(url)
|
||||
print(response.status_code)
|
||||
print(response.text)
|
||||
return None
|
||||
data = response.json()
|
||||
return data.get('access_token')
|
||||
|
||||
|
||||
def get_user_data(site, token):
|
||||
url = 'https://' + site + API_VERIFY_ACCOUNT
|
||||
headers = {
|
||||
'User-Agent': 'NeoDB/1.0',
|
||||
'Authorization': f'Bearer {token}'
|
||||
}
|
||||
response = get(url, headers=headers)
|
||||
if response.status_code != 200:
|
||||
return None
|
||||
return response.json()
|
||||
|
||||
|
||||
def revoke_token(site, token):
|
||||
mast_app = MastodonApplication.objects.get(domain_name=site)
|
||||
|
||||
payload = {
|
||||
'client_id': mast_app.client_id,
|
||||
'client_secret': mast_app.client_secret,
|
||||
'scope': token
|
||||
'token': token
|
||||
}
|
||||
|
||||
if mast_app.is_proxy:
|
||||
url = 'https://' + mast_app.proxy_to + API_REVOKE_TOKEN
|
||||
else:
|
||||
url = 'https://' + site + API_REVOKE_TOKEN
|
||||
response = post(url, data=payload, headers={'User-Agent': 'NeoDB/1.0'})
|
||||
|
||||
|
||||
def verify_token(site, token):
|
||||
""" Check if the token is valid and is of local instance. """
|
||||
url = 'https://' + site + API_VERIFY_ACCOUNT
|
||||
headers = {
|
||||
'User-Agent': 'NeoDB/1.0',
|
||||
'Authorization': f'Bearer {token}'
|
||||
}
|
||||
response = get(url, headers=headers)
|
||||
if response.status_code == 200:
|
||||
res_data = response.json()
|
||||
# check if is local instance user
|
||||
if res_data['acct'] == res_data['username']:
|
||||
return True
|
||||
return False
|
||||
post(url, data=payload, headers={'User-Agent': 'NeoDB/1.0'})
|
||||
|
||||
|
||||
class OAuth2Backend(ModelBackend):
|
||||
|
@ -78,22 +119,23 @@ class OAuth2Backend(ModelBackend):
|
|||
# "authenticate() should check the credentials it gets and returns
|
||||
# a user object that matches those credentials."
|
||||
# arg request is an interface specification, not used in this implementation
|
||||
def authenticate(self, request, token=None, username=None, site=None, **kwargs):
|
||||
|
||||
def authenticate(self, request, token=None, username=None, site=None, **kwargs):
|
||||
""" when username is provided, assume that token is newly obtained and valid """
|
||||
if token is None or site is None:
|
||||
return
|
||||
|
||||
if username is None:
|
||||
user_data = get_user_data(site, token)
|
||||
if user_data:
|
||||
username = user_data['username']
|
||||
code, user_data = verify_account(site, token)
|
||||
if code == 200:
|
||||
userid = user_data['id']
|
||||
else:
|
||||
# aquiring user data fail means token is invalid thus auth fail
|
||||
return None
|
||||
|
||||
# when username is provided, assume that token is newly obtained and valid
|
||||
try:
|
||||
user = UserModel._default_manager.get(username=username, mastodon_site=site)
|
||||
user = UserModel._default_manager.get(mastodon_id=userid, mastodon_site=site)
|
||||
except UserModel.DoesNotExist:
|
||||
return None
|
||||
else:
|
||||
|
|
|
@ -26,6 +26,7 @@ class User(AbstractUser):
|
|||
# mastodon domain name, eg donotban.com
|
||||
mastodon_site = models.CharField(max_length=100, blank=False)
|
||||
mastodon_token = models.CharField(max_length=100, default='')
|
||||
mastodon_refresh_token = models.CharField(max_length=100, default='')
|
||||
mastodon_locked = models.BooleanField(default=False)
|
||||
mastodon_followers = models.JSONField(default=list)
|
||||
mastodon_following = models.JSONField(default=list)
|
||||
|
|
|
@ -639,13 +639,13 @@
|
|||
<div class="user-profile" id="userInfoCard">
|
||||
<div class="user-profile__header">
|
||||
<!-- <img src="" class="user-profile__avatar mast-avatar" alt="{{ user.username }}"> -->
|
||||
<img src="" class="user-profile__avatar mast-avatar">
|
||||
<img src="{{ user.mastodon_account.avatar }}" class="user-profile__avatar mast-avatar">
|
||||
<a href="{% url 'users:home' user.mastodon_username %}">
|
||||
<h5 class="user-profile__username mast-displayname"></h5>
|
||||
<h5 class="user-profile__username mast-displayname">{{ user.mastodon_account.display_name }}</h5>
|
||||
</a>
|
||||
</div>
|
||||
<p><a class="user-profile__link mast-acct" target="_blank" href="https://{{ user.mastodon_site }}/@{{ user.username }}">@{{ user.username }}@{{ user.mastodon_site }}</a></p>
|
||||
<p class="user-profile__bio mast-brief"></p>
|
||||
<p><a class="user-profile__link mast-acct" target="_blank" href="{{ user.mastodon_account.url }}">@{{ user.username }}@{{ user.mastodon_site }}</a></p>
|
||||
<p class="user-profile__bio mast-brief">{{ user.mastodon_account.note }}</p>
|
||||
<!-- <a href="#" class="follow">{% trans '关注TA' %}</a> -->
|
||||
|
||||
{% if request.user != user %}
|
||||
|
|
|
@ -43,7 +43,7 @@ from collection.models import Collection
|
|||
|
||||
# Views
|
||||
########################################
|
||||
def swap_login(request, token, site):
|
||||
def swap_login(request, token, site, refresh_token):
|
||||
del request.session['swap_login']
|
||||
del request.session['swap_domain']
|
||||
code, data = verify_account(site, token)
|
||||
|
@ -58,9 +58,12 @@ def swap_login(request, token, 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.save(update_fields=['username', 'mastodon_site', 'mastodon_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:
|
||||
|
@ -78,12 +81,12 @@ def OAuth2_login(request):
|
|||
|
||||
# Network IO
|
||||
try:
|
||||
token = obtain_token(site, request, code)
|
||||
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)
|
||||
return swap_login(request, token, site, refresh_token)
|
||||
user = authenticate(request, token=token, site=site)
|
||||
if user:
|
||||
auth_login(request, user, token)
|
||||
|
@ -98,6 +101,7 @@ def OAuth2_login(request):
|
|||
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(
|
||||
|
@ -142,38 +146,11 @@ def connect(request):
|
|||
login_domain = request.session['swap_domain'] if request.session.get('swap_login') else request.GET.get('domain')
|
||||
login_domain = login_domain.strip().lower().split('//')[-1].split('/')[0].split('@')[-1]
|
||||
domain = get_instance_domain(login_domain)
|
||||
app = MastodonApplication.objects.filter(domain_name=domain).first()
|
||||
app, error_msg = get_mastodon_application(domain)
|
||||
if app is None:
|
||||
try:
|
||||
response = create_app(domain)
|
||||
except (requests.exceptions.Timeout, ConnectionError):
|
||||
error_msg = _("联邦网络请求超时。")
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
else:
|
||||
# fill the form with returned data
|
||||
if response.status_code != 200:
|
||||
error_msg = "实例连接错误,代码: " + str(response.status_code)
|
||||
print(f'Error connecting {domain}: {response.status_code} {response.content.decode("utf-8")}')
|
||||
else:
|
||||
try:
|
||||
data = response.json()
|
||||
except Exception as e:
|
||||
error_msg = "实例返回内容无法识别"
|
||||
print(f'Error connecting {domain}: {response.status_code} {response.content.decode("utf-8")}')
|
||||
else:
|
||||
app = MastodonApplication.objects.create(domain_name=domain, app_id=data['id'], client_id=data['client_id'],
|
||||
client_secret=data['client_secret'], vapid_key=data['vapid_key'] if 'vapid_key' in data else '')
|
||||
if app is None:
|
||||
return render(request,
|
||||
'common/error.html',
|
||||
{
|
||||
'msg': error_msg,
|
||||
'secondary_msg': "",
|
||||
}
|
||||
)
|
||||
return render(request, 'common/error.html', {'msg': error_msg, 'secondary_msg': "", })
|
||||
else:
|
||||
login_url = "https://" + login_domain + "/oauth/authorize?client_id=" + app.client_id + "&scope=" + quote(settings.MASTODON_CLIENT_SCOPE) + "&redirect_uri=" + request.scheme + "://" + request.get_host() + reverse('users:OAuth2_login') + "&response_type=code"
|
||||
login_url = get_mastodon_login_url(app, login_domain, request)
|
||||
resp = redirect(login_url)
|
||||
resp.set_cookie("mastodon_domain", domain)
|
||||
return resp
|
||||
|
@ -216,8 +193,9 @@ def register(request):
|
|||
return HttpResponseBadRequest()
|
||||
elif request.method == 'POST':
|
||||
token = request.session['new_user_token']
|
||||
user_data = get_user_data(request.COOKIES['mastodon_domain'], token)
|
||||
if user_data is None:
|
||||
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',
|
||||
|
@ -229,9 +207,13 @@ def register(request):
|
|||
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, token)
|
||||
response = redirect(reverse('common:home'))
|
||||
response.delete_cookie('mastodon_domain')
|
||||
|
|
Loading…
Add table
Reference in a new issue