rabin 1 month ago
parent
commit
32c5549af2

+ 1 - 1
demeter/__init__.py

@@ -3,4 +3,4 @@
     demeter init
     author:rabin
 """
-__version__ = '1.0.2'
+__version__ = '1.0.7'

+ 9 - 9
demeter/admin/model/manage_admin.py

@@ -7,12 +7,12 @@
 from .__load__ import *
 
 class Manage_admin(Model):
-	__table__ = 'manage_admin'
-	__comment__ = '超级管理员'
-	id = Fields(type='int', primaryKey=True, autoIncrement=True, comment='管理员ID')
-	role_id = Fields(type='int', comment='角色ID')
-	username = Fields(type='varchar(50)', comment='管理员账号')
-	mobile = Fields(type='bigint', comment='园区管理员手机号', unique=True)
-	password = Fields(type='varchar(38)', comment='安全密码', md5=True)
-	state = Fields(type='boolean', default='True', comment='数据存在状态')
-	cdate = Fields(type='int', default='time', comment='创建时间')
+    __table__ = 'manage_admin'
+    __comment__ = '超级管理员'
+    id = Fields(type='int', primaryKey=True, autoIncrement=True, comment='管理员ID')
+    role_id = Fields(type='int', comment='角色ID')
+    username = Fields(type='varchar(50)', comment='管理员账号')
+    mobile = Fields(type='bigint', comment='园区管理员手机号', unique=True)
+    password = Fields(type='varchar(38)', comment='安全密码', md5=True)
+    state = Fields(type='boolean', default='True', comment='数据存在状态')
+    cdate = Fields(type='int', default='time', comment='创建时间')

+ 9 - 9
demeter/admin/model/manage_log.py

@@ -6,12 +6,12 @@
 from .__load__ import *
 
 class Manage_log(Model):
-	__table__ = 'manage_log'
-	__comment__ = '后台日志表'
-	id = Fields(type='int', primaryKey=True, autoIncrement=True, comment='日志ID')
-	admin_id = Fields(type='int', comment='管理员ID')
-	model = Fields(type='varchar(50)', comment='操作的表')
-	method = Fields(type='varchar(50)', comment='操作的方法')
-	data = Fields(type='text', comment='数据记录')
-	state = Fields(type='boolean', default='True', comment='数据存在状态')
-	cdate = Fields(type='int', default='time', comment='创建时间')
+    __table__ = 'manage_log'
+    __comment__ = '后台日志表'
+    id = Fields(type='int', primaryKey=True, autoIncrement=True, comment='日志ID')
+    admin_id = Fields(type='int', comment='管理员ID')
+    model = Fields(type='varchar(50)', comment='操作的表')
+    method = Fields(type='varchar(50)', comment='操作的方法')
+    data = Fields(type='text', comment='数据记录')
+    state = Fields(type='boolean', default='True', comment='数据存在状态')
+    cdate = Fields(type='int', default='time', comment='创建时间')

+ 9 - 9
demeter/admin/model/manage_role.py

@@ -6,12 +6,12 @@
 from .__load__ import *
 
 class Manage_role(Model):
-	__table__ = 'manage_role'
-	__comment__ = '角色表'
-	id = Fields(type='int', primaryKey=True, autoIncrement=True, comment='角色ID')
-	name = Fields(type='varchar(50)', comment='角色名称')
-	auth = Fields(type='varchar(600)', comment='左侧菜单权限')
-	top = Fields(type='varchar(500)', comment='头部菜单权限')
-	oper = Fields(type='varchar(100)', comment='操作权限增删改查搜等')
-	state = Fields(type='boolean', default='True', comment='数据存在状态')
-	cdate = Fields(type='int', default='time', comment='创建时间')
+    __table__ = 'manage_role'
+    __comment__ = '角色表'
+    id = Fields(type='int', primaryKey=True, autoIncrement=True, comment='角色ID')
+    name = Fields(type='varchar(50)', comment='角色名称')
+    auth = Fields(type='varchar(600)', comment='左侧菜单权限')
+    top = Fields(type='varchar(500)', comment='头部菜单权限')
+    oper = Fields(type='varchar(100)', comment='操作权限增删改查搜等')
+    state = Fields(type='boolean', default='True', comment='数据存在状态')
+    cdate = Fields(type='int', default='time', comment='创建时间')

+ 95 - 95
demeter/admin/page/admin.py

@@ -7,110 +7,110 @@
 from .__load__ import *
 
 class admin_path(Load):
-	@Web.auth
-	@Web.setting
-	def get(self):
-		self.set(
-			name = u'管理员' #中文名
-			,path = '/admin/admin' #路径
-			,width = '600' # 新增页面的宽度
-			,height = '400' # 新增页面的高度
-			,search = (('label-1','cdate-time-start','cdate-time-end','name-input-mlike','mobile-input-mlike'), (u'日期范围',u'开始时间',u'截止时间',u'管理员姓名',u'手机号')) #搜索
-			,thead = (u'管理员ID',u'管理员姓名', u'所属角色', u'手机号', u'更新时间') #表头
-			,tbody = ('id','username', 'role', 'mobile', 'cdate') #表内容
-			,state = False #启用回收站
-		)
-		self.list('manage_admin')
-		self.show('list')
+    @Web.auth
+    @Web.setting
+    def get(self):
+        self.set(
+            name = u'管理员' #中文名
+            ,path = '/admin/admin' #路径
+            ,width = '600' # 新增页面的宽度
+            ,height = '400' # 新增页面的高度
+            ,search = (('label-1','cdate-time-start','cdate-time-end','name-input-mlike','mobile-input-mlike'), (u'日期范围',u'开始时间',u'截止时间',u'管理员姓名',u'手机号')) #搜索
+            ,thead = (u'管理员ID',u'管理员姓名', u'所属角色', u'手机号', u'更新时间') #表头
+            ,tbody = ('id','username', 'role', 'mobile', 'cdate') #表内容
+            ,state = False #启用回收站
+        )
+        self.list('manage_admin')
+        self.show('list')
 
 class admin_update_path(Load):
-	@Web.auth
-	@Web.setting
-	def get(self):
-		self.set(
-			path = '/admin/admin'
-			,label = (u'管理员姓名',u'所属角色',u'手机号',u'密码')
-			,update = ('username-input-required','role_id-select-required','mobile-input-phone','password-password-')
-			,update_role_id = self.service('common').list('manage_role')
-		)
-		self.one('manage_admin')
-		self.show('update')
-	@Web.auth
-	@Web.setting
-	def post(self):
-		self.update('manage_admin', '手机号已经被注册', mobile=self.data['update']['mobile'])
+    @Web.auth
+    @Web.setting
+    def get(self):
+        self.set(
+            path = '/admin/admin'
+            ,label = (u'管理员姓名',u'所属角色',u'手机号',u'密码')
+            ,update = ('username-input-required','role_id-select-required','mobile-input-phone','password-password-')
+            ,update_role_id = self.service('common').list('manage_role')
+        )
+        self.one('manage_admin')
+        self.show('update')
+    @Web.auth
+    @Web.setting
+    def post(self):
+        self.update('manage_admin', '手机号已经被注册', mobile=self.data['update']['mobile'])
 
 
 class role_path(Load):
-	@Web.auth
-	@Web.setting
-	def get(self):
-		self.set(
-			name = u'角色'
-			,path = '/admin/role'
-			,width = '800'
-			,height = '600'
-			,full = 1
-			#,add = False
-			#,edit = False
-			,search = (('label-1','cdate-time-start','cdate-time-end','name-input-mlike'), (u'日期范围',u'开始时间',u'截止时间',u'角色名称'))
-			,thead = (u'角色ID',u'角色名称', u'更新时间')
-			,tbody = ('id','name', 'cdate')
-			,state = False
-		)
-		self.list('manage_role')
-		self.show('list')
+    @Web.auth
+    @Web.setting
+    def get(self):
+        self.set(
+            name = u'角色'
+            ,path = '/admin/role'
+            ,width = '800'
+            ,height = '600'
+            ,full = 1
+            #,add = False
+            #,edit = False
+            ,search = (('label-1','cdate-time-start','cdate-time-end','name-input-mlike'), (u'日期范围',u'开始时间',u'截止时间',u'角色名称'))
+            ,thead = (u'角色ID',u'角色名称', u'更新时间')
+            ,tbody = ('id','name', 'cdate')
+            ,state = False
+        )
+        self.list('manage_role')
+        self.show('list')
 
 class role_update_path(Load):
-	@Web.auth
-	@Web.setting
-	def get(self):
-		auth = self.data['setting']['menuList']
-		self.set(
-			path = '/admin/role'
-			#,label = (u'角色名称',u'操作权限',u'左侧菜单',u'头部菜单')
-			#,update = ('name-input-required','oper-checkbox-required','auth-checkmenu-required','top-checkbox-required')
-			,label = (u'角色名称',u'左侧菜单',u'头部菜单')
-			,update = ('name-input-required','auth-checkmenu-required','top-checkbox-required')
-			,update_oper = ({'id':'select', 'name':'查询'}, {'id':'insert', 'name':'新增'}, {'id':'update', 'name':'修改'}, {'id':'delete', 'name':'删除'}, {'id':'search', 'name':'搜索'})
-			,update_auth = auth
-		)
-		self.one('manage_role')
-		self.show('update')
-	@Web.auth
-	@Web.setting
-	def post(self):
-		self.update('manage_role')
-	@Web.auth
-	@Web.setting
-	def delete(self):
-		self.drop('manage_role')
+    @Web.auth
+    @Web.setting
+    def get(self):
+        auth = self.data['setting']['menuList']
+        self.set(
+            path = '/admin/role'
+            #,label = (u'角色名称',u'操作权限',u'左侧菜单',u'头部菜单')
+            #,update = ('name-input-required','oper-checkbox-required','auth-checkmenu-required','top-checkbox-required')
+            ,label = (u'角色名称',u'左侧菜单',u'头部菜单')
+            ,update = ('name-input-required','auth-checkmenu-required','top-checkbox-required')
+            ,update_oper = ({'id':'select', 'name':'查询'}, {'id':'insert', 'name':'新增'}, {'id':'update', 'name':'修改'}, {'id':'delete', 'name':'删除'}, {'id':'search', 'name':'搜索'})
+            ,update_auth = auth
+        )
+        self.one('manage_role')
+        self.show('update')
+    @Web.auth
+    @Web.setting
+    def post(self):
+        self.update('manage_role')
+    @Web.auth
+    @Web.setting
+    def delete(self):
+        self.drop('manage_role')
 
 class log_path(Load):
-	@Web.auth
-	@Web.setting
-	def get(self):
-		self.set(
-			name = u'日志' #中文名
-			,path = '/admin/log' #路径
-			,width = '600' # 新增页面的宽度
-			,height = '400' # 新增页面的高度
-			,add = False
-			,edit = False
-			,search = (('label-1','cdate-time-start','cdate-time-end', 'admin_id-input-'), (u'日期范围',u'开始时间',u'截止时间',u'管理员ID')) #搜索
-			,thead = (u'管理员ID', u'操作表', u'方法', u'数据', u'更新时间') #表头
-			,tbody = ('admin_id', 'model', 'method', 'data', 'cdate') #表内容
-			,state = False #启用回收站
-		)
-		self.list('manage_log')
-		self.show('list')
+    @Web.auth
+    @Web.setting
+    def get(self):
+        self.set(
+            name = u'日志' #中文名
+            ,path = '/admin/log' #路径
+            ,width = '600' # 新增页面的宽度
+            ,height = '400' # 新增页面的高度
+            ,add = False
+            ,edit = False
+            ,search = (('label-1','cdate-time-start','cdate-time-end', 'admin_id-input-'), (u'日期范围',u'开始时间',u'截止时间',u'管理员ID')) #搜索
+            ,thead = (u'管理员ID', u'操作表', u'方法', u'数据', u'更新时间') #表头
+            ,tbody = ('admin_id', 'model', 'method', 'data', 'cdate') #表内容
+            ,state = False #启用回收站
+        )
+        self.list('manage_log')
+        self.show('list')
 
 class setCookie_path(Load):
-	@Web.auth
-	@Web.setting
-	def post(self):
-		value = self.input('farm', Demeter.config['setting']['farm'])
-		self.set_secure_cookie('farm', value)
-		#self.set_cookie('farm', value)
-		Demeter.config['base']['farm'] = value
+    @Web.auth
+    @Web.setting
+    def post(self):
+        value = self.input('farm', Demeter.config['setting']['farm'])
+        self.set_secure_cookie('farm', value)
+        #self.set_cookie('farm', value)
+        Demeter.config['base']['farm'] = value
 

+ 8 - 8
demeter/admin/page/main.py

@@ -7,13 +7,13 @@
 from .__load__ import *
 
 class index_path(Load):
-	@Web.auth
-	@Web.setting
-	def get(self):
-		self.view("index.html")
+    @Web.auth
+    @Web.setting
+    def get(self):
+        self.view("index.html")
 
 class main_path(Load):
-	@Web.auth
-	@Web.setting
-	def get(self):
-		self.view("main.html")
+    @Web.auth
+    @Web.setting
+    def get(self):
+        self.view("main.html")

+ 16 - 16
demeter/admin/page/upload.py

@@ -10,20 +10,20 @@ import os
 import uuid
 
 class upload_path(Load):
-	@Web.auth
-	@Web.setting
-	def post(self, *args, **kwargs):
-		url = self.request.protocol + "://" + self.request.host
-		file_metas = self.request.files["file"]
+    @Web.auth
+    @Web.setting
+    def post(self, *args, **kwargs):
+        url = self.request.protocol + "://" + self.request.host
+        file_metas = self.request.files["file"]
 
-		day = str(date.today())
-		day = day.split('-')
-		for meta in file_metas:
-			name = meta['filename']
-			temp = name.split('.')
-			file_name =  str(uuid.uuid5(uuid.uuid1(), 'file'))
-			file_path = day[0] + '/' + day[1] + '/' + day[2]
-			file_path = File.mkdirs(os.path.join(Demeter.path, 'runtime','upload', file_path)) + '/' + Demeter.md5(file_name) + '.' + temp[1]
-			with open(file_path, 'wb') as up:
-				up.write(meta['body'])
-		self.out('yes', {'src':url + file_path.replace(Demeter.path + 'runtime', '')})
+        day = str(date.today())
+        day = day.split('-')
+        for meta in file_metas:
+            name = meta['filename']
+            temp = name.split('.')
+            file_name =  str(uuid.uuid5(uuid.uuid1(), 'file'))
+            file_path = day[0] + '/' + day[1] + '/' + day[2]
+            file_path = File.mkdirs(os.path.join(Demeter.path, 'runtime','upload', file_path)) + '/' + Demeter.md5(file_name) + '.' + temp[1]
+            with open(file_path, 'wb') as up:
+                up.write(meta['body'])
+        self.out('yes', {'src':url + file_path.replace(Demeter.path + 'runtime', '')})

+ 19 - 19
demeter/admin/page/user.py

@@ -7,26 +7,26 @@
 from .__load__ import *
 
 class login_path(Load):
-	def get(self):
-		self.view("login.html")
-	def post(self):
-		mobile = self.input('username')
-		password = self.input('password')
-		if mobile and password:
-			admin = self.service('common').one('manage_admin', mobile=mobile)
-			if admin:
-				temp = admin['password'].split('_')
-				if Demeter.md5(password, temp[1]) == admin['password']:
-					self.set_secure_cookie('admin', str(admin['id']))
-					#self.redirect('/')
-					self.out('yes', {'id':admin['id']})
-					return
-		self.out('手机号或密码错误,登录失败')
+    def get(self):
+        self.view("login.html")
+    def post(self):
+        mobile = self.input('username')
+        password = self.input('password')
+        if mobile and password:
+            admin = self.service('common').one('manage_admin', mobile=mobile)
+            if admin:
+                temp = admin['password'].split('_')
+                if Demeter.md5(password, temp[1]) == admin['password']:
+                    self.set_secure_cookie('admin', str(admin['id']))
+                    #self.redirect('/')
+                    self.out('yes', {'id':admin['id']})
+                    return
+        self.out('手机号或密码错误,登录失败')
 
-		
+        
 
 
 class loginout_path(Load):
-	def get(self):
-		self.set_secure_cookie('admin', '')
-		self.redirect('/user/login')
+    def get(self):
+        self.set_secure_cookie('admin', '')
+        self.redirect('/user/login')

+ 693 - 680
demeter/core.py

@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
 """
-	demeter
-	name:core.py
-	author:rabin
+    demeter
+    name:core.py
+    author:rabin
 """
 import time
 import os
@@ -17,696 +17,709 @@ import importlib
 from watchdog.observers import Observer
 from watchdog.events import FileSystemEventHandler
 class Demeter(object):
-	path = ''
-	root = ''
-	config = {}
-	option = {}
-	web = ''
-	request = False
-	route = []
-	logger = False
-	dog = False
-	syncData = {}
-
-	def __new__(self, *args, **kwargs):
-		sys.exit()
-
-	def __init__(self):
-		pass
-
-	@staticmethod
-	def checkPy3():
-		if sys.version > '3':
-			state = True
-		else:
-			state = False
-		return state
-
-	@classmethod
-	def getConfig(self):
-		state = self.checkPy3()
-		if state:
-			import configparser
-			return configparser.ConfigParser()
-		else:
-			import ConfigParser
-			return ConfigParser.ConfigParser()
-
-	@staticmethod
-	def isset(v): 
-		try :
-			type (eval(v))
-		except :
-			return 0
-		else : 
-			return 1
-
-	@classmethod
-	def initConfig(self):
-		if not self.path:
-			self.path = File.path()
-			self.root = File.cur_path()
-		if self.config == {}:
-			filename = self.path + 'conf/'+self.getConfigName()+'.conf'
-			if File.exists(filename):
-				config = self.getConfig()
-				config.read(filename, encoding="utf-8")
-
-				for item in config.sections():
-					self.config[item] = self.readConfig(config, item)
-				return True
-			else:
-				self.path = self.path + '../'
-				return self.initConfig()
-				#Demeter.echo(filename + ' is not exists')
-				#sys.exit()
-
-	@classmethod
-	def getConfigName(self):
-		name = 'dev'
-		if 'DEMETER_CONF' in os.environ:
-			name = os.environ['DEMETER_CONF']
-		param = {}
-		param['config'] = 'c'
-		self.getopt(param)
-		if 'config' in self.option and self.option['config']:
-			name = self.option['config']
-		return name
-
-	@staticmethod
-	def readConfig(config, type):
-		value = config.options(type)
-		result = {}
-		for item in value:
-			result[item] = config.get(type, item)
-		return result
-
-	@classmethod
-	def getopt(self, param = {}):
-		import getopt
-		param['help'] = 'h'
-		shortopts = ''
-		longopts  = []
-		check = []
-		for k,v in param.items():
-			if k == 'help':
-				shortopts = shortopts + v
-			else:
-				shortopts = shortopts + v + ':'
-			longopts.append(k)
-		try:
-			options, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
-			for name, value in options:
-				for k,v in param.items():
-					if name in ('-' + v, '--' + k):
-						if k == 'help':
-							self.usage()
-						else:
-							self.option[k] = value
-		except getopt.GetoptError:
-			#self.usage()
-			return
-
-	@classmethod
-	def usage(self, name = 'usage'):
-		file = self.path + name
-		if not File.exists(file):
-			file = self.root + name
-		self.echo(File.read(file, encoding="utf-8"))
-		sys.exit()
-
-	@classmethod
-	def temp(self, key='', name='', value=''):
-		temp = Demeter.path + 'conf/temp.conf'
-		if File.exists(temp):
-			config = self.getConfig()
-			config.read(temp, encoding="utf-8")
-			if key and name:
-				config.set(key, name, value)
-				config.write(open(temp, 'w'))
-			else:
-				result = {}
-				for item in config.sections():
-					result[item] = self.readConfig(config, item)
-				return result
-
-	@classmethod
-	def echo(self, args):
-		import pprint
-		pprint.pprint(args)
-
-	@classmethod
-	def record(self, key, value):
-		# 记录日志
-		# self.log(key, value)
-		service = self.service('record')
-		service.push(key, value)
-
-	@classmethod
-	def service(self, name, parent = ''):
-		return self.load(name, parent, 'service')
-
-	@classmethod
-	def load(self, name, parent = '', base = 'service'):
-		self.initConfig()
-		path = base + '.'
-		if name == 'common':
-			path = 'demeter.'
-			name = base
-		if parent:
-			path = path + parent + '.'
-		load = self.getClass(name, path)
-		return load()
-
-	@classmethod
-	def adminModel(self, table):
-		self.initConfig()
-		config = ('manage_admin', 'manage_log', 'manage_role')
-		if table in config:
-			return self.getClass(table, 'demeter.admin.model.')
-		return False
-
-	@classmethod
-	def model(self, table, name='rdb'):
-		self.initConfig()
-		name = self.config['db'][name]
-		config = self.config[name]
-		obj = self.getObject('db', 'demeter.')
-		db = getattr(obj, name.capitalize())
-		connect = db(config).get()
-		model = self.adminModel(table)
-		if not model:
-			model = self.getClass(table, 'model.')
-		return model(name, connect, config)
-
-	@classmethod
-	def getMethod(self, module):
-		import inspect
-		return inspect.getmembers(module, callable)
-
-	@classmethod
-	def getPackage(self, package):
-		import pkgutil
-		for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__ + '.', onerror=lambda x: None):
-			yield modname
-
-	@classmethod
-	def getPath(self, path):
-		import pkgutil
-		for importer, modname, ispkg in pkgutil.walk_packages(path):
-			loader = importer.find_module(modname)
-			mod = loader.load_module(modname)
-			yield mod
-
-	@staticmethod
-	def getObject(name, path = ''):
-		return importlib.import_module(path + name)
-
-	"""
-	@classmethod
-	def getObject(self, name, path = ''):
-		module = __import__(path + name)
-		return getattr(module, name)
-	"""
-
-	@classmethod
-	def getClass(self, name, path=''):
-		obj = self.getObject(name, path)
-		return getattr(obj, name.capitalize())
-
-	@staticmethod
-	def bool(value, type = 'db'):
-		if type == 'mysql':
-			value = value == str(True)
-			if value == True:
-				return '1'
-			else:
-				return '2'
-		else:
-			return value == str(True)
-
-	@classmethod
-	def runtime(self, path, file, content=''):
-		path = self.path + 'runtime/' + path + '/'
-		File.mkdir(path)
-		file = path + file
-		if File.exists(file):
-			return False
-		else:
-			File.write(file, content)
-			return True
-
-	@classmethod
-	def webInit(self, name):
-		self.initConfig()
-		self.web = name
-		self.webPath = self.path + self.web + '/'
-		if self.web == 'admin':
-			self.webPath = self.root + self.web + '/'
-		self.getObject('main', name + '.')
-
-	@classmethod
-	def md5(self, value, salt=False):
-		import hashlib
-		if salt:
-			if salt == True:
-				salt = self.rand()
-			value = value + salt
-			return hashlib.md5(value.encode("utf-8")).hexdigest() + '_' + salt
-		else:
-			return hashlib.md5(value.encode("utf-8")).hexdigest()
-
-	@classmethod
-	def sha1(self, value, salt=False):
-		import hashlib
-		if salt:
-			if salt == True:
-				salt = self.rand()
-			value = value + salt
-			return hashlib.sha1(value.encode("utf-8")).hexdigest() + '_' + salt
-		else:
-			return hashlib.sha1(value.encode("utf-8")).hexdigest()
-
-	@classmethod
-	def range(self, length):
-		if self.checkPy3():
-			return range(length)
-		else:
-			return xrange(length)
-
-	@classmethod
-	def rand(self, length = 4):
-		module = self.getObject('random')
-		rand = getattr(module, 'randint')
-		salt = ''
-		chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
-		len_chars = len(chars) - 1
-		l = Demeter.range(length)
-		for i in l:
-			salt += chars[rand(0, len_chars)]
-		return salt
-
-	@classmethod
-	def hash(self):
-		return self.md5(str(time.perf_counter()))
-
-	@classmethod
-	def uuid(self, value):
-		import uuid
-		return str(uuid.uuid5(uuid.uuid1(), value))
-
-	@staticmethod
-	def hour(value):
-		if value < 10:
-			return '0' + str(value)
-		return value
-	@staticmethod
-	def time():
-		return int(time.time())
-
-	@staticmethod
-	def mktime(value, string='%Y-%m-%d %H:%M:%S'):
-		if ' ' in string and ' ' not in value:
-			value = value + ' 00:00:00'
-		return int(time.mktime(time.strptime(value,string)))
-
-	@classmethod
-	def date(self, value, string='%Y-%m-%d %H:%M:%S'):
-		module = self.getObject('datetime')
-		datetime = getattr(module, 'datetime')
-		fromtimestamp = getattr(datetime, 'fromtimestamp')
-		return str(fromtimestamp(value).strftime(string))
-
-	@staticmethod
-	def isJson(value):
-		result = False
-		try:
-			result = json.loads(value)
-		except ValueError:
-			return result
-		return result
-
-	@staticmethod
-	def host(value):
-		import urllib
-		protocol, s1 = urllib.splittype(value)
-		value, s2 = urllib.splithost(s1)
-		value, port = urllib.splitport(value)
-		return value
-
-	@staticmethod
-	def compressUuid(value):
-		row = value.replace('-', '')
-		code = ''
-		hash = [x for x in "0123456789-abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
-		l = Demeter.range(10)
-		for i in l:
-			enbin = "%012d" % int(bin(int(row[i * 3] + row[i * 3 + 1] + row[i * 3 + 2], 16))[2:], 10)
-			code += (hash[int(enbin[0:6], 2)] + hash[int(enbin[6:12], 2)])
-		return code
-
-	@staticmethod
-	def checkMobile(request):
-		if 'Demeter-Mobile' in request.headers:
-			return True
-		userAgent = request.headers['User-Agent']
-		# userAgent = env.get('HTTP_USER_AGENT')
-
-		_long_matches = r'googlebot-mobile|android|avantgo|blackberry|blazer|elaine|hiptop|ip(hone|od)|kindle|midp|mmp|mobile|o2|opera mini|palm( os)?|pda|plucker|pocket|psp|smartphone|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce; (iemobile|ppc)|xiino|maemo|fennec'
-		_long_matches = re.compile(_long_matches, re.IGNORECASE)
-		_short_matches = r'1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-'
-		_short_matches = re.compile(_short_matches, re.IGNORECASE)
-
-		if _long_matches.search(userAgent) != None:
-			return True
-		user_agent = userAgent[0:4]
-		if _short_matches.search(user_agent) != None:
-			return True
-		return False
-
-	@staticmethod
-	def exp(exp, value):
-		if exp:
-			exp = exp.replace('{n}', value)
-			value = str(eval(exp))
-		return value
-
-	@classmethod
-	def curl(self, url = '', param={}, method = 'get', timeout=5, max=2):
-		import requests
-		from requests.adapters import HTTPAdapter
-		try:
-			s = requests.Session()
-			s.mount('http://', HTTPAdapter(max_retries=max))
-			s.mount('https://', HTTPAdapter(max_retries=max))
-			if method == 'get':
-				req = s.get(url, params=param, timeout=timeout)
-			else:
-				req = s.post(url, params=param, timeout=timeout)
-			result = req.text
-		except requests.exceptions.RequestException as e:
-			result = False
-		return result
-
-	@classmethod
-	def out(self, msg='', data={}, code=0, callback='', function=''):
-		if data:
-			if 'page' in data and data['page']['total'] <= 0:
-				del data['page']
-			if 'update' in data and not data['update']:
-				del data['update']
-			if 'search' in data and not data['search']:
-				del data['search']
-		result = ''
-		send = {}
-		send['status'] = 1
-		send['msg'] = msg
-		send['data'] = data
-		send['code'] = code
-		if not send['data']:
-			send['status'] = 2
-		result = json.dumps(send)
-		if callback:
-			result = callback + '(' + result + ')'
-		elif function:
-			result = '<script>parent.' + function + '(' + result + ')' + '</script>';
-		return result
-
-	@classmethod
-	def error(self, string):
-		if self.request:
-			self.request.out(string)
-		else:
-			self.echo(string)
-			#os._exit(0)
-
-	@classmethod
-	def redis(self):
-		self.initConfig()
-		import redis
-		config = self.config['redis']
-		pool = redis.ConnectionPool(host=config['host'], password=config['password'], port=int(config['port']))
-		return redis.Redis(connection_pool=pool)
-
-	@classmethod
-	def sync(self, table, id):
-		table = table.replace('demeter_', '')
-		if 'sync' in Demeter.config:
-			config = Demeter.config['sync']['table'].split(',')
-			service = Demeter.config['sync']['service'].split(',')
-			if table in config and table not in self.syncData:
-				Demeter.service(service[0], service[1]).rsync(table, id)
-				#self.syncData[table] = True
+    path = ''
+    root = ''
+    config = {}
+    option = {}
+    web = ''
+    request = False
+    route = []
+    logger = False
+    dog = False
+    syncData = {}
+
+    def __new__(self, *args, **kwargs):
+        sys.exit()
+
+    def __init__(self):
+        pass
+
+    @staticmethod
+    def checkPy3():
+        if sys.version > '3':
+            state = True
+        else:
+            state = False
+        return state
+
+    @classmethod
+    def getConfig(self):
+        state = self.checkPy3()
+        if state:
+            import configparser
+            return configparser.ConfigParser()
+        else:
+            import ConfigParser
+            return ConfigParser.ConfigParser()
+
+    @staticmethod
+    def isset(v): 
+        try :
+            type (eval(v))
+        except :
+            return 0
+        else : 
+            return 1
+
+    @classmethod
+    def initConfig(self):
+        if not self.path:
+            self.path = File.path()
+            self.root = File.cur_path()
+        if self.config == {}:
+            filename = self.path + 'conf/'+self.getConfigName()+'.conf'
+            if File.exists(filename):
+                config = self.getConfig()
+                config.read(filename, encoding="utf-8")
+
+                for item in config.sections():
+                    self.config[item] = self.readConfig(config, item)
+                return True
+            else:
+                self.path = self.path + '../'
+                return self.initConfig()
+                #Demeter.echo(filename + ' is not exists')
+                #sys.exit()
+
+    @classmethod
+    def getConfigName(self):
+        name = 'dev'
+        if 'DEMETER_CONF' in os.environ:
+            name = os.environ['DEMETER_CONF']
+        param = {}
+        param['config'] = 'c'
+        self.getopt(param)
+        if 'config' in self.option and self.option['config']:
+            name = self.option['config']
+        return name
+
+    @staticmethod
+    def readConfig(config, type):
+        value = config.options(type)
+        result = {}
+        for item in value:
+            result[item] = config.get(type, item)
+        return result
+
+    @classmethod
+    def getopt(self, param = {}):
+        import getopt
+        param['help'] = 'h'
+        shortopts = ''
+        longopts  = []
+        check = []
+        for k,v in param.items():
+            if k == 'help':
+                shortopts = shortopts + v
+            else:
+                shortopts = shortopts + v + ':'
+            longopts.append(k)
+        try:
+            options, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
+            for name, value in options:
+                for k,v in param.items():
+                    if name in ('-' + v, '--' + k):
+                        if k == 'help':
+                            self.usage()
+                        else:
+                            self.option[k] = value
+        except getopt.GetoptError:
+            #self.usage()
+            return
+
+    @classmethod
+    def usage(self, name = 'usage'):
+        file = self.path + name
+        if not File.exists(file):
+            file = self.root + name
+        self.echo(File.read(file, encoding="utf-8"))
+        sys.exit()
+
+    @classmethod
+    def temp(self, key='', name='', value=''):
+        temp = Demeter.path + 'conf/temp.conf'
+        if File.exists(temp):
+            config = self.getConfig()
+            config.read(temp, encoding="utf-8")
+            if key and name:
+                config.set(key, name, value)
+                config.write(open(temp, 'w'))
+            else:
+                result = {}
+                for item in config.sections():
+                    result[item] = self.readConfig(config, item)
+                return result
+
+    @classmethod
+    def echo(self, args):
+        import pprint
+        pprint.pprint(args)
+
+    @classmethod
+    def record(self, key, value):
+        # 记录日志
+        # self.log(key, value)
+        service = self.service('record')
+        service.push(key, value)
+
+    @classmethod
+    def service(self, name, parent = ''):
+        return self.load(name, parent, 'service')
+
+    @classmethod
+    def load(self, name, parent = '', base = 'service'):
+        self.initConfig()
+        path = base + '.'
+        if name == 'common':
+            path = 'demeter.'
+            name = base
+        if parent:
+            path = path + parent + '.'
+        load = self.getClass(name, path)
+        return load()
+
+    @classmethod
+    def adminModel(self, table):
+        self.initConfig()
+        config = ('manage_admin', 'manage_log', 'manage_role')
+        if table in config:
+            return self.getClass(table, 'demeter.admin.model.')
+        return False
+
+    @classmethod
+    def model(self, table, name='rdb'):
+        self.initConfig()
+        name = self.config['db'][name]
+        config = self.config[name]
+        obj = self.getObject('db', 'demeter.')
+        db = getattr(obj, name.capitalize())
+        connect = db(config).get()
+        model = self.adminModel(table)
+        if not model:
+            model = self.getClass(table, 'model.')
+        return model(name, connect, config)
+
+    @classmethod
+    def getMethod(self, module):
+        import inspect
+        return inspect.getmembers(module, callable)
+
+    @classmethod
+    def getPackage(self, package):
+        import pkgutil
+        for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__ + '.', onerror=lambda x: None):
+            yield modname
+
+    @classmethod
+    def getPath(self, path):
+        import pkgutil
+        for importer, modname, ispkg in pkgutil.walk_packages(path):
+            loader = importer.find_module(modname)
+            mod = loader.load_module(modname)
+            yield mod
+
+    @staticmethod
+    def getObject(name, path = ''):
+        return importlib.import_module(path + name)
+
+    """
+    @classmethod
+    def getObject(self, name, path = ''):
+        module = __import__(path + name)
+        return getattr(module, name)
+    """
+
+    @classmethod
+    def getClass(self, name, path=''):
+        obj = self.getObject(name, path)
+        return getattr(obj, name.capitalize())
+
+    @staticmethod
+    def bool(value, type = 'db'):
+        if type == 'mysql':
+            value = value == str(True)
+            if value == True:
+                return '1'
+            else:
+                return '2'
+        else:
+            return value == str(True)
+
+    @classmethod
+    def runtime(self, path, file, content=''):
+        path = self.path + 'runtime/' + path + '/'
+        File.mkdir(path)
+        file = path + file
+        if File.exists(file):
+            return False
+        else:
+            File.write(file, content)
+            return True
+
+    @classmethod
+    def webInit(self, name):
+        self.initConfig()
+        self.web = name
+        self.webPath = self.path + self.web + '/'
+        if self.web == 'admin':
+            self.webPath = self.root + self.web + '/'
+        self.getObject('main', name + '.')
+
+    @classmethod
+    def md5(self, value, salt=False):
+        import hashlib
+        if salt:
+            if salt == True:
+                salt = self.rand()
+            value = value + salt
+            return hashlib.md5(value.encode("utf-8")).hexdigest() + '_' + salt
+        else:
+            return hashlib.md5(value.encode("utf-8")).hexdigest()
+
+    @classmethod
+    def sha1(self, value, salt=False):
+        import hashlib
+        if salt:
+            if salt == True:
+                salt = self.rand()
+            value = value + salt
+            return hashlib.sha1(value.encode("utf-8")).hexdigest() + '_' + salt
+        else:
+            return hashlib.sha1(value.encode("utf-8")).hexdigest()
+
+    @classmethod
+    def range(self, length):
+        if self.checkPy3():
+            return range(length)
+        else:
+            return xrange(length)
+
+    @classmethod
+    def rand(self, length = 4):
+        module = self.getObject('random')
+        rand = getattr(module, 'randint')
+        salt = ''
+        chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
+        len_chars = len(chars) - 1
+        l = Demeter.range(length)
+        for i in l:
+            salt += chars[rand(0, len_chars)]
+        return salt
+
+    @classmethod
+    def hash(self):
+        return self.md5(str(time.perf_counter()))
+
+    @classmethod
+    def uuid(self, value):
+        import uuid
+        return str(uuid.uuid5(uuid.uuid1(), value))
+
+    @staticmethod
+    def hour(value):
+        if value < 10:
+            return '0' + str(value)
+        return value
+    @staticmethod
+    def time():
+        return int(time.time())
+
+    @staticmethod
+    def mktime(value, string='%Y-%m-%d %H:%M:%S'):
+        if ' ' in string and ' ' not in value:
+            value = value + ' 00:00:00'
+        return int(time.mktime(time.strptime(value,string)))
+
+    @classmethod
+    def date(self, value, string='%Y-%m-%d %H:%M:%S'):
+        module = self.getObject('datetime')
+        datetime = getattr(module, 'datetime')
+        fromtimestamp = getattr(datetime, 'fromtimestamp')
+        return str(fromtimestamp(value).strftime(string))
+
+    @staticmethod
+    def isJson(value):
+        result = False
+        try:
+            result = json.loads(value)
+        except ValueError:
+            return result
+        return result
+
+    @staticmethod
+    def host(value):
+        import urllib
+        protocol, s1 = urllib.splittype(value)
+        value, s2 = urllib.splithost(s1)
+        value, port = urllib.splitport(value)
+        return value
+
+    @staticmethod
+    def compressUuid(value):
+        row = value.replace('-', '')
+        code = ''
+        hash = [x for x in "0123456789-abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
+        l = Demeter.range(10)
+        for i in l:
+            enbin = "%012d" % int(bin(int(row[i * 3] + row[i * 3 + 1] + row[i * 3 + 2], 16))[2:], 10)
+            code += (hash[int(enbin[0:6], 2)] + hash[int(enbin[6:12], 2)])
+        return code
+
+    @staticmethod
+    def checkMobile(request):
+        if 'Demeter-Mobile' in request.headers:
+            return True
+        userAgent = request.headers['User-Agent']
+        # userAgent = env.get('HTTP_USER_AGENT')
+
+        _long_matches = r'googlebot-mobile|android|avantgo|blackberry|blazer|elaine|hiptop|ip(hone|od)|kindle|midp|mmp|mobile|o2|opera mini|palm( os)?|pda|plucker|pocket|psp|smartphone|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce; (iemobile|ppc)|xiino|maemo|fennec'
+        _long_matches = re.compile(_long_matches, re.IGNORECASE)
+        _short_matches = r'1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-'
+        _short_matches = re.compile(_short_matches, re.IGNORECASE)
+
+        if _long_matches.search(userAgent) != None:
+            return True
+        user_agent = userAgent[0:4]
+        if _short_matches.search(user_agent) != None:
+            return True
+        return False
+
+    @staticmethod
+    def exp(exp, value):
+        if exp:
+            exp = exp.replace('{n}', value)
+            value = str(eval(exp))
+        return value
+
+    @classmethod
+    def curl(cls, url='', param=None, method='get', timeout=5, max=2, json_mode=True, headers=None):
+        import requests
+        from requests.adapters import HTTPAdapter
+
+        if param is None:
+            param = {}
+        if headers is None:
+            headers = {}
+        try:
+            s = requests.Session()
+            s.mount('http://', HTTPAdapter(max_retries=max))
+            s.mount('https://', HTTPAdapter(max_retries=max))
+            method = method.lower()
+            if method == 'get':
+                resp = s.get(url, params=param, headers=headers, timeout=timeout)
+            elif method == 'post':
+                if json_mode:
+                    resp = s.post(url, json=param, headers=headers, timeout=timeout)
+                else:
+                    resp = s.post(url, data=param, headers=headers, timeout=timeout)
+            else:
+                raise ValueError(f"Unsupported method: {method}")
+
+            resp.raise_for_status()
+            return resp.text
+
+        except requests.exceptions.RequestException as e:
+            return {'error': str(e)}
+
+    @classmethod
+    def out(self, msg='', data={}, code=0, callback='', function=''):
+        if data:
+            if 'page' in data and data['page']['total'] <= 0:
+                del data['page']
+            if 'update' in data and not data['update']:
+                del data['update']
+            if 'search' in data and not data['search']:
+                del data['search']
+        result = ''
+        send = {}
+        send['status'] = 1
+        send['msg'] = msg
+        send['data'] = data
+        send['code'] = code
+        if not send['data']:
+            send['status'] = 2
+        result = json.dumps(send)
+        if callback:
+            result = callback + '(' + result + ')'
+        elif function:
+            result = '<script>parent.' + function + '(' + result + ')' + '</script>';
+        return result
+
+    @classmethod
+    def error(self, string):
+        if self.request:
+            self.request.out(string)
+        else:
+            self.echo(string)
+            #os._exit(0)
+
+    @classmethod
+    def redis(self):
+        self.initConfig()
+        import redis
+        config = self.config['redis']
+        pool = redis.ConnectionPool(host=config['host'], password=config['password'], port=int(config['port']))
+        return redis.Redis(connection_pool=pool)
+
+    @classmethod
+    def sync(self, table, id):
+        table = table.replace('demeter_', '')
+        if 'sync' in Demeter.config:
+            config = Demeter.config['sync']['table'].split(',')
+            service = Demeter.config['sync']['service'].split(',')
+            if table in config and table not in self.syncData:
+                Demeter.service(service[0], service[1]).rsync(table, id)
+                #self.syncData[table] = True
 
 class Log(object):
-	@classmethod
-	def init(self, name, output = False):
-		import logging
-		from logging.handlers import RotatingFileHandler
-		formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
-
-		logger = logging.getLogger(name)
-		logger.setLevel(logging.DEBUG)
-
-		if output:
-			stream_handler = logging.StreamHandler(sys.stderr)
-			stream_handler.setFormatter(formatter)
-			logger.addHandler(stream_handler)
-
-		file_handler = RotatingFileHandler(self.file(name), maxBytes=1024*1024,backupCount=5)
-		file_handler.setLevel(level=logging.DEBUG)
-		file_handler.setFormatter(formatter)
-		logger.addHandler(file_handler)
-
-		return logger
-	@classmethod
-	def read(self, name, lines=200):
-		file = self.file(name)
-		if File.exists(file):
-			return File.tail(file, lines)
-		return ''
-	@classmethod
-	def file(self, name):
-		path = File.path() + 'runtime/log/'
-		File.mkdir(path)
-
-		return os.path.join(path, name + '.log')
+    @classmethod
+    def init(self, name, output = False):
+        import logging
+        from logging.handlers import RotatingFileHandler
+        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+
+        logger = logging.getLogger(name)
+        logger.setLevel(logging.DEBUG)
+
+        if output:
+            stream_handler = logging.StreamHandler(sys.stderr)
+            stream_handler.setFormatter(formatter)
+            logger.addHandler(stream_handler)
+
+        file_handler = RotatingFileHandler(self.file(name), maxBytes=1024*1024,backupCount=5)
+        file_handler.setLevel(level=logging.DEBUG)
+        file_handler.setFormatter(formatter)
+        logger.addHandler(file_handler)
+
+        return logger
+    @classmethod
+    def read(self, name, lines=200):
+        file = self.file(name)
+        if File.exists(file):
+            return File.tail(file, lines)
+        return ''
+    @classmethod
+    def file(self, name):
+        path = File.path() + 'runtime/log/'
+        File.mkdir(path)
+
+        return os.path.join(path, name + '.log')
 
 class WatchDog(object):
 
-	@staticmethod
-	def init(path = [], reloads = [], recursive = False):
-		event_handler = WatchDogHandle(reloads)
-		dog = Observer()
-		base = File.path()
-		if not path:
-			path = ['conf/',]
-		for item in path:
-			dog.schedule(event_handler, base + item, recursive=recursive)
-		dog.start()
+    @staticmethod
+    def init(path = [], reloads = [], recursive = False):
+        event_handler = WatchDogHandle(reloads)
+        dog = Observer()
+        base = File.path()
+        if not path:
+            path = ['conf/',]
+        for item in path:
+            dog.schedule(event_handler, base + item, recursive=recursive)
+        dog.start()
 
-		return dog
+        return dog
 
 class WatchDogHandle(FileSystemEventHandler):
 
-	@classmethod
-	def __init__(self, reloads = False):
-		FileSystemEventHandler.__init__(self)
-		self.reloads = reloads
-
-	@classmethod
-	def on_modified(self, event):
-		if not event.is_directory and '.' in event.src_path:
-			if self.reloads:
-				for item in self.reloads:
-					item.reload()
-			elif Demeter.web:
-				Demeter.webInit(Demeter.web)
-			else:
-				Demeter.echo('modify ' + event.src_path)
+    @classmethod
+    def __init__(self, reloads = False):
+        FileSystemEventHandler.__init__(self)
+        self.reloads = reloads
+
+    @classmethod
+    def on_modified(self, event):
+        if not event.is_directory and '.' in event.src_path:
+            if self.reloads:
+                for item in self.reloads:
+                    item.reload()
+            elif Demeter.web:
+                Demeter.webInit(Demeter.web)
+            else:
+                Demeter.echo('modify ' + event.src_path)
 
 class File(object):
 
-	@staticmethod
-	def write(file, content):
-		handle = open(file, 'w')
-		handle.write(content)
-		handle.close()
-		#Shell.popen('chmod +x ' + file)
-		os.chmod(file, 0o666)
-
-	@staticmethod
-	def read(path, name = ''):
-		handle = open(path + name, 'r')
-		content = handle.read()
-		handle.close()
-		return content
-		
-	@staticmethod
-	def readContent(path, name = ''):
-		content = ''
-		handle = open(path + name, 'r')
-		while True:
-			line = handle.readline()
-			if line:
-				line = line.rstrip("\n")
-				content = content + line
-			else:
-				break
-		handle.close()
-		return content
-
-	@staticmethod
-	def cur_path():
-		return os.path.split(os.path.realpath(__file__))[0] + '/'
-
-	@staticmethod
-	def getFiles(path):
-		return os.listdir(path)
-
-	@staticmethod
-	def path():
-		return os.sys.path[0] + '/'
-
-	@staticmethod
-	def exists(name):
-		return os.path.exists(name)
-
-	@staticmethod
-	def rename(old, new):
-		return os.rename(old, new)
-
-	@classmethod
-	def remove(self, file):
-		if isinstance(file, str) and self.exists(file):
-			if os.path.isfile(file):
-				return os.remove(file)
-			else:
-				# 删除非空目录
-				return shutil.rmtree(file)
-		return False
-
-	@staticmethod
-	def mkdir(path):
-		if File.exists(path) == False:
-			os.mkdir(path)
-		return path
-
-	@staticmethod
-	def mkdirs(path):
-		if File.exists(path) == False:
-			os.makedirs(path)
-		return path
-
-	@staticmethod
-	def ext(path):
-		return os.path.splitext(path)[1]
-
-	@classmethod
-	def runtime(self, path = 'data'):
-		return self.mkdir(self.path() + 'runtime/' + path + '/')
-
-	@classmethod
-	def dest(self, path, name, size = 2, total = 6):
-		data = [name[i:i+size] for i in range(0, total, size)]
-		for key, value in enumerate(data):
-			path = path + value + '/'
-		path = self.mkdirs(path)
-		return path + name
-
-	"""
-	实现 tail -n
-	"""
-	@classmethod
-	def tail(self, filepath, n=10):
-		PAGE = 4096
-		res = ""
-		with open(filepath, 'rb') as f:
-			f_len = f.seek(0, 2)
-			rem = f_len % PAGE
-			page_n = f_len // PAGE
-			r_len = rem if rem else PAGE
-			while True:
-				# 如果读取的页大小>=文件大小,直接读取数据输出
-				if r_len >= f_len:
-					f.seek(0)
-					lines = f.readlines()[::-1]
-					break
-
-				f.seek(-r_len, 2)
-				# print('f_len: {}, rem: {}, page_n: {}, r_len: {}'.format(f_len, rem, page_n, r_len))
-				lines = f.readlines()[::-1]
-				count = len(lines) -1   # 末行可能不完整,减一行,加大读取量
-
-				if count >= n:  # 如果读取到的行数>=指定行数,则退出循环读取数据
-					break
-				else:   # 如果读取行数不够,载入更多的页大小读取数据
-					r_len += PAGE
-					page_n -= 1
-
-		for line in lines[:n][::-1]:
-			res += line.decode('utf-8')
-		return res
+    @staticmethod
+    def write(file, content):
+        handle = open(file, 'w')
+        handle.write(content)
+        handle.close()
+        #Shell.popen('chmod +x ' + file)
+        os.chmod(file, 0o666)
+
+    @staticmethod
+    def read(path, name = ''):
+        handle = open(path + name, 'r')
+        content = handle.read()
+        handle.close()
+        return content
+        
+    @staticmethod
+    def readContent(path, name = ''):
+        content = ''
+        handle = open(path + name, 'r')
+        while True:
+            line = handle.readline()
+            if line:
+                line = line.rstrip("\n")
+                content = content + line
+            else:
+                break
+        handle.close()
+        return content
+
+    @staticmethod
+    def cur_path():
+        return os.path.split(os.path.realpath(__file__))[0] + '/'
+
+    @staticmethod
+    def getFiles(path):
+        return os.listdir(path)
+
+    @staticmethod
+    def path():
+        return os.sys.path[0] + '/'
+
+    @staticmethod
+    def exists(name):
+        return os.path.exists(name)
+
+    @staticmethod
+    def rename(old, new):
+        return os.rename(old, new)
+
+    @classmethod
+    def remove(self, file):
+        if isinstance(file, str) and self.exists(file):
+            if os.path.isfile(file):
+                return os.remove(file)
+            else:
+                # 删除非空目录
+                return shutil.rmtree(file)
+        return False
+
+    @staticmethod
+    def mkdir(path):
+        if File.exists(path) == False:
+            os.mkdir(path)
+        return path
+
+    @staticmethod
+    def mkdirs(path):
+        if File.exists(path) == False:
+            os.makedirs(path)
+        return path
+
+    @staticmethod
+    def ext(path):
+        return os.path.splitext(path)[1]
+
+    @classmethod
+    def runtime(self, path = 'data'):
+        return self.mkdir(self.path() + 'runtime/' + path + '/')
+
+    @classmethod
+    def dest(self, path, name, size = 2, total = 6):
+        data = [name[i:i+size] for i in range(0, total, size)]
+        for key, value in enumerate(data):
+            path = path + value + '/'
+        path = self.mkdirs(path)
+        return path + name
+
+    """
+    实现 tail -n
+    """
+    @classmethod
+    def tail(self, filepath, n=10):
+        PAGE = 4096
+        res = ""
+        with open(filepath, 'rb') as f:
+            f_len = f.seek(0, 2)
+            rem = f_len % PAGE
+            page_n = f_len // PAGE
+            r_len = rem if rem else PAGE
+            while True:
+                # 如果读取的页大小>=文件大小,直接读取数据输出
+                if r_len >= f_len:
+                    f.seek(0)
+                    lines = f.readlines()[::-1]
+                    break
+
+                f.seek(-r_len, 2)
+                # print('f_len: {}, rem: {}, page_n: {}, r_len: {}'.format(f_len, rem, page_n, r_len))
+                lines = f.readlines()[::-1]
+                count = len(lines) -1   # 末行可能不完整,减一行,加大读取量
+
+                if count >= n:  # 如果读取到的行数>=指定行数,则退出循环读取数据
+                    break
+                else:   # 如果读取行数不够,载入更多的页大小读取数据
+                    r_len += PAGE
+                    page_n -= 1
+
+        for line in lines[:n][::-1]:
+            res += line.decode('utf-8')
+        return res
 
 class Shell(object):
-	@staticmethod
-	def popen(command, sub=False, bg=False, timeout=0):
-		string = command
-		if bg == True:
-			command = command + ' 1>/dev/null 2>&1 &'
-
-		if timeout > 0:
-			proc = subprocess.Popen(command,bufsize=0,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True, close_fds=True, preexec_fn = os.setsid)
-			poll_seconds = .250
-			deadline = time.time() + timeout
-			while time.time() < deadline and proc.poll() == None:
-				time.sleep(poll_seconds)
-			if proc.poll() == None:
-				os.killpg(proc.pid, signal.SIGTERM)
-				return 'timeout'
-
-			stdout,stderr = proc.communicate()
-			return stdout
-		elif sub == False:
-			process = os.popen(command)
-			output = process.read()
-			process.close()
-			return output
-		else:
-			popen  = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
-			output = ''
-			Demeter.echo(string)
-			while True:
-				output = popen.stdout.readline()
-				Demeter.echo(output)
-				if popen.poll() is not None:
-					break
-			return output
+    @staticmethod
+    def popen(command, sub=False, bg=False, timeout=0):
+        string = command
+        if bg == True:
+            command = command + ' 1>/dev/null 2>&1 &'
+
+        if timeout > 0:
+            proc = subprocess.Popen(command,bufsize=0,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True, close_fds=True, preexec_fn = os.setsid)
+            poll_seconds = .250
+            deadline = time.time() + timeout
+            while time.time() < deadline and proc.poll() == None:
+                time.sleep(poll_seconds)
+            if proc.poll() == None:
+                os.killpg(proc.pid, signal.SIGTERM)
+                return 'timeout'
+
+            stdout,stderr = proc.communicate()
+            return stdout
+        elif sub == False:
+            process = os.popen(command)
+            output = process.read()
+            process.close()
+            return output
+        else:
+            popen  = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
+            output = ''
+            Demeter.echo(string)
+            while True:
+                output = popen.stdout.readline()
+                Demeter.echo(output)
+                if popen.poll() is not None:
+                    break
+            return output
 
 class Check(object):
-	@staticmethod
-	def match(rule, value):
-		if not rule.match(value):
-			return False
-		return True
-
-	@staticmethod
-	def mobile(value):
-		rule = re.compile(r'1\d{10}')
-		return Check.match(rule, value)
-
-	@staticmethod
-	def number(value):
-		try:
-			int(value)
-			return True
-		except ValueError:
-			return False
-
-	@staticmethod
-	def numberFloat(value):
-		try:
-			float(value)
-			return True
-		except ValueError:
-			return False
+    @staticmethod
+    def match(rule, value):
+        if not rule.match(value):
+            return False
+        return True
+
+    @staticmethod
+    def mobile(value):
+        rule = re.compile(r'1\d{10}')
+        return Check.match(rule, value)
+
+    @staticmethod
+    def number(value):
+        try:
+            int(value)
+            return True
+        except ValueError:
+            return False
+
+    @staticmethod
+    def numberFloat(value):
+        try:
+            float(value)
+            return True
+        except ValueError:
+            return False

+ 80 - 80
demeter/daemon.py

@@ -7,85 +7,85 @@ import signal
   
   
 class Daemon(object):
-  def __init__(self, pidfile='/tmp/daemon.pid', stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
-    self.stdin = stdin
-    self.stdout = stdout
-    self.stderr = stderr
-    self.pidfile = pidfile
-  
-  def daemonize(self):
-    if os.path.exists(self.pidfile):
-      raise RuntimeError('Already running.')
-  
-    # First fork (detaches from parent)
-    try:
-      if os.fork() > 0:
-        raise SystemExit(0)
-    except OSError as e:
-      raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror))
-  
-    os.chdir('/')
-    os.setsid()
-    os.umask(0o22)
-  
-    # Second fork (relinquish session leadership)
-    try:
-      if os.fork() > 0:
-        raise SystemExit(0)
-    except OSError as e:
-      raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror))
-  
-    # Flush I/O buffers
-    sys.stdout.flush()
-    sys.stderr.flush()
-  
-    # Replace file descriptors for stdin, stdout, and stderr
-    with open(self.stdin, 'rb', 0) as f:
-      os.dup2(f.fileno(), sys.stdin.fileno())
-    with open(self.stdout, 'ab', 0) as f:
-      os.dup2(f.fileno(), sys.stdout.fileno())
-    with open(self.stderr, 'ab', 0) as f:
-      os.dup2(f.fileno(), sys.stderr.fileno())
-  
-    # Write the PID file
-    with open(self.pidfile, 'w') as f:
-      print(os.getpid(), file=f)
-  
-    # Arrange to have the PID file removed on exit/signal
-    atexit.register(lambda: os.remove(self.pidfile))
-  
-    signal.signal(signal.SIGTERM, self.__sigterm_handler)
-  
-  # Signal handler for termination (required)
-  @staticmethod
-  def __sigterm_handler(signo, frame):
-    raise SystemExit(1)
-  
-  def start(self):
-    try:
-      self.daemonize()
-    except RuntimeError as e:
-      print(e, file=sys.stderr)
-      raise SystemExit(1)
-  
-    self.run()
-  
-  def stop(self):
-    try:
+    def __init__(self, pidfile='/tmp/daemon.pid', stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
+      self.stdin = stdin
+      self.stdout = stdout
+      self.stderr = stderr
+      self.pidfile = pidfile
+    
+    def daemonize(self):
       if os.path.exists(self.pidfile):
-        with open(self.pidfile) as f:
-          os.kill(int(f.read()), signal.SIGTERM)
-      else:
-        print('Not running.', file=sys.stderr)
+        raise RuntimeError('Already running.')
+    
+      # First fork (detaches from parent)
+      try:
+        if os.fork() > 0:
+          raise SystemExit(0)
+      except OSError as e:
+        raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror))
+    
+      os.chdir('/')
+      os.setsid()
+      os.umask(0o22)
+    
+      # Second fork (relinquish session leadership)
+      try:
+        if os.fork() > 0:
+          raise SystemExit(0)
+      except OSError as e:
+        raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror))
+    
+      # Flush I/O buffers
+      sys.stdout.flush()
+      sys.stderr.flush()
+    
+      # Replace file descriptors for stdin, stdout, and stderr
+      with open(self.stdin, 'rb', 0) as f:
+        os.dup2(f.fileno(), sys.stdin.fileno())
+      with open(self.stdout, 'ab', 0) as f:
+        os.dup2(f.fileno(), sys.stdout.fileno())
+      with open(self.stderr, 'ab', 0) as f:
+        os.dup2(f.fileno(), sys.stderr.fileno())
+    
+      # Write the PID file
+      with open(self.pidfile, 'w') as f:
+        print(os.getpid(), file=f)
+    
+      # Arrange to have the PID file removed on exit/signal
+      atexit.register(lambda: os.remove(self.pidfile))
+    
+      signal.signal(signal.SIGTERM, self.__sigterm_handler)
+    
+    # Signal handler for termination (required)
+    @staticmethod
+    def __sigterm_handler(signo, frame):
+      raise SystemExit(1)
+    
+    def start(self):
+      try:
+        self.daemonize()
+      except RuntimeError as e:
+        print(e, file=sys.stderr)
         raise SystemExit(1)
-    except OSError as e:
-      if 'No such process' in str(e) and os.path.exists(self.pidfile):
-        os.remove(self.pidfile)
-  
-  def restart(self):
-    self.stop()
-    self.start()
-  
-  def run(self):
-  #继承类重写该方法
-    pass
+    
+      self.run()
+    
+    def stop(self):
+      try:
+        if os.path.exists(self.pidfile):
+          with open(self.pidfile) as f:
+            os.kill(int(f.read()), signal.SIGTERM)
+        else:
+          print('Not running.', file=sys.stderr)
+          raise SystemExit(1)
+      except OSError as e:
+        if 'No such process' in str(e) and os.path.exists(self.pidfile):
+          os.remove(self.pidfile)
+    
+    def restart(self):
+      self.stop()
+      self.start()
+    
+    def run(self):
+    #继承类重写该方法
+      pass

+ 72 - 72
demeter/db.py

@@ -6,92 +6,92 @@
 """
 from demeter.core import *
 class Influxdb(object):
-	"""
-	instance = None
-	def __new__(cls, *args, **kwd):
-		if Influxdb.instance is None:
-			Influxdb.instance = object.__new__(cls, *args, **kwd)
-		return Influxdb.instance
-	"""
+    """
+    instance = None
+    def __new__(cls, *args, **kwd):
+        if Influxdb.instance is None:
+            Influxdb.instance = object.__new__(cls, *args, **kwd)
+        return Influxdb.instance
+    """
 
-	def __init__(self, config):
-		influxdb = __import__('influxdb')
-		InfluxDBClient = getattr(influxdb, 'InfluxDBClient')
-		self.connect = InfluxDBClient(config['host'], config['port'], config['username'], config['password'], config['dbname'])
-		self.create(config['dbname'])
+    def __init__(self, config):
+        influxdb = __import__('influxdb')
+        InfluxDBClient = getattr(influxdb, 'InfluxDBClient')
+        self.connect = InfluxDBClient(config['host'], config['port'], config['username'], config['password'], config['dbname'])
+        self.create(config['dbname'])
 
-	def get(self):
-		return self.connect
+    def get(self):
+        return self.connect
 
-	def create(self, name):
-		database = self.connect.get_list_database()
-		self.connect.create_database(name)
+    def create(self, name):
+        database = self.connect.get_list_database()
+        self.connect.create_database(name)
 
 
 class Postgresql(object):
-	"""
-	instance = None
-	def __new__(cls, *args, **kwd):
-		if Postgresql.instance is None:
-			Postgresql.instance = object.__new__(cls, *args, **kwd)
-		return Postgresql.instance
-	"""
-	def __init__(self, config):
-		psycopg2 = __import__('psycopg2')
-		self.create(config['dbname'])
-		self.connect = psycopg2.connect(host=config['host'], port=config['port'], user=config['username'], password=config['password'], database=config['dbname'])
+    """
+    instance = None
+    def __new__(cls, *args, **kwd):
+        if Postgresql.instance is None:
+            Postgresql.instance = object.__new__(cls, *args, **kwd)
+        return Postgresql.instance
+    """
+    def __init__(self, config):
+        psycopg2 = __import__('psycopg2')
+        self.create(config['dbname'])
+        self.connect = psycopg2.connect(host=config['host'], port=config['port'], user=config['username'], password=config['password'], database=config['dbname'])
 
-	def get(self):
-		return self.connect
+    def get(self):
+        return self.connect
 
-	def create(self, name):
-		'psql -U postgres'
-		sql = 'CREATE DATABASE '+name+' WITH OWNER = postgres ENCODING = "UTF8"'
+    def create(self, name):
+        'psql -U postgres'
+        sql = 'CREATE DATABASE '+name+' WITH OWNER = postgres ENCODING = "UTF8"'
 
-		if Demeter.runtime('postgresql', name, sql):
-			Shell.popen('createdb -h localhost -p 5432 -U postgres ' + name)
-		return True
+        if Demeter.runtime('postgresql', name, sql):
+            Shell.popen('createdb -h localhost -p 5432 -U postgres ' + name)
+        return True
 
 class Mysql(object):
-	"""
-	instance = None
-	def __new__(cls, *args, **kwd):
-		if Mysql.instance is None:
-			Mysql.instance = object.__new__(cls, *args, **kwd)
-		return Mysql.instance
-	"""
-	def __init__(self, config):
-		pymysql = __import__('pymysql')
-		self.connect = pymysql.connect(host=config['host'], port=int(config['port']), user=config['username'], password=config['password'], database=config['dbname'], charset=config['charset'])
+    """
+    instance = None
+    def __new__(cls, *args, **kwd):
+        if Mysql.instance is None:
+            Mysql.instance = object.__new__(cls, *args, **kwd)
+        return Mysql.instance
+    """
+    def __init__(self, config):
+        pymysql = __import__('pymysql')
+        self.connect = pymysql.connect(host=config['host'], port=int(config['port']), user=config['username'], password=config['password'], database=config['dbname'], charset=config['charset'])
 
-	def get(self):
-		return self.connect
+    def get(self):
+        return self.connect
 
-	def create(self, name):
-		sql = 'CREATE DATABASE IF NOT EXISTS '+name+' DEFAULT CHARSET utf8 COLLATE utf8_general_ci'
-		if not Demeter.runtime('mysql', name, sql):
-			self.connect.cursor().execute(sql)
-		return sql
+    def create(self, name):
+        sql = 'CREATE DATABASE IF NOT EXISTS '+name+' DEFAULT CHARSET utf8 COLLATE utf8_general_ci'
+        if not Demeter.runtime('mysql', name, sql):
+            self.connect.cursor().execute(sql)
+        return sql
 
 class Sqlite(object):
-	"""
-	instance = None
-	def __new__(cls, *args, **kwd):
-		if Sqlite.instance is None:
-			Sqlite.instance = object.__new__(cls, *args, **kwd)
-		return Sqlite.instance
-	"""
-	def __init__(self, config):
-		sqlite3 = __import__('sqlite3')
-		self.connect = sqlite3.connect(config['file'])
-		# 插入中文问题
-		self.connect.text_factory = str
+    """
+    instance = None
+    def __new__(cls, *args, **kwd):
+        if Sqlite.instance is None:
+            Sqlite.instance = object.__new__(cls, *args, **kwd)
+        return Sqlite.instance
+    """
+    def __init__(self, config):
+        sqlite3 = __import__('sqlite3')
+        self.connect = sqlite3.connect(config['file'])
+        # 插入中文问题
+        self.connect.text_factory = str
 
-	def get(self):
-		return self.connect
+    def get(self):
+        return self.connect
 
-	def create(self, name):
-		sql = 'CREATE DATABASE IF NOT EXISTS '+name+' DEFAULT CHARSET utf8 COLLATE utf8_general_ci'
-		if not Demeter.runtime('sqlite', name, sql):
-			self.connect.cursor().execute(sql)
-		return sql
+    def create(self, name):
+        sql = 'CREATE DATABASE IF NOT EXISTS '+name+' DEFAULT CHARSET utf8 COLLATE utf8_general_ci'
+        if not Demeter.runtime('sqlite', name, sql):
+            self.connect.cursor().execute(sql)
+        return sql

+ 74 - 74
demeter/modbus.py

@@ -11,91 +11,91 @@ import paho.mqtt.client as mqtt
 
 class Connect(object):
 
-	def __init__(self, act):
-		act.connect = self
-		self.client = mqtt.Client()
-		state = hasattr(act, 'message')
-		if state:
-			self.client.on_connect = self.connectAndSub
-			self.client.on_message = act.message
-		else:
-			self.client.on_connect = self.connect
-		self.client.connect(Demeter.config['mqtt']['host'], Demeter.config['mqtt']['port'], int(Demeter.config['mqtt']['timeout']))
-		if state:
-			self.client.loop_forever()
-
-	def __del__(self):
-		pass
-
-	def getClient(self):
-		return self.client
-
-	def connect(self, client, userdata, flags, rc):
-		pass
-
-	def connectAndSub(self, client, userdata, flags, rc):
-		#print("Connected with result code "+str(rc))
-		#client.subscribe("sensor/#")
-		sub = Demeter.config['mqtt']['sub'].split(',')
-		for value in sub:
-			client.subscribe(value + "/#")
-		"""
-		gevent.joinall([
-			gevent.spawn(self.subscribe, client, 'sensor/#'),
-			gevent.spawn(self.subscribe, client, 'pic/#'),
-			gevent.spawn(self.subscribe, client, 'msg/#'),
-		])
-		"""
-
-	@staticmethod
-	def subscribe(client, key):
-		client.subscribe(key)
-
-	def handle(self, key, value):
-		Demeter.record(key, value)
+    def __init__(self, act):
+        act.connect = self
+        self.client = mqtt.Client()
+        state = hasattr(act, 'message')
+        if state:
+            self.client.on_connect = self.connectAndSub
+            self.client.on_message = act.message
+        else:
+            self.client.on_connect = self.connect
+        self.client.connect(Demeter.config['mqtt']['host'], Demeter.config['mqtt']['port'], int(Demeter.config['mqtt']['timeout']))
+        if state:
+            self.client.loop_forever()
+
+    def __del__(self):
+        pass
+
+    def getClient(self):
+        return self.client
+
+    def connect(self, client, userdata, flags, rc):
+        pass
+
+    def connectAndSub(self, client, userdata, flags, rc):
+        #print("Connected with result code "+str(rc))
+        #client.subscribe("sensor/#")
+        sub = Demeter.config['mqtt']['sub'].split(',')
+        for value in sub:
+            client.subscribe(value + "/#")
+        """
+        gevent.joinall([
+            gevent.spawn(self.subscribe, client, 'sensor/#'),
+            gevent.spawn(self.subscribe, client, 'pic/#'),
+            gevent.spawn(self.subscribe, client, 'msg/#'),
+        ])
+        """
+
+    @staticmethod
+    def subscribe(client, key):
+        client.subscribe(key)
+
+    def handle(self, key, value):
+        Demeter.record(key, value)
 
 
 class Pub(object):
 
-	def __init__(self):
-		Connect(self)
+    def __init__(self):
+        Connect(self)
 
-	def __del__(self):
-		pass
+    def __del__(self):
+        pass
 
-	def push(self, key, msg, qos=0, retain=False, callback=False, param=False, feedback=False):
-		result = self.connect.getClient().publish(key,payload=msg,qos=qos,retain=retain)
+    def push(self, key, msg, qos=0, retain=False, callback=False, param=False, feedback=False):
+        result = self.connect.getClient().publish(key,payload=msg,qos=qos,retain=retain)
 
-		self.callback = callback
-		self.param = param
-		if feedback == True and 'key' in self.param:
-			self.connect.client.on_message = self.feedback
-			self.connect.client.subscribe(self.param['key'])
-			self.connect.client.loop_forever()
-		elif qos in (1,2):
-			self.connect.client.on_publish = self.publish
-			self.connect.client.loop_forever()
-		#else:
-			#self.connect.client.disconnect()
-		return result
+        self.callback = callback
+        self.param = param
+        if feedback == True and 'key' in self.param:
+            self.connect.client.on_message = self.feedback
+            self.connect.client.subscribe(self.param['key'])
+            self.connect.client.loop_forever()
+        elif qos in (1,2):
+            self.connect.client.on_publish = self.publish
+            self.connect.client.loop_forever()
+        #else:
+            #self.connect.client.disconnect()
+        return result
 
-	def publish(self, client, userdata, mid, msg='ok'):
-		self.callback(self.param, client, userdata, mid, msg)
-		self.connect.client.disconnect()
+    def publish(self, client, userdata, mid, msg='ok'):
+        self.callback(self.param, client, userdata, mid, msg)
+        self.connect.client.disconnect()
 
-	def feedback(self, client, userdata, msg):
-		if msg.topic == self.param['key']:
-			self.publish(client, userdata, 0, msg.payload)
+    def feedback(self, client, userdata, msg):
+        if msg.topic == self.param['key']:
+            self.publish(client, userdata, 0, msg.payload)
 
 class Sub(object):
 
-	def __init__(self):
-		Connect(self)
+    def __init__(self):
+        Connect(self)
 
-	def __del__(self):
-		pass
+    def __del__(self):
+        pass
 
-	def message(self, client, userdata, msg):
-		#print(msg.topic+" "+str(msg.payload))
-		#return
-		self.connect.handle(msg.topic, str(msg.payload))
+    def message(self, client, userdata, msg):
+        #print(msg.topic+" "+str(msg.payload))
+        #return
+        self.connect.handle(msg.topic, str(msg.payload))

+ 77 - 77
demeter/mqtt.py

@@ -11,94 +11,94 @@ import paho.mqtt.client as mqtt
 
 class Connect(object):
 
-	def __init__(self, act, topic=False):
-		act.connect = self
-		self.client = mqtt.Client()
-		self.topic = topic
-		state = hasattr(act, 'message')
-		if state:
-			self.client.on_connect = self.connectAndSub
-			self.client.on_message = act.message
-		else:
-			self.client.on_connect = self.connect
-		self.client.connect(Demeter.config['mqtt']['host'], int(Demeter.config['mqtt']['port']), int(Demeter.config['mqtt']['timeout']))
-		if state:
-			self.client.loop_forever()
-
-	def __del__(self):
-		pass
-
-	def getClient(self):
-		return self.client
-
-	def connect(self, client, userdata, flags, rc):
-		pass
-
-	def connectAndSub(self, client, userdata, flags, rc):
-		#print("Connected with result code "+str(rc))
-		#client.subscribe("sensor/#")
-		#sub = Demeter.config['mqtt']['sub'].split(',')
-		sub = self.topic.split(',')
-		for value in sub:
-			client.subscribe(value + "/#")
-		"""
-		gevent.joinall([
-			gevent.spawn(self.subscribe, client, 'sensor/#'),
-			gevent.spawn(self.subscribe, client, 'pic/#'),
-			gevent.spawn(self.subscribe, client, 'msg/#'),
-		])
-		"""
-
-	@staticmethod
-	def subscribe(client, key):
-		client.subscribe(key)
-
-	def handle(self, key, value):
-		service = Demeter.service('record', 'mqtt')
-		service.push(key, value)
+    def __init__(self, act, topic=False):
+        act.connect = self
+        self.client = mqtt.Client()
+        self.topic = topic
+        state = hasattr(act, 'message')
+        if state:
+            self.client.on_connect = self.connectAndSub
+            self.client.on_message = act.message
+        else:
+            self.client.on_connect = self.connect
+        self.client.connect(Demeter.config['mqtt']['host'], int(Demeter.config['mqtt']['port']), int(Demeter.config['mqtt']['timeout']))
+        if state:
+            self.client.loop_forever()
+
+    def __del__(self):
+        pass
+
+    def getClient(self):
+        return self.client
+
+    def connect(self, client, userdata, flags, rc):
+        pass
+
+    def connectAndSub(self, client, userdata, flags, rc):
+        #print("Connected with result code "+str(rc))
+        #client.subscribe("sensor/#")
+        #sub = Demeter.config['mqtt']['sub'].split(',')
+        sub = self.topic.split(',')
+        for value in sub:
+            client.subscribe(value + "/#")
+        """
+        gevent.joinall([
+            gevent.spawn(self.subscribe, client, 'sensor/#'),
+            gevent.spawn(self.subscribe, client, 'pic/#'),
+            gevent.spawn(self.subscribe, client, 'msg/#'),
+        ])
+        """
+
+    @staticmethod
+    def subscribe(client, key):
+        client.subscribe(key)
+
+    def handle(self, key, value):
+        service = Demeter.service('record', 'mqtt')
+        service.push(key, value)
 
 
 class Pub(object):
 
-	def __init__(self):
-		Connect(self)
+    def __init__(self):
+        Connect(self)
 
-	def __del__(self):
-		pass
+    def __del__(self):
+        pass
 
-	def push(self, key, msg, qos=0, retain=False, callback=False, param=False, feedback=False):
-		result = self.connect.getClient().publish(key,payload=msg,qos=qos,retain=retain)
+    def push(self, key, msg, qos=0, retain=False, callback=False, param=False, feedback=False):
+        result = self.connect.getClient().publish(key,payload=msg,qos=qos,retain=retain)
 
-		self.callback = callback
-		self.param = param
-		if feedback == True and 'key' in self.param:
-			self.connect.client.on_message = self.feedback
-			self.connect.client.subscribe(self.param['key'])
-			self.connect.client.loop_forever()
-		elif qos in (1,2):
-			self.connect.client.on_publish = self.publish
-			self.connect.client.loop_forever()
-		#else:
-			#self.connect.client.disconnect()
-		return result
+        self.callback = callback
+        self.param = param
+        if feedback == True and 'key' in self.param:
+            self.connect.client.on_message = self.feedback
+            self.connect.client.subscribe(self.param['key'])
+            self.connect.client.loop_forever()
+        elif qos in (1,2):
+            self.connect.client.on_publish = self.publish
+            self.connect.client.loop_forever()
+        #else:
+            #self.connect.client.disconnect()
+        return result
 
-	def publish(self, client, userdata, mid, msg='ok'):
-		self.callback(self.param, client, userdata, mid, msg)
-		self.connect.client.disconnect()
+    def publish(self, client, userdata, mid, msg='ok'):
+        self.callback(self.param, client, userdata, mid, msg)
+        self.connect.client.disconnect()
 
-	def feedback(self, client, userdata, msg):
-		if msg.topic == self.param['key']:
-			self.publish(client, userdata, 0, msg.payload)
+    def feedback(self, client, userdata, msg):
+        if msg.topic == self.param['key']:
+            self.publish(client, userdata, 0, msg.payload)
 
 class Sub(object):
 
-	def __init__(self, topic=False):
-		Connect(self, topic=topic)
+    def __init__(self, topic=False):
+        Connect(self, topic=topic)
 
-	def __del__(self):
-		pass
+    def __del__(self):
+        pass
 
-	def message(self, client, userdata, msg):
-		#print(msg.topic+" "+str(msg.payload))
-		#return
-		self.connect.handle(msg.topic, str(msg.payload))
+    def message(self, client, userdata, msg):
+        #print(msg.topic+" "+str(msg.payload))
+        #return
+        self.connect.handle(msg.topic, str(msg.payload))

+ 60 - 60
demeter/service.py

@@ -8,69 +8,69 @@ from demeter.core import *
 
 class Service(object):
 
-	def list(self, model, state = True, search=None, page=False, order='cdate desc', limit = '0,100'):
-		model = self.model(model)
-		if state != -1:
-			model.state = state
-		if search:
-			for key, value in search.items():
-				if value or value == 0:
-					if '-' in key:
-						key = key.split('-')
-						keyLen = len(key)
-						if keyLen > 2 and key[2]:
-							method = key[2]
-						else:
-							method = 'assign'
-						self.assign(model, key[0], value, method)
-					else:
-						self.assign(model, key, value)
-		data = model.select(page=page, order=order, limit=limit)
-		return data
+    def list(self, model, state = True, search=None, page=False, order='cdate desc', limit = '0,100'):
+        model = self.model(model)
+        if state != -1:
+            model.state = state
+        if search:
+            for key, value in search.items():
+                if value or value == 0:
+                    if '-' in key:
+                        key = key.split('-')
+                        keyLen = len(key)
+                        if keyLen > 2 and key[2]:
+                            method = key[2]
+                        else:
+                            method = 'assign'
+                        self.assign(model, key[0], value, method)
+                    else:
+                        self.assign(model, key, value)
+        data = model.select(page=page, order=order, limit=limit)
+        return data
 
-	def one(self, model, **kwd):
-		model = self.model(model)
-		if kwd:
-			for key,value in kwd.items():
-				self.assign(model, key, value)
-		data = model.select(type='fetchone')
-		return data
+    def one(self, model, **kwd):
+        model = self.model(model)
+        if kwd:
+            for key,value in kwd.items():
+                self.assign(model, key, value)
+        data = model.select(type='fetchone')
+        return data
 
-	def update(self, model, id, data, cdate=True):
-		model = self.model(model)
-		if id:
-			model.id = id
-			if cdate == True and 'cdate' not in data:
-				data['cdate'] = 'time'
-			model.update(data)
-		else:
-			for key, value in data.items():
-				method = 'assign'
-				if 'date' in key:
-					method = 'time'
-				self.assign(model, key, value, method)
-			id = model.insert()
-		#Demeter.sync(model, id)
-		return id
+    def update(self, model, id, data, cdate=True):
+        model = self.model(model)
+        if id:
+            model.id = id
+            if cdate == True and 'cdate' not in data:
+                data['cdate'] = 'time'
+            model.update(data)
+        else:
+            for key, value in data.items():
+                method = 'assign'
+                if 'date' in key:
+                    method = 'time'
+                self.assign(model, key, value, method)
+            id = model.insert()
+        #Demeter.sync(model, id)
+        return id
 
-	def delete(self, model, id, state = False):
-		model = self.model(model)
-		model.id = id
-		state = model.update(state=state)
-		#Demeter.sync(model, id)
-		return state
+    def delete(self, model, id, state = False):
+        model = self.model(model)
+        model.id = id
+        state = model.update(state=state)
+        #Demeter.sync(model, id)
+        return state
 
-	def rDelete(self, model, id):
-		model = self.model(model)
-		model.id = id
-		return model.delete()
+    def rDelete(self, model, id):
+        model = self.model(model)
+        model.id = id
+        return model.delete()
 
-	def model(self, model):
-		return Demeter.model(model)
+    def model(self, model):
+        return Demeter.model(model)
 
-	def assign(self, model, key, value, method='assign'):
-		if hasattr(model, key):
-			attr = getattr(model, key)
-			if hasattr(attr, method):
-				call = getattr(attr, method)
-				call(value)
+    def assign(self, model, key, value, method='assign'):
+        if hasattr(model, key):
+            attr = getattr(model, key)
+            if hasattr(attr, method):
+                call = getattr(attr, method)
+                call(value)

+ 54 - 54
demeter/tcp.py

@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
 """
-	demeter
-	name:tcp.py
-	author:rabin
+    demeter
+    name:tcp.py
+    author:rabin
 """
 import socket  
 import time
@@ -14,56 +14,56 @@ from tornado import stack_context
 from tornado.escape import native_str 
 
 class Connection(object):
-	clients = set()
-	EOF = '|e|'
-	def __init__(self, stream, address):
-		Connection.clients.add(self)
-		self._pub = Pub()
-		self._stream = stream
-		self._address = address
-		self._stream.set_close_callback(self.on_close)
-		self.read_message()
-		
-	def read_message(self):
-		self._message_callback = stack_context.wrap(self.on_message)  
-		self._stream.read_until(self.EOF, self._message_callback)
-	
-	def on_message(self, data):
-		data = data.replace(self.EOF, '')
-		temp = data.split('|:|')
-		key = temp[0]
-		value = temp[1]
-		self._pub.push(key, value)
-		
-		#print "User said:", data[:-1], self._address
-		"""
-		for conn in Connection.clients:
-			conn.send_message(data)
-		"""
-		self.read_message()
-		
-	def send_message(self, data):
-		self._stream.write(data)
-			
-	def on_close(self):
-		#print "A user has left the chat room.", self._address
-		Connection.clients.remove(self)
-	
-class Server(TCPServer):	
-	def handle_stream(self, stream, address):
-		#print "New connection :", address, stream
-		Connection(stream, address)
-		#print "connection num is:", len(Connection.clients)
+    clients = set()
+    EOF = '|e|'
+    def __init__(self, stream, address):
+        Connection.clients.add(self)
+        self._pub = Pub()
+        self._stream = stream
+        self._address = address
+        self._stream.set_close_callback(self.on_close)
+        self.read_message()
+        
+    def read_message(self):
+        self._message_callback = stack_context.wrap(self.on_message)  
+        self._stream.read_until(self.EOF, self._message_callback)
+    
+    def on_message(self, data):
+        data = data.replace(self.EOF, '')
+        temp = data.split('|:|')
+        key = temp[0]
+        value = temp[1]
+        self._pub.push(key, value)
+        
+        #print "User said:", data[:-1], self._address
+        """
+        for conn in Connection.clients:
+            conn.send_message(data)
+        """
+        self.read_message()
+        
+    def send_message(self, data):
+        self._stream.write(data)
+            
+    def on_close(self):
+        #print "A user has left the chat room.", self._address
+        Connection.clients.remove(self)
+    
+class Server(TCPServer):    
+    def handle_stream(self, stream, address):
+        #print "New connection :", address, stream
+        Connection(stream, address)
+        #print "connection num is:", len(Connection.clients)
 
 class Client(object):
-	EOF = '|e|'
-	def __init__(self, host='0.0.0.0', port=8000):
-		self.connect = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-		self.connect.connect((host, port))
-		
-	def send(self, msg):
-		msg = msg + self.EOF
-		self.connect.sendall(msg)
-		#data = self.connect.recv(1024)
-	def close(self):
-		self.connect.close()
+    EOF = '|e|'
+    def __init__(self, host='0.0.0.0', port=8000):
+        self.connect = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.connect.connect((host, port))
+        
+    def send(self, msg):
+        msg = msg + self.EOF
+        self.connect.sendall(msg)
+        #data = self.connect.recv(1024)
+    def close(self):
+        self.connect.close()

+ 328 - 310
demeter/web.py

@@ -1,8 +1,9 @@
 # -*- coding: utf-8 -*-
 """
-	demeter
-	name:web.py
-	author:rabin
+    demeter
+    name:web.py
+    author:rabin
+    好多年前写的,以后换成fastapi吧
 """
 #from gevent import monkey
 #monkey.patch_all()
@@ -11,321 +12,338 @@ import functools
 import os
 import json
 import threading
+import signal
 from demeter.core import *
 import tornado.web
 import tornado.ioloop
 import tornado.httpserver
 import tornado.httpclient 
 import tornado.concurrent
+import asyncio
 
 class Base(tornado.web.RequestHandler):
 
-	def set_default_headers(self):
-		self.set_header("Access-Control-Allow-Origin", "*")
-		self.set_header("Access-Control-Allow-Headers", "x-requested-with")
-		self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
-
-	def initialize(self):
-		try:
-			Demeter.request = self
-			self.assign()
-			self.page()
-			self.cookie()
-			self.setting()
-		except Exception as e:
-			return
-
-	def get_current_user(self):
-		return self.get_secure_cookie(self.KEYS[0])
-
-	def assign(self):
-		self.data = {}
-		self.data['setting'] = Demeter.config['setting']
-		self.data['base'] = Demeter.config['base']
-		
-	def cookie(self):
-		for key in self.KEYS:
-			cookie = None
-			"""
-			if key in self.data['base']:
-				cookie = self.data['base'][key]
-			"""
-			if not cookie:
-				cookie = self.get_secure_cookie(key)
-				#cookie = self.get_cookie(key)
-			if not cookie:
-				value = self.input(key)
-				if value:
-					#self.set_secure_cookie(key, value)
-					self.data['setting'][key] = value
-				else:
-					self.data['setting'][key] = 0
-			else:
-				self.data['setting'][key] = cookie
-
-	def page(self):
-		Demeter.config['page'] = {}
-		page = self.input('page')
-		if page:
-			Demeter.config['page']['ajax'] = True
-		else:
-			Demeter.config['page']['ajax'] = False
-			page = 1
-		Demeter.config['page']['current'] = page
-		Demeter.config['page']['total'] = 0
-		self.data['page'] = Demeter.config['page']
-
-	def search(self):
-		data = self.request.arguments
-		self.data['search'] = {}
-		self.data['update'] = {}
-		for key in data:
-			if 'search_' in key:
-				index = key.replace('search_', '')
-				i = 0
-				for a in data[key]:
-					data[key][i] = a.decode()
-				self.data['search'][index] = ",".join(data[key])
-			if 'update_' in key:
-				index = key.replace('update_', '')
-				i = 0
-				for a in data[key]:
-					data[key][i] = a.decode()
-					i = i + 1
-				self.data['update'][index] = ",".join(data[key])
-
-	def input(self, key, value=None):
-		return self.get_argument(key, value)
-
-	def inputs(self, key):
-		return self.get_arguments(key)
-
-	def service(self, name):
-		return Demeter.service(name)
-
-	def model(self, name):
-		return Demeter.model(name)
-
-	def set(self, **kwd):
-		self.data['common'] = kwd
-		self.data['common']['argvs'] = ''
-
-	def show(self, name):
-		self.view('common/'+name+'.html')
-
-	def list(self, model, order = 'cdate desc'):
-		self.data['state'] = self.input('state', True)
-		self.data['list'] = self.service('common').list(model, state=self.data['state'], search=self.data['search'], page=True, order=order)
-
-	def one(self, model, **kwd):
-		self.data['info'] = {}
-		if 'id' not in kwd and self.input('id'):
-			id = self.input('id')
-			if id:
-				kwd['id'] = id
-		if kwd:
-			self.data['info'] = self.service('common').one(model, **kwd)
-
-	def update(self, model, msg='', id=0, **kwd):
-		if not self.data['auth']:
-			self.auth()
-		else:
-			if id <= 0:
-				id = self.input('id')
-			if kwd:
-				info = self.service('common').one(model, **kwd)
-				if info and (id != info['id']):
-					self.out(msg)
-					return
-			state = self.service('common').update(model, id, self.data['update'])
-			self.log(model, 'update', self.data['update'])
-			self.out('yes', {'id':state})
-			return state
-
-	def drop(self, model):
-		if not self.data['auth']:
-			self.auth()
-		else:
-			id = self.input('id')
-			state = self.input('state', False)
-			state = self.service('common').delete(model, id, state)
-			self.log(model, 'delete', {id:id, state:state})
-			self.out('yes', {'state':state})
-
-	def log(self, model, method, data):
-		if 'admin' in self.data['setting'] and self.data['setting']['admin'] > 0:
-			insert = {}
-			insert['admin_id'] = self.data['setting']['admin']
-			insert['model'] = model
-			insert['method'] = method
-			insert['data'] = json.dumps(data)
-			self.service('common').update('manage_log', None, insert)
-
-	def view(self, name):
-		if not self.data['auth']:
-			self.auth()
-		else:
-			config = Demeter.config[Demeter.web]
-			path = ''
-			if 'mobile' in config:
-				mobile = Demeter.checkMobile(self.request)
-				if mobile:
-					path = 'mobile/'
-				else:
-					path = 'pc/'
-			self.render(path + name, data=self.data, Demeter=Demeter)
-
-	def auth(self):
-		self.out('您没有权限')
-
-	def out(self, msg='', data={}, code=0):
-		callback = self.input('callback')
-		function = self.input('function')
-		result = Demeter.out(msg=msg, data=data, code=code, callback=callback, function=function)
-		self.write(result)
-		if not data:
-			from tornado.web import Finish
-			raise Finish()
-		else:
-			self.finish()
+    def set_default_headers(self):
+        self.set_header("Access-Control-Allow-Origin", "*")
+        self.set_header("Access-Control-Allow-Headers", "x-requested-with")
+        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
+
+    def initialize(self):
+        try:
+            Demeter.request = self
+            self.assign()
+            self.page()
+            self.cookie()
+            self.setting()
+        except Exception as e:
+            return
+
+    def get_current_user(self):
+        return self.get_secure_cookie(self.KEYS[0])
+
+    def assign(self):
+        self.data = {}
+        self.data['setting'] = Demeter.config['setting']
+        self.data['base'] = Demeter.config['base']
+        
+    def cookie(self):
+        for key in self.KEYS:
+            cookie = None
+            """
+            if key in self.data['base']:
+                cookie = self.data['base'][key]
+            """
+            if not cookie:
+                cookie = self.get_secure_cookie(key)
+                #cookie = self.get_cookie(key)
+            if not cookie:
+                value = self.input(key)
+                if value:
+                    #self.set_secure_cookie(key, value)
+                    self.data['setting'][key] = value
+                else:
+                    self.data['setting'][key] = 0
+            else:
+                self.data['setting'][key] = cookie
+
+    def page(self):
+        Demeter.config['page'] = {}
+        page = self.input('page')
+        if page:
+            Demeter.config['page']['ajax'] = True
+        else:
+            Demeter.config['page']['ajax'] = False
+            page = 1
+        Demeter.config['page']['current'] = page
+        Demeter.config['page']['total'] = 0
+        self.data['page'] = Demeter.config['page']
+
+    def search(self):
+        data = self.request.arguments
+        self.data['search'] = {}
+        self.data['update'] = {}
+        for key in data:
+            if 'search_' in key:
+                index = key.replace('search_', '')
+                i = 0
+                for a in data[key]:
+                    data[key][i] = a.decode()
+                self.data['search'][index] = ",".join(data[key])
+            if 'update_' in key:
+                index = key.replace('update_', '')
+                i = 0
+                for a in data[key]:
+                    data[key][i] = a.decode()
+                    i = i + 1
+                self.data['update'][index] = ",".join(data[key])
+
+    def input(self, key, value=None):
+        return self.get_argument(key, value)
+
+    def inputs(self, key):
+        return self.get_arguments(key)
+
+    def inputAll(self):
+        param = {k: v[0].decode() for k, v in self.request.arguments.items() if v}
+        if self.request.headers.get("Content-Type", "").startswith("application/json"):
+            try:
+                param.update(tornado.escape.json_decode(self.request.body))
+            except:
+                pass
+        return param
+
+    def service(self, name):
+        return Demeter.service(name)
+
+    def model(self, name):
+        return Demeter.model(name)
+
+    def set(self, **kwd):
+        self.data['common'] = kwd
+        self.data['common']['argvs'] = ''
+
+    def show(self, name):
+        self.view('common/'+name+'.html')
+
+    def list(self, model, order = 'cdate desc'):
+        self.data['state'] = self.input('state', True)
+        self.data['list'] = self.service('common').list(model, state=self.data['state'], search=self.data['search'], page=True, order=order)
+
+    def one(self, model, **kwd):
+        self.data['info'] = {}
+        if 'id' not in kwd and self.input('id'):
+            id = self.input('id')
+            if id:
+                kwd['id'] = id
+        if kwd:
+            self.data['info'] = self.service('common').one(model, **kwd)
+
+    def update(self, model, msg='', id=0, **kwd):
+        if not self.data['auth']:
+            self.auth()
+        else:
+            if id <= 0:
+                id = self.input('id')
+            if kwd:
+                info = self.service('common').one(model, **kwd)
+                if info and (id != info['id']):
+                    self.out(msg)
+                    return
+            state = self.service('common').update(model, id, self.data['update'])
+            self.log(model, 'update', self.data['update'])
+            self.out('yes', {'id':state})
+            return state
+
+    def drop(self, model):
+        if not self.data['auth']:
+            self.auth()
+        else:
+            id = self.input('id')
+            state = self.input('state', False)
+            state = self.service('common').delete(model, id, state)
+            self.log(model, 'delete', {id:id, state:state})
+            self.out('yes', {'state':state})
+
+    def log(self, model, method, data):
+        if 'admin' in self.data['setting'] and self.data['setting']['admin'] > 0:
+            insert = {}
+            insert['admin_id'] = self.data['setting']['admin']
+            insert['model'] = model
+            insert['method'] = method
+            insert['data'] = json.dumps(data)
+            self.service('common').update('manage_log', None, insert)
+
+    def view(self, name):
+        if not self.data['auth']:
+            self.auth()
+        else:
+            config = Demeter.config[Demeter.web]
+            path = ''
+            if 'mobile' in config:
+                mobile = Demeter.checkMobile(self.request)
+                if mobile:
+                    path = 'mobile/'
+                else:
+                    path = 'pc/'
+            self.render(path + name, data=self.data, Demeter=Demeter)
+
+    def host(self):
+        return self.request.protocol + "://" + self.request.host
+
+    def auth(self):
+        self.out('您没有权限')
+
+    def out(self, msg='', data={}, code=0):
+        callback = self.input('callback')
+        function = self.input('function')
+        result = Demeter.out(msg=msg, data=data, code=code, callback=callback, function=function)
+        self.write(result)
+        if not data:
+            from tornado.web import Finish
+            raise Finish()
+        else:
+            self.finish()
 
 class Web(object):
-	@classmethod
-	def auth(self, method):
-		return tornado.web.authenticated(method)
-
-	@classmethod
-	def setting(self, method):
-		return self.run(method)
-		
-	@staticmethod
-	def run(method):
-		@tornado.gen.coroutine
-		@functools.wraps(method)
-		def callback(self, *args, **kwargs):
-			#self._auto_finish = False
-			try:
-				result = method(self, *args, **kwargs)
-				return result
-			except Exception as e:
-				import traceback
-				error = traceback.format_exc()
-				if 'Finish' in error:
-					return
-				else:
-					traceback.print_exc()
-					try:
-						return self.view('404.html')
-					except Exception as e:
-						return self.out('404')
-			#return gevent.spawn(method, self, *args, **kwargs)
-		return callback
-
-	@classmethod
-	def init(self, application):
-		for v in application:
-			self.load(v)
-
-	@classmethod
-	def load(self, package):
-		"""
-		path = os.path.split(os.path.realpath(file))[0] + '/'
-		sys.path.append(path)
-		files = self.file(path)
-		"""
-		url = []
-		for key in Demeter.getPackage(package):
-			module = Demeter.getObject(key)
-			url = self.url(module, key, url)
-		Demeter.route = Demeter.route + url
-
-	"""
-	@staticmethod
-	def file(path):
-		files = os.listdir(path)
-		result = []
-		for key in files:
-			if key and '.DS_Store' not in key and  '__' not in key and 'pyc' not in key:
-				key = key.replace('.py', '')
-				result.append(key)
-		return result
-	"""
-
-	@staticmethod
-	def url(module, key, url):
-		str = Demeter.getMethod(module)
-		key = key.split('.')[-1]
-		for i,j in str:
-			act = ''
-			if '_path' in i:
-				act = i.replace('_path', '')
-			if '_html' in i:
-				act = i.replace('_html', '.html')
-			if act:
-				attr = getattr(module, i)
-				if key == 'main' and act == 'index':
-					url.append((r'/', attr))
-				elif key == act or act == 'index':
-					url.append((r'/'+key, attr))
-				url.append((r'/'+key+'/'+act, attr))
-		return url
-	@classmethod
-	def start(self, application=[]):
-		t = threading.Thread(target=lambda: self.start_server(application))
-		t.start()
-	@classmethod
-	def start_server(self, application=[]):
-		self.init(application)
-		if 'route' in Demeter.config['setting']:
-			Demeter.echo(Demeter.route)
-		config = Demeter.config[Demeter.web]
-		cookie = False
-		if 'tornado' not in Demeter.config:
-			Demeter.config['tornado'] = {}
-		if 'xsrf_cookies' in config:
-			cookie = Demeter.bool(config['xsrf_cookies'])
-		settings = dict({
-			"static_path": Demeter.webPath + 'static',
-			"template_path": Demeter.webPath + 'templates',
-			"cookie_secret": 'demeter',
-			"login_url": '/user/login',
-			"xsrf_cookies": cookie,
-			"debug": Demeter.bool(config['debug']),
-			#"autoreload": Demeter.bool(config['autoreload']),
-			"port": config['port'],
-			"max_buffer_size": int(config['max_buffer_size']),
-			"process": int(config['process'])
-		}, **Demeter.config['tornado'])
-
-		com = ('cookie_secret', 'login_url', 'static_path', 'template_path')
-		for v in com:
-			if v in config:
-				settings[v] = config[v]
-
-		handlers = []
-		def application_setting():
-			handlers.append((r"/upload/(.*)", tornado.web.StaticFileHandler, {"path": Demeter.path + 'runtime/upload/'}))
-			handlers.append((r"/files/(.*[\.png|\.jpg|\.gif|\.js|\.css|\.font|\.fonts|\.ttc|\.ttf|\.woff|\.woff2|\.fon|\.eot|\.otf])", tornado.web.StaticFileHandler, {"path": Demeter.path + 'runtime/files/'}))
-			handlers.append((r"/qrcode/(.*)", tornado.web.StaticFileHandler, {"path": Demeter.path + 'runtime/qrcode/'}))
-			handlers.append((r"/camera/(.*)", tornado.web.StaticFileHandler, {"path": Demeter.path + 'runtime/camera/'}))
-			handlers.append((r"/static/(.*)", tornado.web.StaticFileHandler, {"path": "static"}))
-			handlers.append((r"/(apple-touch-icon\.png)", tornado.web.StaticFileHandler, dict(path=settings['static_path'])))
-			handlers.extend(Demeter.route)
-
-		application_setting()
-		application = tornado.web.Application(handlers=handlers, **settings)
-		if Demeter.checkPy3():
-			import asyncio
-			asyncio.set_event_loop(asyncio.new_event_loop())
-		if settings['debug'] == True:
-			application.listen(settings['port'])
-		else:
-			server = tornado.httpserver.HTTPServer(application, settings['max_buffer_size'])
-			server.bind(settings['port'])
-			server.start(settings['process'])
-		try:		
-			Demeter.echo('running on port %s' % settings['port'])
-			tornado.ioloop.IOLoop.instance().start()
-
-		except KeyboardInterrupt:
-			tornado.ioloop.IOLoop.instance().stop()
+    @classmethod
+    def auth(cls, method):
+        return tornado.web.authenticated(method)
+
+    @classmethod
+    def setting(cls, method):
+        return cls.run(method)
+        
+    @staticmethod
+    def run(method):
+        @tornado.gen.coroutine
+        @functools.wraps(method)
+        def callback(self, *args, **kwargs):
+            try:
+                result = method(self, *args, **kwargs)
+                return result
+            except Exception as e:
+                import traceback
+                error = traceback.format_exc()
+                if 'Finish' in error:
+                    return
+                else:
+                    traceback.print_exc()
+                    try:
+                        return self.view('404.html')
+                    except Exception as e:
+                        return self.out('404')
+        return callback
+
+    @classmethod
+    def init(cls, application):
+        for v in application:
+            cls.load(v)
+
+    @classmethod
+    def load(cls, package):
+        url = []
+        for key in Demeter.getPackage(package):
+            module = Demeter.getObject(key)
+            url = cls.url(module, key, url)
+        Demeter.route = Demeter.route + url
+
+    @staticmethod
+    def url(module, key, url):
+        methods = Demeter.getMethod(module)
+        key = key.split('.')[-1]
+        for i, j in methods:
+            act = ''
+            if '_path' in i:
+                act = i.replace('_path', '')
+            if '_html' in i:
+                act = i.replace('_html', '.html')
+            if act:
+                attr = getattr(module, i)
+                if key == 'main' and act == 'index':
+                    url.append((r'/', attr))
+                elif key == act or act == 'index':
+                    url.append((r'/' + key, attr))
+                url.append((r'/' + key + '/' + act, attr))
+        return url
+
+    @classmethod
+    def start(cls, application=[]):
+        # 这里不使用线程,直接主线程启动服务
+        cls.start_server(application)
+
+    @classmethod
+    def start_server(cls, application=[]):
+        # 确保事件循环存在
+        try:
+            asyncio.get_running_loop()
+        except RuntimeError:
+            asyncio.set_event_loop(asyncio.new_event_loop())
+
+        cls.init(application)
+        if 'route' in Demeter.config['setting']:
+            Demeter.echo(Demeter.route)
+
+        config = Demeter.config[Demeter.web]
+        cookie = False
+        if 'tornado' not in Demeter.config:
+            Demeter.config['tornado'] = {}
+        if 'xsrf_cookies' in config:
+            cookie = Demeter.bool(config['xsrf_cookies'])
+
+        settings = dict({
+            "static_path": Demeter.webPath + 'static',
+            "template_path": Demeter.webPath + 'templates',
+            "cookie_secret": 'demeter',
+            "login_url": '/user/login',
+            "xsrf_cookies": cookie,
+            "debug": Demeter.bool(config['debug']),
+            "port": config['port'],
+            "max_buffer_size": int(config['max_buffer_size']),
+            "process": int(config['process'])
+        }, **Demeter.config['tornado'])
+
+        # 补充设置项
+        for v in ('cookie_secret', 'login_url', 'static_path', 'template_path'):
+            if v in config:
+                settings[v] = config[v]
+
+        handlers = []
+
+        def application_setting():
+            handlers.append((r"/upload/(.*)", tornado.web.StaticFileHandler, {"path": Demeter.path + 'runtime/upload/'}))
+            handlers.append((r"/files/(.*[\.png|\.jpg|\.gif|\.js|\.css|\.font|\.fonts|\.ttc|\.ttf|\.woff|\.woff2|\.fon|\.eot|\.otf])",
+                             tornado.web.StaticFileHandler, {"path": Demeter.path + 'runtime/files/'}))
+            handlers.append((r"/qrcode/(.*)", tornado.web.StaticFileHandler, {"path": Demeter.path + 'runtime/qrcode/'}))
+            handlers.append((r"/camera/(.*)", tornado.web.StaticFileHandler, {"path": Demeter.path + 'runtime/camera/'}))
+            handlers.append((r"/static/(.*)", tornado.web.StaticFileHandler, {"path": "static"}))
+            handlers.append((r"/(apple-touch-icon\.png)", tornado.web.StaticFileHandler, dict(path=settings['static_path'])))
+            handlers.extend(Demeter.route)
+
+        def stop_server(signum, frame):
+            print("Signal received, stopping Tornado...")
+            tornado.ioloop.IOLoop.current().add_callback_from_signal(tornado.ioloop.IOLoop.current().stop)
+
+        # 注册信号处理,只能在主线程调用
+        signal.signal(signal.SIGINT, stop_server)
+        signal.signal(signal.SIGTERM, stop_server)
+
+        application_setting()
+        application = tornado.web.Application(handlers=handlers, **settings)
+
+        if settings['debug']:
+            application.listen(settings['port'])
+        else:
+            server = tornado.httpserver.HTTPServer(application, settings['max_buffer_size'])
+            server.bind(settings['port'])
+            server.start(settings['process'])
+
+        Demeter.echo(f'running on port {settings["port"]}')
+
+        try:
+            tornado.ioloop.IOLoop.current().start()
+        except KeyboardInterrupt:
+            print("KeyboardInterrupt received, stopping...")
+            tornado.ioloop.IOLoop.current().stop()
+        finally:
+            print("Server stopped, exiting.")
+            os._exit(0)