mirror of https://github.com/openspug/spug
init api project
commit
6d6d5e93c6
|
@ -0,0 +1,7 @@
|
||||||
|
*.pyc
|
||||||
|
/venv/
|
||||||
|
__pycache__/
|
||||||
|
/.idea/
|
||||||
|
/db.sqlite3
|
||||||
|
migrations/
|
||||||
|
/access.log
|
|
@ -0,0 +1,37 @@
|
||||||
|
from django.db import models
|
||||||
|
from libs import ModelMixin, human_time
|
||||||
|
from django.contrib.auth.hashers import make_password, check_password
|
||||||
|
|
||||||
|
|
||||||
|
class User(models.Model, ModelMixin):
|
||||||
|
username = models.CharField(max_length=100, unique=True)
|
||||||
|
nickname = models.CharField(max_length=100)
|
||||||
|
password_hash = models.CharField(max_length=100) # hashed password
|
||||||
|
is_supper = models.BooleanField(default=False)
|
||||||
|
is_active = models.BooleanField(default=True)
|
||||||
|
access_token = models.CharField(max_length=32)
|
||||||
|
token_expired = models.IntegerField(null=True)
|
||||||
|
last_login = models.CharField(max_length=20)
|
||||||
|
|
||||||
|
created_at = models.CharField(max_length=20, default=human_time)
|
||||||
|
created_by = models.ForeignKey('User', models.PROTECT, related_name='+', null=True)
|
||||||
|
deleted_at = models.CharField(max_length=20, null=True)
|
||||||
|
deleted_by = models.ForeignKey('User', models.PROTECT, related_name='+', null=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_password(plain_password: str) -> str:
|
||||||
|
return make_password(plain_password, hasher='pbkdf2_sha256')
|
||||||
|
|
||||||
|
def verify_password(self, plain_password: str) -> bool:
|
||||||
|
return check_password(plain_password, self.password_hash)
|
||||||
|
|
||||||
|
def has_perms(self, codes):
|
||||||
|
# return self.is_supper or self.role in codes
|
||||||
|
return self.is_supper
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<User %r>' % self.username
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'users'
|
||||||
|
ordering = ('-id',)
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
from apps.account.views import *
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^login/', login),
|
||||||
|
url(r'^logout/', logout),
|
||||||
|
url(r'^user/$', UserView.as_view()),
|
||||||
|
]
|
|
@ -0,0 +1,85 @@
|
||||||
|
from django.core.cache import cache
|
||||||
|
from django.views.generic import View
|
||||||
|
from libs import JsonParser, Argument, human_time, json_response
|
||||||
|
from .models import User
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class UserView(View):
|
||||||
|
def get(self, request):
|
||||||
|
users = User.objects.filter(is_supper=False, deleted_by_id__isnull=True)
|
||||||
|
return json_response([x.to_dict(excludes=('access_token', 'password_hash')) for x in users])
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
form, error = JsonParser(
|
||||||
|
Argument('username', help='请输入登录名'),
|
||||||
|
Argument('password', help='请输入密码'),
|
||||||
|
Argument('nickname', help='请输入姓名'),
|
||||||
|
).parse(request.body)
|
||||||
|
if error is None:
|
||||||
|
form.password_hash = User.make_password(form.pop('password'))
|
||||||
|
form.created_by = request.user
|
||||||
|
User.objects.create(**form)
|
||||||
|
return json_response(error=error)
|
||||||
|
|
||||||
|
def patch(self, request):
|
||||||
|
form, error = JsonParser(
|
||||||
|
Argument('id', type=int, help='请指定操作对象'),
|
||||||
|
Argument('username', required=False),
|
||||||
|
Argument('password', required=False),
|
||||||
|
Argument('nickname', required=False),
|
||||||
|
Argument('is_active', type=bool, required=False),
|
||||||
|
).parse(request.body, True)
|
||||||
|
if error is None:
|
||||||
|
if form.get('password'):
|
||||||
|
form.password_hash = User.make_password(form.pop('password'))
|
||||||
|
User.objects.filter(pk=form.pop('id')).update(**form)
|
||||||
|
return json_response(error=error)
|
||||||
|
|
||||||
|
def delete(self, request):
|
||||||
|
form, error = JsonParser(
|
||||||
|
Argument('id', type=int, help='请指定操作对象')
|
||||||
|
).parse(request.GET)
|
||||||
|
if error is None:
|
||||||
|
User.objects.filter(pk=form.id).update(
|
||||||
|
deleted_at=human_time(),
|
||||||
|
deleted_by=request.user
|
||||||
|
)
|
||||||
|
return json_response(error=error)
|
||||||
|
|
||||||
|
|
||||||
|
def login(request):
|
||||||
|
form, error = JsonParser(
|
||||||
|
Argument('username', help='请输入用户名'),
|
||||||
|
Argument('password', help='请输入密码')
|
||||||
|
).parse(request.body)
|
||||||
|
if error is None:
|
||||||
|
user = User.objects.filter(username=form.username).first()
|
||||||
|
if user:
|
||||||
|
if not user.is_active:
|
||||||
|
return json_response(error="账户已被禁用")
|
||||||
|
if user.verify_password(form.password):
|
||||||
|
cache.delete(form.username)
|
||||||
|
token_isvalid = user.access_token and len(user.access_token) == 32 and user.token_expired >= time.time()
|
||||||
|
user.access_token = user.access_token if token_isvalid else uuid.uuid4().hex
|
||||||
|
user.token_expired = time.time() + 8 * 60 * 60
|
||||||
|
user.last_login = human_time()
|
||||||
|
user.save()
|
||||||
|
return json_response({'access_token': user.access_token, 'nickname': user.nickname})
|
||||||
|
|
||||||
|
value = cache.get_or_set(form.username, 0, 86400)
|
||||||
|
if value >= 3:
|
||||||
|
if user and user.is_active:
|
||||||
|
user.is_active = False
|
||||||
|
user.save()
|
||||||
|
return json_response(error='账户已被禁用')
|
||||||
|
cache.set(form.username, value + 1, 86400)
|
||||||
|
return json_response(error="用户名或密码错误,连续多次错误账户将会被禁用")
|
||||||
|
return json_response(error=error)
|
||||||
|
|
||||||
|
|
||||||
|
def logout(request):
|
||||||
|
request.user.token_expired = 0
|
||||||
|
request.user.save()
|
||||||
|
return json_response()
|
|
@ -0,0 +1,5 @@
|
||||||
|
from .parser import JsonParser, Argument
|
||||||
|
from .decorators import *
|
||||||
|
from .validators import *
|
||||||
|
from .mixins import *
|
||||||
|
from .utils import *
|
|
@ -0,0 +1,37 @@
|
||||||
|
from functools import wraps
|
||||||
|
from .utils import json_response
|
||||||
|
|
||||||
|
|
||||||
|
def permission_required_supper(view_func):
|
||||||
|
@wraps(view_func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
request = None
|
||||||
|
for item in args:
|
||||||
|
if hasattr(item, 'user'):
|
||||||
|
request = item
|
||||||
|
break
|
||||||
|
if request is None or not request.user.is_supper:
|
||||||
|
return json_response(error='需要管理员权限')
|
||||||
|
return view_func(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def permission_required(perm_list):
|
||||||
|
def decorate(view_func):
|
||||||
|
codes = (perm_list,) if isinstance(perm_list, str) else perm_list
|
||||||
|
|
||||||
|
@wraps(view_func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
request = None
|
||||||
|
for item in args:
|
||||||
|
if hasattr(item, 'user'):
|
||||||
|
request = item
|
||||||
|
break
|
||||||
|
if request is None or (not request.user.is_supper and not request.user.has_perms(codes)):
|
||||||
|
return json_response(error='拒绝访问')
|
||||||
|
return view_func(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
return decorate
|
|
@ -0,0 +1,39 @@
|
||||||
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
|
from django.conf import settings
|
||||||
|
from .utils import json_response
|
||||||
|
from apps.account.models import User
|
||||||
|
import traceback
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class HandleExceptionMiddleware(MiddlewareMixin):
|
||||||
|
"""
|
||||||
|
处理试图函数异常
|
||||||
|
"""
|
||||||
|
|
||||||
|
def process_exception(self, request, exception):
|
||||||
|
traceback.print_exc()
|
||||||
|
return json_response(error='Exception: %s' % exception)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationMiddleware(MiddlewareMixin):
|
||||||
|
"""
|
||||||
|
登录验证
|
||||||
|
"""
|
||||||
|
|
||||||
|
def process_request(self, request):
|
||||||
|
if request.path in settings.AUTHENTICATION_EXCLUDES:
|
||||||
|
return None
|
||||||
|
if any(x.match(request.path) for x in settings.AUTHENTICATION_EXCLUDES if hasattr(x, 'match')):
|
||||||
|
return None
|
||||||
|
access_token = request.META.get('HTTP_X_TOKEN') or request.GET.get('x-token')
|
||||||
|
if access_token and len(access_token) == 32:
|
||||||
|
user = User.objects.filter(access_token=access_token).first()
|
||||||
|
if user and user.token_expired >= time.time() and user.is_active:
|
||||||
|
request.user = user
|
||||||
|
user.token_expired = time.time() + 8 * 60 * 60
|
||||||
|
user.save()
|
||||||
|
return None
|
||||||
|
response = json_response(error="验证失败,请重新登录")
|
||||||
|
response.status_code = 401
|
||||||
|
return response
|
|
@ -0,0 +1,53 @@
|
||||||
|
from .utils import json_response
|
||||||
|
|
||||||
|
|
||||||
|
# 混入类,提供Model实例to_dict方法
|
||||||
|
class ModelMixin(object):
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def to_dict(self, excludes: tuple = None, selects: tuple = None) -> dict:
|
||||||
|
if not hasattr(self, '_meta'):
|
||||||
|
raise TypeError('<%r> does not a django.db.models.Model object.' % self)
|
||||||
|
elif selects:
|
||||||
|
return {f: getattr(self, f) for f in selects}
|
||||||
|
elif excludes:
|
||||||
|
return {f.attname: getattr(self, f.attname) for f in self._meta.fields if f.attname not in excludes}
|
||||||
|
else:
|
||||||
|
return {f.attname: getattr(self, f.attname) for f in self._meta.fields}
|
||||||
|
|
||||||
|
|
||||||
|
# 使用该混入类,需要request.user对象实现has_perms方法
|
||||||
|
class PermissionMixin(object):
|
||||||
|
"""
|
||||||
|
CBV mixin which verifies that the current user has all specified
|
||||||
|
permissions.
|
||||||
|
"""
|
||||||
|
permission_required = None
|
||||||
|
|
||||||
|
def get_permission_required(self):
|
||||||
|
"""
|
||||||
|
Override this method to override the permission_required attribute.
|
||||||
|
Must return an iterable.
|
||||||
|
"""
|
||||||
|
if self.permission_required is None:
|
||||||
|
raise AttributeError(
|
||||||
|
'{0} is missing the permission_required attribute. Define {0}.permission_required, or override '
|
||||||
|
'{0}.get_permission_required().'.format(self.__class__.__name__)
|
||||||
|
)
|
||||||
|
if isinstance(self.permission_required, str):
|
||||||
|
perms = (self.permission_required,)
|
||||||
|
else:
|
||||||
|
perms = self.permission_required
|
||||||
|
return perms
|
||||||
|
|
||||||
|
def has_permission(self):
|
||||||
|
"""
|
||||||
|
Override this method to customize the way permissions are checked.
|
||||||
|
"""
|
||||||
|
perms = self.get_permission_required()
|
||||||
|
return self.request.user.has_perms(perms)
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
if not self.has_permission():
|
||||||
|
return json_response(error='拒绝访问')
|
||||||
|
return super(PermissionMixin, self).dispatch(request, *args, **kwargs)
|
|
@ -0,0 +1,123 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from .utils import AttrDict
|
||||||
|
|
||||||
|
|
||||||
|
# 自定义的解析异常
|
||||||
|
class ParseError(BaseException):
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
|
||||||
|
# 需要校验的参数对象
|
||||||
|
class Argument(object):
|
||||||
|
"""
|
||||||
|
:param name: name of option
|
||||||
|
:param default: default value if the argument if absent
|
||||||
|
:param bool required: is required
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, default=None, required=True, type=str, filter=None, help=None, nullable=False):
|
||||||
|
self.name = name
|
||||||
|
self.default = default
|
||||||
|
self.type = type
|
||||||
|
self.required = required
|
||||||
|
self.nullable = nullable
|
||||||
|
self.filter = filter
|
||||||
|
self.help = help
|
||||||
|
if not isinstance(self.name, str):
|
||||||
|
raise TypeError('Argument name must be string')
|
||||||
|
if filter and not callable(self.filter):
|
||||||
|
raise TypeError('Argument filter is not callable')
|
||||||
|
|
||||||
|
def parse(self, has_key, value):
|
||||||
|
if not has_key:
|
||||||
|
if self.required and self.default is None:
|
||||||
|
raise ParseError(
|
||||||
|
self.help or 'Required Error: %s is required' % self.name)
|
||||||
|
else:
|
||||||
|
return self.default
|
||||||
|
elif value in [u'', '', None]:
|
||||||
|
if self.default is not None:
|
||||||
|
return self.default
|
||||||
|
elif not self.nullable and self.required:
|
||||||
|
raise ParseError(
|
||||||
|
self.help or 'Value Error: %s must not be null' % self.name)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
if self.type:
|
||||||
|
if self.type in (list, dict) and isinstance(value, str):
|
||||||
|
value = json.loads(value)
|
||||||
|
assert isinstance(value, self.type)
|
||||||
|
elif self.type == bool and isinstance(value, str):
|
||||||
|
assert value.lower() in ['true', 'false']
|
||||||
|
value = value.lower() == 'true'
|
||||||
|
elif not isinstance(value, self.type):
|
||||||
|
value = self.type(value)
|
||||||
|
except (TypeError, ValueError, AssertionError):
|
||||||
|
raise ParseError(self.help or 'Type Error: %s type must be %s' % (
|
||||||
|
self.name, self.type))
|
||||||
|
|
||||||
|
if self.filter:
|
||||||
|
if not self.filter(value):
|
||||||
|
raise ParseError(
|
||||||
|
self.help or 'Value Error: %s filter check failed' % self.name)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# 解析器基类
|
||||||
|
class BaseParser(object):
|
||||||
|
def __init__(self, *args):
|
||||||
|
self.args = []
|
||||||
|
for e in args:
|
||||||
|
if isinstance(e, str):
|
||||||
|
e = Argument(e)
|
||||||
|
elif not isinstance(e, Argument):
|
||||||
|
raise TypeError('%r is not instance of Argument' % e)
|
||||||
|
self.args.append(e)
|
||||||
|
|
||||||
|
def _get(self, key):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def _init(self, data):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def add_argument(self, **kwargs):
|
||||||
|
self.args.append(Argument(**kwargs))
|
||||||
|
|
||||||
|
def parse(self, data=None, clear=False):
|
||||||
|
rst = AttrDict()
|
||||||
|
try:
|
||||||
|
self._init(data)
|
||||||
|
for e in self.args:
|
||||||
|
has_key, value = self._get(e.name)
|
||||||
|
if clear and has_key is False and e.required is False:
|
||||||
|
continue
|
||||||
|
rst[e.name] = e.parse(has_key, value)
|
||||||
|
except ParseError as err:
|
||||||
|
return None, err.message
|
||||||
|
return rst, None
|
||||||
|
|
||||||
|
|
||||||
|
# Json解析器
|
||||||
|
class JsonParser(BaseParser):
|
||||||
|
def __init__(self, *args):
|
||||||
|
self.__data = None
|
||||||
|
super(JsonParser, self).__init__(*args)
|
||||||
|
|
||||||
|
def _get(self, key):
|
||||||
|
return key in self.__data, self.__data.get(key)
|
||||||
|
|
||||||
|
def _init(self, data):
|
||||||
|
try:
|
||||||
|
if isinstance(data, (str, bytes)):
|
||||||
|
data = data.decode('utf-8')
|
||||||
|
self.__data = json.loads(data) if data else {}
|
||||||
|
else:
|
||||||
|
assert hasattr(data, '__contains__')
|
||||||
|
assert hasattr(data, 'get')
|
||||||
|
assert callable(data.get)
|
||||||
|
self.__data = data
|
||||||
|
except (ValueError, AssertionError):
|
||||||
|
raise ParseError('Invalid data type for parse')
|
|
@ -0,0 +1,93 @@
|
||||||
|
from django.http.response import HttpResponse
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
from datetime import datetime, date as datetime_date
|
||||||
|
from decimal import Decimal
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
# 转换时间格式到字符串
|
||||||
|
def human_time(date=None):
|
||||||
|
if date:
|
||||||
|
assert isinstance(date, datetime)
|
||||||
|
else:
|
||||||
|
date = datetime.now()
|
||||||
|
return date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
|
||||||
|
# 转换时间格式到字符串(天)
|
||||||
|
def human_date(date=None):
|
||||||
|
if date:
|
||||||
|
assert isinstance(date, datetime)
|
||||||
|
else:
|
||||||
|
date = datetime.now()
|
||||||
|
return date.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
|
||||||
|
# 解析时间类型的数据
|
||||||
|
def parse_time(value):
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
return value
|
||||||
|
if isinstance(value, str):
|
||||||
|
if len(value) == 10:
|
||||||
|
return datetime.strptime(value, '%Y-%m-%d')
|
||||||
|
elif len(value) == 19:
|
||||||
|
return datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
|
||||||
|
raise TypeError('Expect a datetime.datetime value')
|
||||||
|
|
||||||
|
|
||||||
|
# 传两个时间得到一个时间差
|
||||||
|
def human_diff_time(time1, time2):
|
||||||
|
time1 = parse_time(time1)
|
||||||
|
time2 = parse_time(time2)
|
||||||
|
delta = time1 - time2 if time1 > time2 else time2 - time1
|
||||||
|
if delta.seconds < 60:
|
||||||
|
text = '%d秒' % delta.seconds
|
||||||
|
elif delta.seconds < 3600:
|
||||||
|
text = '%d分' % (delta.seconds / 60)
|
||||||
|
else:
|
||||||
|
text = '%d小时' % (delta.seconds / 3600)
|
||||||
|
return '%d天%s' % (delta.days, text) if delta.days else text
|
||||||
|
|
||||||
|
|
||||||
|
def json_response(data='', error=''):
|
||||||
|
content = AttrDict(data=data, error=error)
|
||||||
|
if error:
|
||||||
|
content.data = ''
|
||||||
|
elif hasattr(data, 'to_dict'):
|
||||||
|
content.data = data.to_dict()
|
||||||
|
elif isinstance(data, (list, QuerySet)) and all([hasattr(item, 'to_dict') for item in data]):
|
||||||
|
content.data = [item.to_dict() for item in data]
|
||||||
|
return HttpResponse(json.dumps(content, cls=DateTimeEncoder), content_type='application/json')
|
||||||
|
|
||||||
|
|
||||||
|
# 继承自dict,实现可以通过.来操作元素
|
||||||
|
class AttrDict(dict):
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
self.__setitem__(key, value)
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
return self.__getitem__(item)
|
||||||
|
|
||||||
|
def __delattr__(self, item):
|
||||||
|
self.__delitem__(item)
|
||||||
|
|
||||||
|
|
||||||
|
# 日期json序列化
|
||||||
|
class DateTimeEncoder(json.JSONEncoder):
|
||||||
|
def default(self, o):
|
||||||
|
if isinstance(o, datetime):
|
||||||
|
return o.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
elif isinstance(o, datetime_date):
|
||||||
|
return o.strftime('%Y-%m-%d')
|
||||||
|
elif isinstance(o, Decimal):
|
||||||
|
return float(o)
|
||||||
|
|
||||||
|
return json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
|
||||||
|
# 生成指定长度的随机数
|
||||||
|
def generate_random_str(length: int = 4, is_digits: bool = True) -> str:
|
||||||
|
words = string.digits if is_digits else string.ascii_letters + string.digits
|
||||||
|
return ''.join(random.sample(words, length))
|
|
@ -0,0 +1,26 @@
|
||||||
|
import ipaddress
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
# 判断是否是ip地址
|
||||||
|
def ip_validator(value):
|
||||||
|
try:
|
||||||
|
ipaddress.ip_address(value)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 判断是否是日期字符串,支持 2018-04-11 或 2018-04-11 14:55:30
|
||||||
|
def date_validator(value: str) -> bool:
|
||||||
|
value = value.strip()
|
||||||
|
try:
|
||||||
|
if len(value) == 10:
|
||||||
|
datetime.strptime(value, '%Y-%m-%d')
|
||||||
|
return True
|
||||||
|
elif len(value) == 19:
|
||||||
|
datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return False
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""Django's command-line utility for administrative tasks."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'spug.settings')
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
) from exc
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,2 @@
|
||||||
|
Django==2.2.7
|
||||||
|
channels==2.3.1
|
|
@ -0,0 +1,4 @@
|
||||||
|
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||||
|
|
||||||
|
application = ProtocolTypeRouter({
|
||||||
|
})
|
|
@ -0,0 +1,69 @@
|
||||||
|
"""
|
||||||
|
Django settings for spug project.
|
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 2.2.7.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/2.2/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/2.2/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = 'vk0do47)egwzz!uk49%(y3s(fpx4+ha@ugt-hcv&%&d@hwr&p7'
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'channels',
|
||||||
|
'apps.account',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'spug.urls'
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'spug.wsgi.application'
|
||||||
|
ASGI_APPLICATION = 'spug.routing.application'
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/2.2/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
|
TIME_ZONE = 'Asia/Shanghai'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_L10N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
AUTHENTICATION_EXCLUDES = ()
|
|
@ -0,0 +1,20 @@
|
||||||
|
"""spug URL Configuration
|
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
|
https://docs.djangoproject.com/en/2.2/topics/http/urls/
|
||||||
|
Examples:
|
||||||
|
Function views
|
||||||
|
1. Add an import: from my_app import views
|
||||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||||
|
Class-based views
|
||||||
|
1. Add an import: from other_app.views import Home
|
||||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||||
|
Including another URLconf
|
||||||
|
1. Import the include() function: from django.urls import include, path
|
||||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
|
from django.urls import path, include
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('account/', include('apps.account.urls'))
|
||||||
|
]
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""
|
||||||
|
WSGI config for spug project.
|
||||||
|
|
||||||
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'spug.settings')
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
|
@ -0,0 +1,29 @@
|
||||||
|
import argparse
|
||||||
|
import django
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
sys.path.append(BASE_DIR)
|
||||||
|
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", 'spug.settings')
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from apps.account.models import User
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='创建用户')
|
||||||
|
parser.add_argument('-u', required=True, metavar='username', help='账户名称')
|
||||||
|
parser.add_argument('-p', required=True, metavar='password', help='账户密码')
|
||||||
|
parser.add_argument('-n', default='', metavar='nickname', help='账户昵称')
|
||||||
|
parser.add_argument('-s', default=False, action='store_true', help='是否是超级用户(默认否)')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
User.objects.create(
|
||||||
|
username=args.u,
|
||||||
|
nickname=args.n,
|
||||||
|
password_hash=User.make_password(args.p),
|
||||||
|
is_supper=args.s,
|
||||||
|
)
|
||||||
|
|
||||||
|
print('创建成功')
|
Loading…
Reference in New Issue