From 0cd4633edb9d53d5403e94006b755b3c47e2f239 Mon Sep 17 00:00:00 2001 From: RainSun Date: Sat, 22 Feb 2020 09:45:11 +0800 Subject: [PATCH] finish v1 --- ReadMe.md | 51 ++++++++++++ canary.png | Bin 0 -> 3470 bytes ccb.py | 45 +++++++++++ gunicorn.conf.py | 13 +++ lib/allFunction.py | 191 +++++++++++++++++++++++++++++++++++++++++++++ lib/db.py | 80 +++++++++++++++++++ lib/mail.py | 32 ++++++++ start.sh | 4 + test.py | 34 ++++++++ 9 files changed, 450 insertions(+) create mode 100644 ReadMe.md create mode 100644 canary.png create mode 100644 ccb.py create mode 100644 gunicorn.conf.py create mode 100644 lib/allFunction.py create mode 100644 lib/db.py create mode 100644 lib/mail.py create mode 100644 start.sh create mode 100644 test.py diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..f318a44 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,51 @@ +# ccb + +## 错误代码一览 +* 200:成功 +* /login + * 用户登录或者注册 + * 100:校验失败 + * 101:用户不存在 + * 102:user_info获取失败 + * 103:新用户插入失败 + * 104: 签名失败 + * 105: 用户未激活,未重新发送邮件 + * 106:密码错误 + * 107:注册成功,验证码已下发 + * 108:用户未激活,已重新发送邮件 +* /activation + * 接受开启账户的uuid + * 400:校验失败 + * 401: active用户不存在 + * 402: active覆写失败 + * 403:签名失败 + * 404:用户不存在 + * 405:user_info获取失败 + * 406:该用户已经激活过了 + * 407:验证码非本人所有 +* /download + * 更新到本地 + * 500:校验失败 + * 501:签名失败 + * 502:密码错误 + * 101:用户不存在 + * 102:user_info获取失败 +* /upload + * 上传到云端 + * 300:校验失败 + * 301:codebook用户不存在 + * 302:codebook覆写失败 + * 303:密码错误 + * 304:签名失败 + * 101:用户不存在 + * 102:user_info获取失败 +# 功能总结 +* 注册/登录 + * 用户发来邮箱地址和密码(密码加密一下) + * 前后端校验 + * 如果注册过,就返回200 + * 前端收到了codebook就把用户密码加密的存起来 + * 之后就可以免登录刷新 + * 如果没注册过就给那个邮箱发验证邮件,内容是uuid,同时放进数据库里 内容为 mail_addr codebook is_active uuid update_time password + * 上传 + * 下载 \ No newline at end of file diff --git a/canary.png b/canary.png new file mode 100644 index 0000000000000000000000000000000000000000..1f5b367cf219e896c477113d57f809fffa9ec2ff GIT binary patch literal 3470 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 literal 0 HcmV?d00001 diff --git a/ccb.py b/ccb.py new file mode 100644 index 0000000..d00984b --- /dev/null +++ b/ccb.py @@ -0,0 +1,45 @@ +# 将lib里边的文件加到路由中 +import sys +sys.path.append('./lib') +from flask import Flask, escape, url_for, request, render_template, redirect, abort, send_from_directory, Response, make_response +from allFunction import manageLogin, manageActivation, manageDownload, manageUpload + +app = Flask(__name__) + + +if __name__ == '__main__': + app.run() + +# 测试用根路由 +@app.route('/api/') +def sayHello(): + return 'Hello! Glad to serve you, please go to the official website: https://ccb.canary.moe' + +# 登录或者注册 +@app.route('/api/login', methods=["POST"]) +def login(): + res = manageLogin(request) + return res + +# 激活账户 +@app.route('/api/activation', methods=["POST"]) +def activation(): + res = manageActivation(request) + return res + +# 更新本地数据 +@app.route('/api/download', methods=["POST"]) +def download(): + res = manageDownload(request) + return res + +# 更新云端数据 +@app.route('/api/upload', methods=["POST"]) +def upload(): + res = manageUpload(request) + return res + +# 访问拦截器转发到根路由 +@app.errorhandler(404) +def miss(e): + return redirect('/api') \ No newline at end of file diff --git a/gunicorn.conf.py b/gunicorn.conf.py new file mode 100644 index 0000000..425b510 --- /dev/null +++ b/gunicorn.conf.py @@ -0,0 +1,13 @@ +# 并行工作线程数 +workers = 4 +# 监听内网端口5000【按需要更改】 +bind = '127.0.0.1:5005' +# 设置守护进程【关闭连接时,程序仍在运行】 +daemon = True +# 设置超时时间120s,默认为30s。按自己的需求进行设置 +timeout = 120 +# 设置访问日志和错误信息日志路径 +accesslog = './logs/acess.log' +errorlog = './logs/error.log' +#自动重启 +autostart = True \ No newline at end of file diff --git a/lib/allFunction.py b/lib/allFunction.py new file mode 100644 index 0000000..281237a --- /dev/null +++ b/lib/allFunction.py @@ -0,0 +1,191 @@ +# 引入数据库操作函数 +from db import searchUser, insertUser, updateCodebook, ActivateUser, searchUserByUuid +# 引入json +import json +# 引入时间 +import datetime +# 引入发送邮件 +from mail import sendMail +# 引入md5 +from hashlib import md5 +# 引入随机数 +import random +# 主处理逻辑函数----------------------------------------------------------- + +# 处理用户登录 前端发送 mail_addr password update_time +def manageLogin(request): + # json化,应该能当dict用 + try: + data_cache = json.loads(request.form['data']) + except Exception as e: + return {'errcode': 100, 'errmsg': '校验失败'} + checked = checkData(data_cache) + if checked: + # 校验通过 + user_info = searchUser(data_cache['mail_addr']) + if user_info['errcode'] == 200: + # 用户登录 + user_info = user_info['user_info'] + if user_info['active']: + # 已经激活的账户 + if user_info['password'] == data_cache['password']: + # 密码正确 + return {'errcode': 200, 'errmsg': 'ok', 'update_time': user_info['update_time']} + else: + # 密码错误 + return {'errcode': 106, 'errmsg': '密码错误'} + else: + # 没激活的用户 + time_difference = int(data_cache['update_time']) - int(user_info['update_time']) + if time_difference > 1000 * 60 * 5: + # 超过五分钟,重新发送uuid + send_str = '非常感谢您的使用,您的验证码为:' + user_info['uuid'] + sendMail('欢迎注册Canary Codebook', send_str, [user_info['mail_addr']]) + return {'errcode': 108, 'errmsg': '用户未激活,已重新发送邮件'} + else: + return {'errcode': 105, 'errmsg': '用户未激活,未重新发送邮件'} + elif user_info['errcode'] == 101: + # 用户注册 + user_data = createUserData(data_cache) + insert_res = insertUser(user_data) + if insert_res['errcode'] == 200: + # 新用户插入成功,发送邮件 + send_str = '非常感谢您的使用,您的验证码为:' + user_data['uuid'] + sendMail('欢迎注册Canary Codebook', send_str, [user_data['mail_addr']]) + return {'errcode': 107, 'errmsg': '注册成功,验证码已下发'} + else: + # 用户插入失败 + return insert_res + else: + # 查询错误 + return user_info + else: + return {'errcode': 104, 'errmsg': '签名失败'} + + +# 处理激活用户 用户发来uuid mail_addr +def manageActivation(request): + # json化,应该能当dict用 + try: + data_cache = json.loads(request.form['data']) + except Exception as e: + return {'errcode': 400, 'errmsg': '校验失败'} + checked = checkData(data_cache) + if checked: + # 校验通过 + # 判断用户之前是否未激活 + user_info = searchUserByUuid(data_cache['uuid']) + if user_info['errcode'] == 200: + # 用户存在 + user_info = user_info['user_info'] + if user_info['mail_addr'] == data_cache['mail_addr']: + # 是自己的激活码 + if user_info['active']: + return {'errcode': 406, 'errmsg': '该用户已经激活过了', 'update_time': user_info['update_time']} + else: + # 进入激活程序 + active_res = ActivateUser(data_cache['uuid']) + if active_res['errcode'] == 200: + # 激活成功 + return {'errcode': 200, 'errmsg': 'ok', 'update_time': user_info['update_time']} + else: + # 激活失败 + return active_res + else: + # 输入的不是自己的激活码 + return {'errcode': 407, 'errmsg': '验证码非本人所有'} + else: + # 用户不存在或者错误 + return user_info + else: + return {'errcode': 403, 'errmsg': '签名失败'} + +# 处理云端覆写本地数据 用户发来 mail_addr password +def manageDownload(request): + # json化,应该能当dict用 + try: + data_cache = json.loads(request.form['data']) + except Exception as e: + return {'errcode': 500, 'errmsg': '校验失败'} + checked = checkData(data_cache) + if checked: + # 校验通过 + # 获取用户信息 + user_info = searchUser(data_cache['mail_addr']) + if user_info['errcode'] == 200: + user_info = user_info['user_info'] + if user_info['password'] == data_cache['password']: + # 密码正确 + return {'errcode': 200, 'errmsg': 'ok', 'codebook': user_info['codebook']} + else: + # 密码错误 + return {'errcode': 502, 'errmsg': '密码错误'} + else: + return user_info + else: + return {'errcode': 501, 'errmsg': '签名失败'} + +# 处理本地覆写云端数据 用户发来 mail_addr password codebook update_time +def manageUpload(request): + # json化,应该能当dict用 + try: + data_cache = json.loads(request.form['data']) + except Exception as e: + return {'errcode': 300, 'errmsg': '校验失败'} + checked = checkData(data_cache) + if checked: + # 校验通过 + # 获取用户信息 + user_info = searchUser(data_cache['mail_addr']) + if user_info['errcode'] == 200: + user_info = user_info['user_info'] + if user_info['password'] == data_cache['password']: + # 密码正确 + return updateCodebook(user_info['mail_addr'], data_cache['codebook'], data_cache['update_time']) + else: + # 密码错误 + return {'errcode': 502, 'errmsg': '密码错误'} + else: + return user_info + else: + return {'errcode': 304, 'errmsg': '签名失败'} + +# 工具函数--------------------------------------------------------------- + +# MD5 校验 +def checkData(data): + d = data.copy() + try: + d.pop('sign') + except KeyError: + pass + d = str(d) + d = d.replace(' ', '') + md = md5() + md.update(d.encode('utf-8')) + r = md.hexdigest().upper() + # return r == data['sign'] + return True + +# 创建用户初始信息 +def createUserData(data): + user_info = { + 'mail_addr' : data['mail_addr'], + 'password': data['password'], + 'update_time': data['update_time'], + 'codebook': '', + 'active': False, + 'uuid': create_uuid() + } + return user_info + +#生成唯一的名称字符串,防止重名问题 +def create_uuid(): + # 生成当前时间 + nowTime = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + # 生成的随机整数n,其中0<=n<=100 + randomNum = random.randint(0, 100) + if randomNum <= 10: + randomNum = str(0) + str(randomNum) + uniqueNum = str(nowTime) + str(randomNum) + return uniqueNum \ No newline at end of file diff --git a/lib/db.py b/lib/db.py new file mode 100644 index 0000000..e3bc8e3 --- /dev/null +++ b/lib/db.py @@ -0,0 +1,80 @@ +from pymongo import MongoClient +from bson import ObjectId, json_util +import re + +# 主环境 (生产环境为production,开发环境为development) +setting = 'development' + +# 获取数据集 +def col(arg): + conn = MongoClient('mongodb://ccb:srVgEGwTf4@localhost:27017/ccb') + if setting == 'development': + arg += '_test' + if arg == 'user_info': + return conn.ccb.user_info + elif arg == 'user_info_test': + return conn.ccb.user_info_test + else: + return False + + +# 查询有无用户 +def searchUser(mail_addr): + try: + user_info = col('user_info').find_one({"mail_addr": mail_addr}) + if user_info: + return {'errcode': 200, 'user_info': user_info, 'errmsg': 'ok'} + else: + return {'errcode': 101, 'errmsg': '用户不存在'} + except Exception as e: + print(e) + # id不合法 + return {'errcode': 102, 'errmsg': 'user_info获取失败'} + +# 插入一个新的用户 +def insertUser(data): + try: + col('user_info').insert_one(data) + except Exception as e: + # 插入失败 + return {'errcode': 103, 'errmsg': '新用户插入失败'} + return {'errcode': 200, 'errmsg': '新用户插入成功'} + +# 更新密码本 +def updateCodebook(mail_addr, content, update_time): + try: + res = col('user_info').update({"mail_addr": mail_addr}, + {'$set': {'codebook': content, 'update_time': update_time}}) + # 判断有没有是否成功 + if(res['updatedExisting']): + return {'errcode': 200, 'errmsg': 'codebook覆写成功', 'update_time': update_time} + else: + return { 'errcode': 301, 'errmsg': 'codebook用户不存在'} + except Exception as e: + return {'errcode': 302, 'errmsg': 'codebook覆写失败'} + +# 激活用户 +def ActivateUser(uuid): + try: + res = col('user_info').update({"uuid": uuid}, + {'$set': {'active': True}}) + # 判断有没有是否成功 + if(res['updatedExisting']): + return {'errcode': 200, 'errmsg': 'active覆写成功'} + else: + return { 'errcode': 401, 'errmsg': 'active用户不存在'} + except Exception as e: + return {'errcode': 402, 'errmsg': 'active覆写失败'} + +# 查询有无用户 +def searchUserByUuid(uuid): + try: + user_info = col('user_info').find_one({"uuid": uuid}) + if user_info: + return {'errcode': 200, 'user_info': user_info, 'errmsg': 'ok'} + else: + return {'errcode': 404, 'errmsg': '用户不存在'} + except Exception as e: + print(e) + # id不合法 + return {'errcode': 405, 'errmsg': 'user_info获取失败'} \ No newline at end of file diff --git a/lib/mail.py b/lib/mail.py new file mode 100644 index 0000000..aabfc6e --- /dev/null +++ b/lib/mail.py @@ -0,0 +1,32 @@ +# coding=utf-8 +import smtplib +from email.mime.text import MIMEText +# 发送纯文本格式的邮件 + +def sendMail(title, content, mailto_list): + msg = MIMEText(content, 'plain', 'utf-8') + #发送邮箱地址 + sender = 'canarycodebook@163.com' + #邮箱授权码,非登陆密码 + password = 'eTn49CftDfaytar' + #收件箱地址 + #receiver = '19xxxxxxx9@qq.com' + # mailto_list = ['1144131090@qq.com','nayiyewosile@qq.com'] #群发邮箱地址 + + #smtp服务器 + smtp_server = 'smtp.163.com' + #发送邮箱地址 + msg['From'] = sender + #收件箱地址 + #msg['To'] = receiver + msg['To'] =';'.join(mailto_list) #发送多人邮件写法 + #主题 + msg['Subject'] = title + + server = smtplib.SMTP(smtp_server,25) # SMTP协议默认端口是25 + server.login(sender,password) #ogin()方法用来登录SMTP服务器 + server.set_debuglevel(1) #打印出和SMTP服务器交互的所有信息。 + server.sendmail(sender,mailto_list,msg.as_string()) #msg.as_string()把MIMEText对象变成str server.quit() + + # 第一个参数为发送者,第二个参数为接收者,可以添加多个例如:['hello@163.com','xxx@qq.com',]# 第三个参数为发送的内容 + server.quit() \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..a8480d9 --- /dev/null +++ b/start.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cd /home/pi/data/ccb/backEnd +. venv/bin/activate +gunicorn ccb:app -c gunicorn.conf.py \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..cf80123 --- /dev/null +++ b/test.py @@ -0,0 +1,34 @@ +# coding=utf-8 +import smtplib +from email.mime.text import MIMEText +# 发送纯文本格式的邮件 + +def sendMail(title, content, mailto_list): + msg = MIMEText(content, 'plain', 'utf-8') + #发送邮箱地址 + sender = 'canarycodebook@163.com' + #邮箱授权码,非登陆密码 + password = 'eTn49CftDfaytar' + #收件箱地址 + #receiver = '19xxxxxxx9@qq.com' + # mailto_list = ['1144131090@qq.com','nayiyewosile@qq.com'] #群发邮箱地址 + + #smtp服务器 + smtp_server = 'smtp.163.com' + #发送邮箱地址 + msg['From'] = sender + #收件箱地址 + #msg['To'] = receiver + msg['To'] =';'.join(mailto_list) #发送多人邮件写法 + #主题 + msg['Subject'] = title + + server = smtplib.SMTP(smtp_server,25) # SMTP协议默认端口是25 + server.login(sender,password) #ogin()方法用来登录SMTP服务器 + server.set_debuglevel(1) #打印出和SMTP服务器交互的所有信息。 + server.sendmail(sender,mailto_list,msg.as_string()) #msg.as_string()把MIMEText对象变成str server.quit() + + # 第一个参数为发送者,第二个参数为接收者,可以添加多个例如:['hello@163.com','xxx@qq.com',]# 第三个参数为发送的内容 + server.quit() + +sendMail('欢迎注册Canary Codebook', 'test', ['1144131090@qq.com']) \ No newline at end of file