class BaseModel(db.Model): """模型抽象基础类 通过设置__abstract__属性,成为SQLAlchemy 抽象基类, 不再映射到数据库中 """ __abstract__ = True updated_at = db.Column(db.DateTime, default=datetime.utcnow) created_at = db.Column(db.DateTime, default=datetime.utcnow) def __repr__(self): try: identifier = self.name except AttributeError: identifier = self.id return "<{} {}>".format(self.__class__.__name__, identifier) # 结果形如<类名 name> def save(self): """保存到数据库中 """ db.session.add(self) db.session.commit() def delete(self): """从数据库中删除 """ db.session.delete(self) db.session.commit()
class Server(BaseModel): """Redis服务器模型 """ __tablename__ = 'redis_server' id = db.Column(db.Integer, primary_key=True) # unique = True 设置不能有同名的服务器 name = db.Column(db.String(64), unique=True) description = db.Column(db.String(512)) host = db.Column(db.String(15)) port = db.Column(db.Integer, default=6379) password = db.Column(db.String()) @property def redis(self): return StrictRedis(host=self.host, port=self.port, password=self.password) def ping(self): """检查 Redis 服务器是否可以访问 """ try: return self.redis.ping() except RedisError: raise RedisConnectError( 400, 'redis server %s can not connected' % self.host) @property def status(self): """服务器当前状态 """ status = 'error' try: if self.ping(): status = 'ok' except RedisConnectError: pass return status def get_metrics(self): """获取 Redis 服务器监控信息 通过 Redis 服务器指令 INFO 返回监控信息, 参考 https://redis.io/commands/INFO """ try: # TODO 新版本的 Redis 服务器支持查看某一 setion 的信息,不必返回所有信息 return self.redis.info() except RedisError: raise RedisConnectError( 400, 'redis server %s can not connected' % self.host)
class Server(BaseModel): """Redis服务器模型 """ __tablename__ = 'redis_server' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True) description = db.Column(db.String(512)) host = db.Column(db.String(15)) port = db.Column(db.Integer, default=6379) password = db.Column(db.String()) def __repr__(self): return '<Server(name=%s)>' % self.name @property def redis(self): return StrictRedis(host=self.host, port=self.port, password=self.password) def ping(self): """检查Redis服务器是否可以访问 """ try: return self.redis.ping() # 这里调用的是StrictRedis对象的ping方法 except RedisError: raise RestException( 400, 'redis server %s can not connected' % self.host) def get_metrics(self): """获取Redis服务器监控信息 通过Redis服务器指令INFO返回监控信息 """ try: return self.redis.info() # 这里调用的是StrictRedis对象的info方法 except RedisError: raise RestException( 400, 'redis server %s can not connected' % self.host)
class User(BaseModel): """用户模型 """ __tablename__ = 'user' id = db.Column(db.Integer, primary_key=True) wx_id = db.Column(db.String(32), unique=True) name = db.Column(db.String(64), unique=True) email = db.Column(db.String(64), unique=True) _password = db.Column(db.String(128)) is_admin = db.Column(db.Boolean, default=False) login_at = db.Column(db.DateTime) @property def password(self): return self._password @password.setter def password(self, passwd): """设置密码 """ self._password = generate_password_hash(passwd) def verify_password(self, password): """检查密码 """ return check_password_hash(self.password, password) @classmethod def authenticate(cls, identifier, password): """认证用户 Args: identifier(str): 用户名或邮箱 password(str): 用户密码 Returns: object: 用户对象 Raise: AuthenticationError """ user = cls.query.filter( db.or_(cls.name == identifier, cls.email == identifier)).first() if user is None or not user.verify_password(password): raise AuthenticationError(403, 'authentication failed') return user def generate_token(self): """生成json web token 生成token, 有效期为1天,过期后十分钟内可以使用老token刷新获取新的token """ # token过期时间,默认在一天后过期 exp = datatime.utcnow() + timedelta(days=1) # token过期后十分钟内,还可以使用老token进行刷新token refresh_exp = timegm((exp + timedelta(seconds=60 * 10)).utctimetuple()) payload = { 'uid': self.id, 'is_admin': self.is_admin, 'exp': exp, 'refresh_exp': refresh_exp } return jwt.encode(payload, current_app.secret_key, algorithm='HS512').decode('utf-8') @classmethod def verify_token(cls, token, verify_exp=True): """检查验证json web token Args: token(str): json web token verify_exp(bool): 是否验证token的过期时间 Return: object: 返回用户对象 Raise: InvalidTokenError """ now = datetime.utcnow() if verify_exp: options = None else: options = {'verify_exp': False} try: payload = jwt.decode(token, current_app.secret_key, verify=True, algorithm=['HS512'], options=options, require_exp=True) except jwt.InvalidTokenError as e: raise InvalidTokenError(403, str(e)) # 验证token是否正确 if any(('is_admin' not in payload, 'refresh_exp' not in payload, 'uid' not in payload)): raise InvalidTokenError(403, 'invalid token') # 如果刷新时间过期,则认为token无效 if payload['refresh_exp'] < timegm(now.utctimetuple()): raise InvalidTokenError(403, 'invalid token') u = User.query.get(payload.get('uid')) if u is None: raise InvalidTokenError(403, 'user not exist') return u @classmethod def create_administrator(cls): """创建管理员账户 Return: name(str): 管理员账户名称 password(str): 管理员账户密码 """ name = 'admin' # 管理员账户名称默认为admin admin = cls.query.filter_by(name=name).first() if admin: return admin.name, '' password = '******' admin = User(name=name, email='*****@*****.**', is_admin=True) admin.password = password admin.save() return name, password @classmethod def wx_id_user(cls, wx_id): """根据 wx_id获取用户 """ return cls.query.filter_by(wx_id=wx_id).first()