def select(obj, filter, where): table = obj.tblname fields = tuple(obj.fields.keys()) args = tuple() select = '*' flen = len(filter) if flen == 1: select = filter[0] elif flen > 1: names = deque() for n in filter: if n in fields: names.append(n) select = "%s" % ', '.join(names) s = f"SELECT {select} FROM {table} WHERE" i = 0 cond = ' AND ' for k, v in where.items(): if i == 0: cond = ' ' if k == 'pk' or k in fields: typ = fieldType(obj.fields, k) s += f"{cond}{k}=" + lang.valfmt(typ) args += (typ(v), ) i += 1 s += ";" log.debug(s) return (s, args)
def setLang(manager): global lang log.debug(f"set lang {manager.name}") lang = manager table.lang = manager mod_insert.lang = manager mod_select.lang = manager
def execute(self, op, param=tuple()): if self._cur is None: log.debug('new cursor') self._cur = self._conn.cursor() log.debug('execute') self._cur.execute(op, param) return self._cur
def view(db): """show core status info""" log.debug('view') s = DBStatus() row = s.get(db, 'status', pk = _pk) if row is None: return {} return dict(row)
def _setup(config, wapp): """initialize setup handlers""" log.debug('init setup') wapp.route('/', 'GET', setup.redirect, name='setup.redir') wapp.route('/_/setup', 'GET', setup.index, name='setup') wapp.route('/_/setup/db/create', ['GET', 'POST'], setup.dbCreate, name='db.create') wapp.route('/<rpath:path>', 'GET', setup.redirect, name='setup.redirall')
def _debugMode(args, cfgfn): """start webapp in debug mode - use bottle server""" app = wapp.init(cfgfn=cfgfn) app.run(host='127.0.0.1', port=int(args.port), quiet=False, reloader=True, debug=True) log.debug('exit') return 0
def serve(filename): """serve static files""" filename = path.normpath(filename) ext = path.splitext(filename)[1] if ext == '' or filename.startswith('.') or \ not _serveExtension.get(ext, False): log.warn(f"static file refuse '{filename}'") return bottle.HTTPError(404, "file not found") log.debug(f"serve {filename}") return bottle.static_file(filename, root=_rootdir)
def wrapper(*args, **kwargs): log.debug(f"wrapper {ctx.name}") start = None if self.debug: start = time() resp = callback(*args, **kwargs) if isinstance(resp, bottle.HTTPResponse): self._setHeaders(resp, start) else: self._setHeaders(bottle.response, start) return resp
def parse(argv): """parse command line arguments""" p = _new() args = p.parse_args(args=argv) if args.debug: log.init('debug') else: log.init(args.log) log.debug(version.string('rosshm')) log.debug(f"config {args.config}") return args
def connect(cfg): log.debug('connect') lang = MySQLLang() sql.setLang(lang) return MySQLdb.connect( host=cfg.get('host', 'localhost'), db=cfg.get('name', 'rosshmdb'), user=cfg.get('user', 'rosshm'), passwd=cfg.get('password', None), connect_timeout=cfg.get('timeout', 60), charset=cfg.get('charset', 'utf8'), use_unicode=True, cursorclass=DictCursor, )
def wrapper(*args, **kwargs): log.debug(f"wrapper {ctx.name}") conn = db.connect(self.cfg) kwargs['db'] = conn try: resp = callback(*args, **kwargs) conn.commit() except db.IntegrityError as err: log.error(str(err)) conn.rollback() raise bottle.HTTPError(500, "database error", err) finally: conn.close() return resp
def _dbstatus(): """get database status table info""" log.debug('db status') status = {} error = None try: conn = _dbconn() status = coredb.status(conn) conn.close() if status is not None: status = dict(status) except db.DatabaseError as err: log.error(f"check database: {err}") error = str(err) return {'error': error, 'status': status, 'db': config.database()}
def connect(cfg): log.debug('connect') sql.setLang(SqliteLang()) name = cfg.get('name') if name.startswith(':memory:'): memdb = name.split(':')[2].strip() if memdb == '': memdb = 'rosshmdb' uri = f"file:{memdb}?mode=memory&cache=shared" else: uri = f"file:{name}?cache=shared" log.debug(f"uri {uri}") conn = sqlite3.connect(uri, uri = True) conn.row_factory = sqlite3.Row return conn
def create(name, fields): s = f"CREATE TABLE {name}" fl = deque() fl.append(lang.primaryKey()) for f, d in fields.items(): typ = d[0] args = d[1] fl.append(_mkfield(f, typ, args)) s += " (%s)" % ', '.join(fl) opts = lang.tableOptions() if opts != '': s += f" {opts}" s += ";" log.debug(s) return s
def init(config, wapp): """initialize webapp core package check database or start setup handler""" log.debug(f"init {config.filename()}") debug = config.getbool('debug') if checkdb(config): global _plugins dbcfg = config.database() log.debug(f"db plugin {dbcfg}") _plugins = [DBPlugin(dbcfg, debug=debug)] _views(config, wapp, _plugins) return True else: _setup(config, wapp) return False
def insert(obj, data): table = obj.tblname fields = tuple(obj.fields.keys()) fl = deque() vl = deque() vfmt = deque() for k, v in data.items(): if k == 'pk' or k in fields: fl.append(k) typ = fieldType(obj.fields, k) vfmt.append(lang.valfmt(typ)) vl.append(typ(v)) s = f"INSERT INTO {table}" s += " (%s)" % ', '.join(fl) s += " VALUES (%s);" % ', '.join(vfmt) log.debug(s) return (s, vl)
def apply(self, callback, ctx): """set Cache-Control and security headers amongst other things for all responses""" log.debug(f"apply {ctx.name}") def wrapper(*args, **kwargs): log.debug(f"wrapper {ctx.name}") start = None if self.debug: start = time() resp = callback(*args, **kwargs) if isinstance(resp, bottle.HTTPResponse): self._setHeaders(resp, start) else: self._setHeaders(bottle.response, start) return resp return wrapper
def checkdb(config, conn=None): dbcfg = config.database() log.debug(f"checkdb {dbcfg['driver']} {dbcfg['name']} {dbcfg['config']}") if dbcfg.get('driver') == 'sqlite' and not dbcfg.get('name').startswith( ':memory:'): dbfn = dbcfg.get('name') if not path.isfile(dbfn): makedirs(path.dirname(dbfn), mode=0o750, exist_ok=True) rv = True try: if conn is None: conn = db.connect(dbcfg) rv = _check(conn) except db.DatabaseError as err: log.error(f"check database: {err}") return False finally: if conn is not None: conn.close() return rv
def create(dbn, conn, **initdb_args): """create database schema""" # create schema tracking table first log.info(f"create {dbn} schema") meta = DBSchema() meta.dbn = dbn tbl = DBTable(meta) tbl.create(conn) meta.set(conn, object=tbl.name, version=tbl.version) # create the rest of the tables tables = list(reg.DB.tables[dbn].keys()) log.debug(f"create {dbn} tables {tables}") for tbl in reg.DB.tables[dbn].values(): log.info(f"create {dbn} table {tbl.name}") tbl.create(conn) meta.set(conn, object=tbl.name, version=tbl.version) # initialize database initdb = reg.DB.init.get(dbn, None) if initdb is not None: initdb(conn, **initdb_args) return {}
def close(self, nolog=False): if not nolog: log.debug('close') if self._cur is not None: if not nolog: log.debug('close cursor') self._cur.close() self._cur = None if not nolog: log.debug('close connection') self._conn.close() self._closed = True
def dbCreate(req=None): """create database""" log.debug('db create') dbstat = _dbstatus() if dbstat['error'] is None: # database is ok? return {'error': 'database already created?', 'db': config.database()} if req is None: req = bottle.request rv = {} if req.method == 'POST': log.debug('db create action') admin = _getAdmin(req) conn = None try: conn = _dbconn() log.info(f"create admin user: {admin.username}") rv = db.create('core', conn, admin=admin) conn.commit() bottle.redirect('/_/setup') except db.IntegrityError as err: log.error(f"create database integrity error: {err}") rv['error'] = str(err) if conn is not None: log.debug('rollback') conn.rollback() except db.DatabaseError as err: log.error(f"create database: {err}") rv['error'] = str(err) if conn is not None: log.debug('rollback') conn.rollback() finally: if conn is not None: conn.close() rv['db'] = config.database() return rv
def _uwsgi(args, cfgfn): """start webapp using uwsgi command line tool""" rc = 0 inifn = path.abspath(libdir / 'wapp' / 'uwsgi.ini') cmd = ('uwsgi', '--need-plugin', 'python3') cmd += ('--set-ph', f"rosshm-home={_gethome()}") if args.workers != '': cmd += ('--set-ph', f"rosshm-workers={args.workers}") if args.threads != '': cmd += ('--set-ph', f"rosshm-threads={args.threads}") if args.user != '': cmd += ('--set-ph', f"rosshm-user={args.user}") if args.group != '': cmd += ('--set-ph', f"rosshm-group={args.group}") cmd += ('--set-ph', f"rosshm-port={args.port}") cmd += ('--touch-reload', cfgfn) cmd += ('--ini', inifn) cmdenv = {'ROSSHM_CONFIG': cfgfn} try: log.debug(f"run {cmd}") log.debug(f"config {cfgfn}") proc.run(cmd, shell=False, env=cmdenv, check=True) except proc.CalledProcessError as err: log.error(f"{err}") rc = err.returncode except KeyboardInterrupt: rc = 128 log.debug(f"exit {rc}") return rc
def __init__(self, conn): log.debug('init') self._conn = conn self._cur = None self._closed = False
def rollback(self): log.debug('rollback') self._conn.rollback()
def _views(config, wapp, plugins): """initialize core api handlers""" log.debug('init views') wapp.route('/_/', 'GET', status.view, name='core.status', apply=plugins)
def commit(self): log.debug('commit') self._conn.commit()
def create(self, db): log.debug(f"create {self.name}") stmt = sql.createTable(self.name, self.fields) db.execute(stmt)
def init(config, wapp): """initialize web package""" log.debug(f"init {config.filename()}")
def setup(self, wapp): # the plugin is not installed globally so this should never run log.debug(f"setup {self.name}")
def init(cfgfn=None): """initialize webapp""" config.init(fn=cfgfn) debug = config.getbool('debug') if debug: log.init('debug') else: log.init(config.get('log.level')) log.debug(version.string('rosshm')) log.debug(config.filename()) tpldir = path.abspath(libdir / 'tpl') log.debug(f"templates path {tpldir}") bottle.TEMPLATE_PATH = [tpldir] wapp = bottle.Bottle() inifn = path.abspath(libdir / 'wapp' / 'bottle.ini') log.debug(f"bottle config {inifn}") wapp.config.load_config(inifn) log.debug('install plugins') wapp.install(response.Plugin(debug=debug)) if config.getbool('static.enable'): log.debug('serve static files') wapp.route(r'/static/<filename:re:.*\..*>', 'GET', static.serve, name='static') coreok = False if config.getbool('core.enable'): log.debug('core init') coreok = core.init(config, wapp) else: log.warn('core disabled') coreok = True log.debug(f"coreok {coreok}") if coreok and config.getbool('web.enable'): log.debug('web init') web.init(config, wapp) else: log.warn('web disabled') return wapp