Skip to content

Commit 7148af2

Browse files
authored
Merge pull request Ehco1996#226 from Ehco1996/dev
bug fix
2 parents 56733f8 + de8b6cb commit 7148af2

File tree

16 files changed

+223
-103
lines changed

16 files changed

+223
-103
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,14 @@ Wiki: [Wiki](https://github.com/Ehco1996/django-sspanel/wiki)
5454

5555
## 部署教程:
5656

57+
部署需要vps的支持
58+
我推荐 Vultr 家,最便宜的机房2.5美金一个月
59+
支持支付宝支付,对于新手来说,拿来练手最合适不过了
60+
这里有一个推广链接 :https://www.vultr.com/?ref=7914717-4F
61+
**通过这个链接注册,你能得到50美元的回赠!,而我能得到25美元的回赠**
62+
5763
硬核版: [部署教程](https://github.com/Ehco1996/django-sspanel/wiki/%E9%9D%A2%E6%9D%BF%E9%83%A8%E7%BD%B2)
5864

5965
萌新版: [部署教程](https://github.com/Ehco1996/django-sspanel/wiki/%E9%9D%A2%E6%9D%BF%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B-%E8%90%8C%E6%96%B0%E7%89%88)
6066

61-
Docker版: [部署教程](https://github.com/Ehco1996/django-sspanel/wiki/Docker-%E4%B8%80%E9%94%AE%E5%AE%89%E8%A3%85)
67+
Docker版: [部署教程](https://github.com/Ehco1996/django-sspanel/wiki/Docker-%E4%B8%80%E9%94%AE%E5%AE%89%E8%A3%85)

apps/api/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
path("shop/", views.purchase, name="purchase"),
1212
path("traffic/query/", views.traffic_query, name="traffic_query"),
1313
path("change/theme/", views.change_theme, name="change_theme"),
14+
path("change/sub_type/", views.change_sub_type, name="change_sub_type"),
1415
path("checkin/", views.checkin, name="checkin"),
1516
# 邀请码接口
1617
path("get/invitecode/", views.get_invitecode, name="get_invitecode"),

apps/api/views.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
from django.conf import settings
77
from django.http import JsonResponse
88
from django.shortcuts import HttpResponse
9+
from ratelimit.decorators import ratelimit
910
from django.views.decorators.csrf import csrf_exempt
1011
from django.views.decorators.http import require_http_methods
1112
from django.contrib.auth.decorators import login_required, permission_required
1213

1314
from apps.payments import pay
14-
from apps.constants import NODE_USER_INFO_TTL
15+
from apps.constants import NODE_USER_INFO_TTL, NODE_USER_CACHE_KEY
1516
from apps.utils import traffic_format, simple_cached_view, get_node_user, authorized
1617
from apps.ssserver.models import Suser, TrafficLog, Node, NodeOnlineLog, AliveIp
1718
from apps.sspanel.models import InviteCode, Goods, User, Donate, UserOrder
@@ -160,8 +161,18 @@ def change_theme(request):
160161
user = request.user
161162
user.theme = theme
162163
user.save()
163-
registerinfo = {"title": "修改成功!", "subtitle": "主题更换成功,刷新页面可见", "status": "success"}
164-
return JsonResponse(registerinfo)
164+
res = {"title": "修改成功!", "subtitle": "主题更换成功,刷新页面可见", "status": "success"}
165+
return JsonResponse(res)
166+
167+
168+
@login_required
169+
def change_sub_type(request):
170+
sub_type = request.POST.get("sub_type")
171+
user = request.user
172+
user.sub_type = sub_type
173+
user.save()
174+
res = {"title": "修改成功!", "subtitle": "订阅类型更换成功!", "status": "success"}
175+
return JsonResponse(res)
165176

166177

167178
@authorized
@@ -219,7 +230,7 @@ def node_online_api(request):
219230

220231

221232
@authorized
222-
@simple_cached_view(ttl=NODE_USER_INFO_TTL)
233+
@simple_cached_view(key=NODE_USER_CACHE_KEY, ttl=NODE_USER_INFO_TTL)
223234
@require_http_methods(["GET"])
224235
def user_api(request, node_id):
225236
"""
@@ -327,13 +338,14 @@ class OrderView(View):
327338
def get(self, request):
328339
user = request.user
329340
order = UserOrder.get_recent_created_order(user)
330-
order.check_order_status()
341+
order and order.check_order_status()
331342
if order and order.status == UserOrder.STATUS_FINISHED:
332343
info = {"title": "充值成功!", "subtitle": "请去商品界面购买商品!", "status": "success"}
333344
else:
334345
info = {"title": "支付查询失败!", "subtitle": "亲,确认支付了么?", "status": "error"}
335346
return JsonResponse({"info": info})
336347

348+
@ratelimit(key="user", rate="1/1s")
337349
def post(self, request):
338350
amount = int(request.POST.get("num"))
339351

apps/constants.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
# 判断节点在线时间间隔
7676
NODE_TIME_OUT = 75
7777

78-
# 默认缓存时间
79-
DEFUALT_CACHE_TTL = 60 * 60 * 2
78+
79+
DEFAULT_CACHE_TTL = 60 * 60 * 2
8080
NODE_USER_INFO_TTL = 60 * 5
81+
NODE_USER_CACHE_KEY = "cache.get.userF.info"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 2.1.7 on 2019-03-31 02:46
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [("sspanel", "0005_auto_20190224_0625")]
9+
10+
operations = [
11+
migrations.AddField(
12+
model_name="user",
13+
name="sub_type",
14+
field=models.SmallIntegerField(
15+
choices=[(0, "只订阅SS"), (1, "只订阅SSR"), (2, "订阅所有")],
16+
default=2,
17+
verbose_name="订阅类型",
18+
),
19+
)
20+
]

apps/sspanel/models.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import time
22
import base64
33
import datetime
4+
from urllib.parse import urlencode
45

56
import pendulum
67
import markdown
@@ -9,18 +10,28 @@
910
from django.conf import settings
1011
from django.utils import timezone
1112
from django.db import transaction
12-
from requests import PreparedRequest
13+
from django.core.cache import cache
1314
from django.contrib.auth.models import AbstractUser
1415
from django.core.validators import MaxValueValidator, MinValueValidator
1516

1617
from apps.payments import pay
17-
from apps.constants import THEME_CHOICES
18+
from apps.constants import THEME_CHOICES, NODE_USER_CACHE_KEY
1819
from apps.utils import get_long_random_string, traffic_format
1920

2021

2122
class User(AbstractUser):
2223
"""SS账户模型"""
2324

25+
SUB_TYPE_SS = 0
26+
SUB_TYPE_SSR = 1
27+
SUB_TYPE_ALL = 2
28+
29+
SUB_TYPES = (
30+
(SUB_TYPE_SS, "只订阅SS"),
31+
(SUB_TYPE_SSR, "只订阅SSR"),
32+
(SUB_TYPE_ALL, "订阅所有"),
33+
)
34+
2435
invitecode = models.CharField(verbose_name="邀请码", max_length=40)
2536
invited_by = models.PositiveIntegerField(verbose_name="邀请人id", default=1)
2637
balance = models.DecimalField(
@@ -47,6 +58,9 @@ class User(AbstractUser):
4758
default=settings.DEFAULT_THEME,
4859
max_length=10,
4960
)
61+
sub_type = models.SmallIntegerField(
62+
verbose_name="订阅类型", choices=SUB_TYPES, default=SUB_TYPE_ALL
63+
)
5064

5165
class Meta(AbstractUser.Meta):
5266
verbose_name = "用户"
@@ -102,12 +116,9 @@ def expire_time(self):
102116
@property
103117
def sub_link(self):
104118
"""生成该用户的订阅地址"""
105-
p = PreparedRequest()
106-
token = base64.b64encode(self.username.encode()).decode()
107-
url = settings.HOST + "/server/subscribe/"
119+
token = base64.urlsafe_b64encode(self.username.encode()).decode()
108120
params = {"token": token}
109-
p.prepare_url(url, params)
110-
return p.url
121+
return settings.HOST + f"/server/subscribe/?{urlencode(params)}"
111122

112123
@property
113124
def ss_user(self):
@@ -309,6 +320,7 @@ def purchase_by_user(self, user):
309320
user.level = self.level
310321
ss_user.save()
311322
user.save()
323+
cache.delete(NODE_USER_CACHE_KEY)
312324
# 增加购买记录
313325
PurchaseHistory.objects.create(
314326
good=self, user=user, money=self.money, purchtime=now
@@ -448,9 +460,11 @@ def gen_out_trade_no(cls):
448460

449461
@classmethod
450462
def get_not_paid_order(cls, user, amount):
451-
return cls.objects.filter(
452-
user=user, status=cls.STATUS_CREATED, amount=amount
453-
).first()
463+
return (
464+
cls.objects.filter(user=user, status=cls.STATUS_CREATED, amount=amount)
465+
.order_by("-created_at")
466+
.first()
467+
)
454468

455469
@classmethod
456470
def get_recent_created_order(cls, user):
@@ -466,11 +480,11 @@ def make_up_lost_orders(cls):
466480

467481
@classmethod
468482
def get_or_create_order(cls, user, amount):
483+
now = pendulum.now()
484+
order = cls.get_not_paid_order(user, amount)
485+
if order and order.expired_at > now:
486+
return order
469487
with transaction.atomic():
470-
now = pendulum.now()
471-
order = cls.get_not_paid_order(user, amount)
472-
if order and order.expired_at > now:
473-
return order
474488
out_trade_no = cls.gen_out_trade_no()
475489
trade = pay.alipay.api_alipay_trade_precreate(
476490
subject=settings.ALIPAY_TRADE_INFO.format(amount),

apps/sspanel/views.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,24 +87,18 @@ def user_login(request):
8787
if request.method == "POST":
8888
form = LoginForm(request.POST)
8989
if form.is_valid():
90-
# 获取表单用户名和密码
91-
username = form.cleaned_data["username"]
92-
password = form.cleaned_data["password"]
93-
# 进行用户验证
94-
user = authenticate(username=username, password=password)
95-
if user is not None and user.is_active:
90+
user = authenticate(
91+
username=form.cleaned_data["username"],
92+
password=form.cleaned_data["password"],
93+
)
94+
if user and user.is_active:
9695
login(request, user)
9796
messages.success(request, "自动跳转到用户中心", extra_tags="登录成功!")
9897
return HttpResponseRedirect(reverse("sspanel:userinfo"))
9998
else:
100-
form = LoginForm()
10199
messages.error(request, "请重新填写信息!", extra_tags="登录失败!")
102-
context = {"form": form}
103-
return render(request, "sspanel/login.html", context=context)
104-
else:
105-
context = {"form": LoginForm()}
106-
107-
return render(request, "sspanel/login.html", context=context)
100+
context = {"form": LoginForm()}
101+
return render(request, "sspanel/login.html", context=context)
108102

109103

110104
def user_logout(request):
@@ -125,13 +119,15 @@ def userinfo(request):
125119
remain_traffic = "{:.2f}".format(100 - user.ss_user.used_percentage)
126120
context = {
127121
"user": user,
122+
"user_sub_type": user.get_sub_type_display(),
128123
"anno": anno,
129124
"remain_traffic": remain_traffic,
130125
"min_traffic": min_traffic,
131126
"max_traffic": max_traffic,
132127
"sub_link": user.sub_link,
133128
"import_code": Node.get_import_code(user),
134129
"themes": THEME_CHOICES,
130+
"sub_types": User.SUB_TYPES,
135131
}
136132
return render(request, "sspanel/userinfo.html", context=context)
137133

apps/ssserver/models.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -218,24 +218,10 @@ class Node(ExportModelOperationsMixin("node"), models.Model):
218218

219219
SS_TYPE_CHOICES = ((0, "SS"), (1, "SSR"), (2, "SS/SSR"))
220220

221-
@classmethod
222-
def get_import_code(cls, user):
223-
"""获取该用户的所有节点的导入信息"""
224-
ss_user = user.ss_user
225-
sub_code_list = []
226-
node_list = cls.objects.filter(level__lte=user.level, show=1)
227-
for node in node_list:
228-
sub_code_list.append(node.get_node_link(ss_user))
229-
return "\n".join(sub_code_list)
230-
231-
@classmethod
232-
def get_node_ids(cls, all=False):
233-
"""返回所有节点的id"""
234-
if all is False:
235-
nodes = cls.objects.filter(show=1)
236-
else:
237-
nodes = cls.objects.all()
238-
return [node.node_id for node in nodes]
221+
class Meta:
222+
ordering = ["-show", "order"]
223+
verbose_name_plural = "节点"
224+
db_table = "ss_node"
239225

240226
node_id = models.IntegerField("节点id", unique=True)
241227
port = models.IntegerField("节点端口", default=443, blank=True, help_text="单端口多用户时需要")
@@ -393,15 +379,29 @@ def human_used_traffic(self):
393379
def get_by_node_id(cls, node_id):
394380
return cls.objects.get(node_id=node_id)
395381

382+
@classmethod
383+
def get_import_code(cls, user):
384+
"""获取该用户的所有节点的导入信息"""
385+
ss_user = user.ss_user
386+
sub_code_list = []
387+
node_list = cls.objects.filter(level__lte=user.level, show=1)
388+
for node in node_list:
389+
sub_code_list.append(node.get_node_link(ss_user))
390+
return "\n".join(sub_code_list)
391+
392+
@classmethod
393+
def get_node_ids(cls, all=False):
394+
"""返回所有节点的id"""
395+
if all is False:
396+
nodes = cls.objects.filter(show=1)
397+
else:
398+
nodes = cls.objects.all()
399+
return [node.node_id for node in nodes]
400+
396401
# verbose_name
397402
human_total_traffic.short_description = "总流量"
398403
human_used_traffic.short_description = "使用流量"
399404

400-
class Meta:
401-
ordering = ["-show", "order"]
402-
verbose_name_plural = "节点"
403-
db_table = "ss_node"
404-
405405

406406
class TrafficLog(ExportModelOperationsMixin("traffic_log"), models.Model):
407407
"""用户流量记录"""

apps/ssserver/views.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import json
22
import base64
3+
import binascii
4+
from urllib import parse
35

46
from django.urls import reverse
57
from django.conf import settings
68
from django.shortcuts import render
79
from django.contrib import messages
810
from django.shortcuts import get_object_or_404
911
from django.views.decorators.http import require_http_methods
10-
from django.http import StreamingHttpResponse, HttpResponseRedirect
12+
from django.http import StreamingHttpResponse, HttpResponseRedirect, HttpResponseNotFound
1113
from django.contrib.auth.decorators import login_required, permission_required
1214

1315
from .models import Suser, Node
@@ -116,13 +118,25 @@ def subscribe(request):
116118
"""
117119
返回ssr订阅链接
118120
"""
119-
token = request.GET.get("token", "")
120-
username = base64.b64decode(token).decode()
121+
url = request.build_absolute_uri()
122+
token = parse.parse_qs(parse.urlparse(url).query).get("token", [])
123+
if token:
124+
try:
125+
username = base64.b64decode(token[0]).decode()
126+
except binascii.Error:
127+
return HttpResponseNotFound()
128+
else:
129+
return HttpResponseNotFound()
121130
# 验证token
122131
user = get_object_or_404(User, username=username)
123132
ss_user = user.ss_user
124133
# 遍历该用户所有的节点
125-
node_list = Node.objects.filter(level__lte=user.level, show=1)
134+
if user.sub_type == User.SUB_TYPE_ALL:
135+
node_list = Node.objects.filter(level__lte=user.level, show=1)
136+
else:
137+
node_list = Node.objects.filter(
138+
level__lte=user.level, show=1, node_type=user.sub_type
139+
)
126140
# 生成订阅链接部分
127141
sub_code = "MAX={}\n".format(len(node_list))
128142
for node in node_list:

apps/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from django.core.cache import cache
1010
from django.http import JsonResponse
1111

12-
from apps.constants import DEFUALT_CACHE_TTL
12+
from apps.constants import DEFAULT_CACHE_TTL
1313
from apps.cachext import make_default_key
1414

1515

@@ -71,7 +71,7 @@ def decorator(func):
7171
@wraps(func)
7272
def cached_view(*agrs, **kwagrs):
7373
cache_key = key if key else make_default_key(func, *agrs, **kwagrs)
74-
cache_ttl = ttl if ttl else DEFUALT_CACHE_TTL
74+
cache_ttl = ttl if ttl else DEFAULT_CACHE_TTL
7575
resp = cache.get(cache_key)
7676
if resp:
7777
return resp

0 commit comments

Comments
 (0)