def debug_random_login(request): if not settings.DEBUG: raise AccessDenied(title="Эта фича доступна только при DEBUG=true") slug = "random_" + random_string() user, is_created = User.objects.get_or_create( slug=slug, defaults=dict( patreon_id=random_string(), membership_platform_type=User.MEMBERSHIP_PLATFORM_PATREON, email=slug + "@random.dev", full_name="%s %d y.o. Developer" % (random.choice(["Максим", "Олег"]), random.randint(18, 101)), company="Acme Corp.", position=random.choice(["Подниматель пингвинов", "Опускатель серверов", "Коллектор пивных бутылок"]), balance=10000, membership_started_at=datetime.utcnow(), membership_expires_at=datetime.utcnow() + timedelta(days=365 * 100), created_at=datetime.utcnow(), updated_at=datetime.utcnow(), is_email_verified=True, moderation_status=User.MODERATION_STATUS_APPROVED, ), ) if is_created: Post.upsert_user_intro(user, "Интро как интро, аппрув прошло :Р", is_visible=True) session = Session.create_for_user(user) return set_session_cookie(redirect("profile", user.slug), user, session)
def create_for_user(cls, user): return Session.objects.create( user=user, token=random_string(length=32), created_at=datetime.utcnow(), expires_at=max(user.membership_expires_at, datetime.utcnow() + timedelta(days=30)), )
def save(self, *args, **kwargs): if not self.secret_hash: self.secret_hash = random_string(length=10) if not self.slug: self.slug = generate_unique_slug(User, self.full_name, separator="") self.updated_at = datetime.utcnow() return super().save(*args, **kwargs)
def delete_user_data(user: User): if user.deleted_at is None: # user changed his mind return old_slug = str(user.slug) # anonymize user user.slug = random_string(length=32) user.email = f"{user.slug}@deleted.com" user.is_email_unsubscribed = True user.is_email_verified = False user.moderation_status = User.MODERATION_STATUS_REJECTED user.full_name = "💀 Юзер Удалился" user.avatar = None user.company = None user.position = None user.city = None user.country = None user.geo = None user.bio = None user.contact = None user.email_digest_type = User.EMAIL_DIGEST_TYPE_NOPE user.telegram_id = None user.telegram_data = None user.membership_platform_data = None user.save() # delete intro Post.objects.filter(author=user, type=Post.TYPE_INTRO).delete() # delete draft and unpublished posts Post.objects.filter(author=user, is_visible=False).delete() # transfer visible post ownership to "@deleted" user deleted_user = User.objects.filter(slug=settings.DELETED_USERNAME).first() if deleted_user: Post.objects.filter(author=user, is_visible=True).update(author=deleted_user) # replace nickname in replies new_slug = str(user.slug) Comment.objects\ .filter(reply_to__isnull=False, text__contains=f"@{old_slug}")\ .update( text=Replace("text", Value(f"@{old_slug}"), Value(f"@{new_slug}")), html=None ) # drop related data UserAchievement.objects.filter(user=user).delete() UserTag.objects.filter(user=user).delete() UserExpertise.objects.filter(user=user).delete() Session.objects.filter(user=user).delete() Code.objects.filter(user=user).delete() PostBookmark.objects.filter(user=user).delete()
def generate_unique_slug(model, name, separator="-"): attempts = 5 while attempts > 0: slug = slugify(name[:30], separator=separator) is_exists = model.objects.filter(slug__iexact=slug).exists() if not is_exists: return slug attempts -= 1 name += random_string(length=2) return str(uuid4())
def save(self): redis_key = 'suggestion:%s' % random_string() data = { 'account_id': self.account_id, 'image_url': self.image_url, 'name': self.name, 'replacement': self.replacement, 'count': self.count, } redis_client.set(redis_key, json.dumps(data)) self.redis_key = redis_key
def delete_user_data(user: User): if user.deleted_at is None: # user changed his mind return # anonymize user user.slug = random_string(length=32) user.email = f"{user.slug}@deleted.com" user.is_email_unsubscribed = True user.is_email_verified = False user.moderation_status = User.MODERATION_STATUS_REJECTED user.full_name = "💀 Юзер Удалился" user.avatar = None user.company = None user.position = None user.city = None user.country = None user.geo = None user.bio = None user.contact = None user.email_digest_type = User.EMAIL_DIGEST_TYPE_NOPE user.telegram_id = None user.telegram_data = None user.membership_platform_data = None user.save() # delete intro Post.objects.filter(author=user, type=Post.TYPE_INTRO).delete() # delete draft posts Post.objects.filter(author=user, is_visible=False).delete() # drop related data UserAchievement.objects.filter(user=user).delete() UserTag.objects.filter(user=user).delete() UserExpertise.objects.filter(user=user).delete() Session.objects.filter(user=user).delete() Code.objects.filter(user=user).delete() PostBookmark.objects.filter(user=user).delete() # cancel stripe subscriptions if user.stripe_id: stripe_subscriptions = stripe.Subscription.list( customer=user.stripe_id, limit=100) subscription_ids = [s["id"] for s in stripe_subscriptions["data"]] for subscription_id in subscription_ids: try: stripe.Subscription.delete(subscription_id) except Exception: pass
def debug_dev_login(request): if not settings.DEBUG: raise AccessDenied(title="Эта фича доступна только при DEBUG=true") user, is_created = User.objects.get_or_create( slug="dev", defaults=dict( membership_platform_type=User.MEMBERSHIP_PLATFORM_PATREON, membership_platform_id="DUMMY", email="*****@*****.**", full_name="Senior 23 y.o. Developer", company="FAANG", position="Team Lead конечно", balance=10000, membership_started_at=datetime.utcnow(), membership_expires_at=datetime.utcnow() + timedelta(days=365 * 100), created_at=datetime.utcnow(), updated_at=datetime.utcnow(), is_email_verified=True, is_profile_complete=True, is_profile_reviewed=True, is_profile_rejected=False, roles=["god"], ), ) if is_created: Post.upsert_user_intro(user, "Очень плохое интро", is_visible=True) session = Session.objects.create( user=user, token=random_string(length=32), created_at=datetime.utcnow(), expires_at=datetime.utcnow() + timedelta(days=365 * 100), ) response = redirect("profile", user.slug) response.set_cookie( key="token", value=session.token, max_age=settings.SESSION_COOKIE_AGE, httponly=True, secure=False, ) return response
def patreon_oauth_callback(request): code = request.GET.get("code") if not code: return render( request, "error.html", { "title": "Что-то сломалось между нами и патреоном", "message": "Так бывает. Попробуйте залогиниться еще раз" }) try: auth_data = patreon.fetch_auth_data(code) user_data = patreon.fetch_user_data(auth_data["access_token"]) except PatreonException as ex: if "invalid_grant" in str(ex): return render( request, "error.html", { "title": "Тут такое дело 😭", "message": "Авторизация патреона — говно. " "Она не сразу понимает, что вы стали патроном и отдаёт " "статус «отказано» в первые несколько минут, а иногда и часов. " "Я уже написал им в саппорт, но пока вам надо немного подождать и авторизоваться снова. " "Если долго не будет пускать — напишите мне в личку на патреоне." }) return render( request, "error.html", { "message": "Не получилось загрузить ваш профиль с серверов патреона. " "Попробуйте еще раз, наверняка оно починится. " f"Но если нет, то вот текст ошибки, с которым можно пожаловаться мне в личку:", "data": str(ex) }) membership = patreon.parse_active_membership(user_data) if not membership: return render( request, "error.html", { "title": "Надо быть патроном, чтобы состоять в Клубе", "message": "Кажется, вы не патроните <a href=\"https://www.patreon.com/join/vas3k\">@vas3k</a>. " "А это одно из основных требований для входа в Клуб.<br><br>" "Ещё иногда бывает, что ваш банк отказывает патреону в снятии денег. " "Проверьте, всё ли там у них в порядке." }) now = datetime.utcnow() user, is_created = User.objects.get_or_create( membership_platform_type=User.MEMBERSHIP_PLATFORM_PATREON, membership_platform_id=membership.user_id, defaults=dict( email=membership.email, full_name=membership.full_name[:120], avatar=upload_image_from_url(membership.image) if membership.image else None, membership_started_at=membership.started_at, membership_expires_at=membership.expires_at, created_at=now, updated_at=now, is_email_verified=False, is_profile_complete=False, # redirect new users to an intro page ), ) if is_created: user.balance = membership.lifetime_support_cents / 100 else: user.membership_expires_at = membership.expires_at user.balance = membership.lifetime_support_cents / 100 # TODO: remove when the real money comes in user.membership_platform_data = { "access_token": auth_data["access_token"], "refresh_token": auth_data["refresh_token"], } user.save() session = Session.objects.create( user=user, token=random_string(length=32), created_at=now, expires_at=user.membership_expires_at, ) redirect_to = reverse("profile", args=[user.slug]) state = request.GET.get("state") if state: redirect_to += f"?{state}" response = redirect(redirect_to) response.set_cookie( key="token", value=session.token, max_age=settings.SESSION_COOKIE_AGE, httponly=True, secure=not settings.DEBUG, ) return response
def patreon_oauth_callback(request): code = request.GET.get("code") if not code: return render( request, "error.html", { "message": "Что-то сломалось между нами и патреоном. Так бывает. Попробуйте залогиниться еще раз." }) try: auth_data = patreon.fetch_auth_data(code) user_data = patreon.fetch_user_data(auth_data["access_token"]) except PatreonException as ex: if "invalid_grant" in str(ex): return render( request, "error.html", { "message": "Тут такое дело. Авторизация патреона — говно. " "Она не сразу понимает, что вы стали моим патроном и отдаёт мне ошибку. " "Я уже написал им в саппорт, но пока вам надо немного подождать и авторизоваться снова. " "Обычно тогда срабатывает. Если нет — напишите мне в личные сообщения на патреоне." }) return render( request, "error.html", { "message": "Не получилось загрузить ваш профиль с серверов патреона. " "Попробуйте еще раз, наверняка оно починится. " f"Но если нет, то вот текст ошибки, с которым можно пожаловаться мне в личку:", "data": str(ex) }) membership = patreon.parse_active_membership(user_data) if not membership: return render( request, "error.html", { "message": "Надо быть патроном чтобы состоять в клубе.<br>" '<a href="https://www.patreon.com/join/vas3k">Станьте им здесь!</a>' }) now = datetime.utcnow() user, is_created = User.objects.get_or_create( membership_platform_type=User.MEMBERSHIP_PLATFORM_PATREON, membership_platform_id=membership.user_id, defaults=dict( email=membership.email, full_name=membership.full_name[:120], avatar=upload_image_from_url(membership.image) if membership.image else None, membership_started_at=membership.started_at, membership_expires_at=membership.expires_at, created_at=now, updated_at=now, is_email_verified=False, is_profile_complete=False, # redirect new users to an intro page ), ) if is_created: user.balance = membership.lifetime_support_cents / 100 else: user.membership_expires_at = membership.expires_at user.balance = membership.lifetime_support_cents / 100 # TODO: remove when the real money comes in user.membership_platform_data = { "access_token": auth_data["access_token"], "refresh_token": auth_data["refresh_token"], } user.save() session = Session.objects.create( user=user, token=random_string(length=32), created_at=now, expires_at=first_day_of_next_month(now), ) redirect_to = reverse("profile", args=[user.slug]) state = request.GET.get("state") if state: redirect_to += f"?{state}" response = redirect(redirect_to) response.set_cookie( key="token", value=session.token, max_age=settings.SESSION_COOKIE_AGE, httponly=True, secure=not settings.DEBUG, ) return response