from apscheduler.schedulers.background import BackgroundScheduler from flask import Flask from config import Config from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_basicauth import BasicAuth app = Flask(__name__) basic_auth = BasicAuth(app) app.config.from_object(Config) db = SQLAlchemy(app) migrate = Migrate(app, db) sche = BackgroundScheduler() sche.start() from app import routes
import time import sys import os from flask import Blueprint, request, session, url_for from flask import render_template, redirect, jsonify from flask_basicauth import BasicAuth from authlib.integrations.flask_oauth2 import current_token from authlib.oauth2 import OAuth2Error from .models import db, User, OAuth2Client from .oauth2 import authorization, require_oauth from .intents import api_delegate from .admin import admin_render basic_auth = BasicAuth() bp = Blueprint('routes', 'home') @bp.route('/oauth/authorize', methods=['GET', 'POST']) def authorize(): user = User.query.filter_by( username=os.environ["GOOGLE_DEFAULT_USERNAME"]).first() resp = authorization.create_authorization_response(grant_user=user) return resp @bp.route('/oauth/token', methods=['POST']) def issue_token(): return authorization.create_token_response() @bp.route('/oauth/revoke', methods=['POST'])
return app_obj def add_bp(app_obj): modules_define = [ test.app, record.app, promise.app, motion.app, archive.app ] for bp_app in modules_define: app_obj.register_blueprint(bp_app) app = init_app() # client = FlaskAPNS() # client.init_app(app) admin_basic_auth = BasicAuth(app) migrate = Migrate(app, db) @app.route('/') @app.route('/index') def index(): return 'This is index page' @app.teardown_appcontext def session_clear(exception): if exception and session.is_active: session.rollback() else: session.commit()
def make_app(test_mode_internal=False): if test_mode_internal: app, db = appmaker.make_test_app() else: # pragma: no cover app, db = appmaker.make_app() app.config['TEMPLATES_AUTO_RELOAD'] = True if 0: app.config['BASIC_AUTH_USERNAME'] = '******' app.config['BASIC_AUTH_PASSWORD'] = '******' app.config['BASIC_AUTH_FORCE'] = True basic_auth = BasicAuth(app) urlvalidate.register(app) app.jinja_env.globals["web_root"] = config.web_root app.jinja_env.globals["action_prefix"] = config.action_prefix app.jinja_env.globals["test_mode"] = config.test_mode def format_datetime(tstamp): """ Format float date into UTC timestamp. """ return time.strftime("%c", time.gmtime(tstamp)) app.jinja_env.filters["datetime"] = format_datetime app.jinja_env.trim_blocks = True app.jinja_env.lstrip_blocks = True @app.route('/api1/js/<path:path>') def js_static(path): return send_from_directory('js', path) @app.route("/api1/") def _entry(): cml = Global.current_member_list() published = ProposalMetadata.all_public() member_applications = cml.applications() return render_template("index.html", proposals=published, member_applications=member_applications, cml=cml) @app.route("/api1/unpublished-proposals") def _unpublished(): unpublished_proposals = ( Global.current_member_list().proposals().filter( ProposalMetadata.file_public == False)) return render_template("unpublished_proposals.html", proposals=unpublished_proposals) @app.route("/api1/debug") def get_debug_overview(): if config.test_mode: return render_template("debug_overview.html") else: abort(404) @app.route("/api1/debug/objects") def _get_debug_objects(): if config.test_mode: objmap = get_all_objects() return render_template("debug_objects.html", objmap=objmap) else: abort(404) @app.route("/api1/debug/hashrefs-by-type/<objtype:name>") def _get_debug_hashrefs_by_type(name): if config.test_mode: Cls = name2type[name] res = [obj.hashref() for obj in Cls.query] return jsonify(res) else: abort(404) @app.route("/api1/debug/current-member-list-hash") def _get_debug_current_member_list_hash(): if not config.test_mode: abort(404) return Global.current_member_list().hashref() @app.route("/api1/debug/meta-for-raw-file/<shex:hashval>") def _get_debug_meta_for_raw(hashval): if not config.test_mode: abort(404) obj = RawFile.by_hash(hashval) if obj is None: abort(404) else: return obj.proposal_metadata.hashref() @app.route("/api1/debug/vote-for-raw-file/<shex:hashval>") def _get_debug_vote_for_raw_file(hashval): if not config.test_mode: abort(404) v = ProposalVote.by_raw_file_hash(hashval) if v is None: abort(404) else: return v.hashref() @app.route("/api1/debug/result-for-vote/<shex:hashval>") def _get_debug_result_for_vote(hashval): if not config.test_mode: abort(404) v = ProposalVote.by_hash(hashval) if v is None: abort(404) else: return v.result.hashref() @app.route("/api1/debug/summary-of-proposal-vote-result/<shex:hashval>") def _get_debug_summary_of_proposal_vote_result(hashval): if not config.test_mode: abort(404) v = ProposalVoteResult.by_hash(hashval) if v is None: abort(404) else: return jsonify(v.summarize()) @app.route("/api1/debug/summary-of-member-election-result/<shex:hashval>") def _get_debug_summary_of_member_election_result(hashval): if not config.test_mode: abort(404) v = MemberElectionResult.by_hash(hashval) if v is None: abort(404) else: return jsonify(v.summarize()) @app.route("/api1/debug/testkeys") def get_testkeys(): if config.test_mode: from test_tmemberlist import makeTestMemberKeys testmembers = makeTestMemberKeys() return render_template("debug_testkeys.html", testmembers=zip(*testmembers)) else: abort(404) @app.route("/api1/debug/shutdown") def debug_shutdown(): """ Shutdown the web server. """ if config.test_mode: os.kill(os.getppid(), signal.SIGTERM) else: abort(404) @app.route("/api1/render/<objtype:name>/<shex:hashval>") def _render(name, hashval): Cls = name2type[name] obj = Cls.by_hash(hashval) if obj is None: abort(404) if not obj.public(): abort(401) # FIXME: or simply 404? try: j = obj.toJ() except RuntimeError: abort(404) # no JSON representation possible -> can't be rendered v = obj.extraRender() v.update({name: obj}) return render_template("render_%s.html" % obj.__tablename__, render_object_type=name, render_object_id=hashval, **v) @app.route("/api1/raw/<objtype:name>/<shex:hashval>") def _get_raw(name, hashval): Cls = name2type[name] obj = Cls.by_hash(hashval) if obj is None: abort(404) if not obj.public(): abort(401) if isinstance(obj, butypes.RawFile): return Response(response=obj.data, mimetype=obj.proposal_metadata.mime_type, headers={ "Content-Disposition": "attachment;filename=%s" % obj.proposal_metadata.filename }) else: return Response(response=obj.x_json, mimetype="application/json") @app.route("/api1/form/<name>") @app.route("/api1/form/<name>/<shex:hashval>") def _form(name, hashval=None): if name not in [ "generic", "proposal-upload", "proposal-publish", "open-proposal-vote", "close-proposal-vote", "cast-proposal-ballot", "cast-proposal-ballot-multiple", "cast-member-ballot-multiple", "propose-member", ]: abort(404) formopts = {} if name == "cast-proposal-ballot": pv = ProposalVote.by_hash(hashval) if pv is None: formopts = {"proposal": None} else: formopts = {"proposal": pv.proposal_metadata} elif name == "cast-member-ballot-multiple": formopts = { "member_applications": Global.current_member_list().applications() } return render_template("form_" + name + ".html", cml=Global.current_member_list(), hashval=hashval, **formopts) @app.route("/api1/form/cast-member-ballot/<name>/<address>") def _form_cast_member_ballot(name, address): return render_template("form_cast-member-ballot.html", cml=Global.current_member_list(), member_name=name, member_address=address) @app.route("/api1/form/close-member-elections") def _form_close_member_elections(): cml = Global.current_member_list() ma_names = " ".join(ma.new_member.name for ma in cml.applications()) return render_template("form_close-member-elections.html", cml=cml, ma_names=ma_names) @app.route("/api1/actions-by-member", methods=["GET"]) def _actions_by_member_name(): name = request.args.get('name') if name is None: abort(403, "Member name is missing.") if Member.by_name(name) is None: abort(404, "Member '%s' not found." % name) actions = queries.ActionByMemberNameAndType(name) return render_template("actions-by-member.html", actions=actions, membername=name) @app.route("/api1/proposal-ballots-by-member", methods=["GET"]) def _proposal_ballots_by_member_name(): name = request.args.get('name') if name is None: abort(403, "Member name is missing.") if Member.by_name(name) is None: abort(404, "Member '%s' not found." % name) ballots = queries.ActionByMemberNameAndType(name, "cast-proposal-ballot") published = list(ProposalMetadata.all_public()) # calculate set of published proposals that have and have not # yet been voted on by the selected member. pms_voted_on_by_member = [(ProposalVote.by_hash( ballot.parser.actvars["vote_hash"]).proposal_metadata) for ballot in ballots] pms_not_voted_on_by_member = [ pm for pm in published if pm not in pms_voted_on_by_member ] return render_template( "proposal-ballots-by-member.html", ballots=ballots, membername=name, published=published, pms_voted_on_by_member=pms_voted_on_by_member, pms_not_voted_on_by_member=pms_not_voted_on_by_member, ProposalVote=ProposalVote) @app.route("/api1/action", methods=["POST"]) def _action(): try: with write_lock: author_name = request.form[ "author_name"] if "author_name" in request.form else None action_string = request.form[ "action_string"] if "action_string" in request.form else None signature = request.form[ "signature"] if "signature" in request.form else None upload = request.files[ "upload"] if "upload" in request.files else None log.debug("action: Extracted fields") log.debug("action: string: %s", action_string) if author_name is None: abort(403, "Author name field is missing.") if action_string is None: abort(403, "Action string is missing.") if signature is None: abort(403, "Signature is missing.") author = Member.by_name(author_name) if author is None: abort(403, "Author '%s' does not exist." % author_name) if upload is not None: data = upload.read(config.max_upload + 1) if len(data) > config.max_upload: abort(413, "Upload too large.") hash_ref = butype.sha256(data) log.debug("Upload data hash: %s, len: %d", hash_ref, len(data)) if RawFile.by_hash(hash_ref) is not None: abort(409, "File exists.") else: hash_ref = None data = None log.debug("action: Handled upload") try: action = butypes.Action( #timestamp=None, author=author, action_string=action_string, signature=signature) returnval = action.apply(upload, data) db.session.commit() except jvalidate.ValidationError as ve: log.warn("Action: Problem: %d, %s", ve.status, str(ve)) if ve.error_page is not None: return render_template(ve.error_page["template"], **ve.error_page), ve.status else: abort(ve.status, str(ve)) except sqlalchemy.exc.IntegrityError as ie: # pragma: no cover # should not happen, but better be prepared log.warn("SQLalchemy integrity error: %s", ie) abort(403, "Database integrity error.") log.debug("action: Successful execution") return render_template("on_action_%s.html" % action.parser.act, action=action, returnval=returnval), 201 finally: try: db.session.close() db.session.expire_all() except Exception as e: log.warn("Error (%s) while invalidating session after action.", str(e)) abort(500, "Problem closing DB session.") @app.route("/api1/multi-action", methods=["POST"]) def _multi_action(): try: with write_lock: author_name = request.form[ "author_name"] if "author_name" in request.form else None multi_action_string = request.form[ "action_string"] if "action_string" in request.form else None multi_action_string = multi_action_string.replace("\r", "") multi_signature = request.form[ "signature"] if "signature" in request.form else None log.debug("multi-action: Extracted fields") if author_name is None: abort(403, "Author name field is missing.") if multi_action_string is None: abort(403, "Multi-action string is missing.") if multi_signature is None: abort(403, "Multi-signature is missing.") author = Member.by_name(author_name) if author is None: abort(403, "Author '%s' does not exist." % author_name) try: multi_action = butypes.MultiAction( author=author, multi_action_string=multi_action_string, multi_signature=multi_signature) returnvals = multi_action.apply() db.session.commit() except jvalidate.ValidationError as ve: log.warn("MultiAction: Problem: %d, %s", ve.status, str(ve)) if ve.error_page is not None: return render_template(ve.error_page["template"], **ve.error_page), ve.status else: abort(ve.status, str(ve)) except sqlalchemy.exc.IntegrityError as ie: # pragma: no cover # should not happen, but better be prepared log.warn("SQLalchemy integrity error: %s", ie) abort(403, "Database integrity error.") log.debug("multi-action: Successful execution") return render_template("on_multi_action.html", multi_action=multi_action, returnvals=returnvals), 201 finally: try: db.session.close() db.session.expire_all() except Exception as e: log.warn( "Error (%s) while invalidating session after multi-action.", str(e)) abort(500, "Problem closing DB session.") @app.route("/api1/zip/<objtype:name>/<shex:hashval>") def _get_zip(name, hashval): import io import zipfile Cls = name2type[name] obj = Cls.by_hash(hashval) if obj is None: abort(404) if not obj.public(): abort(401) output = io.BytesIO() with zipfile.ZipFile(file=output, mode="w", compression=zipfile.ZIP_DEFLATED) as zip: already = set() def writeObjAndDeps(obj, level): log.debug("Writing object: %d %s", level, obj) if obj.public() and obj not in already: if obj.__tablename__ != RawFile.__tablename__: zip.writestr( os.path.join("buv-export", obj.__tablename__, obj.hashref() + ".json"), obj.serialize()) else: zip.writestr( os.path.join("buv-export", obj.__tablename__, obj.hashref() + ".file"), obj.serialize()) already.add(obj) for dep in obj.dependencies(): writeObjAndDeps(dep, level + 1) writeObjAndDeps(obj, 0) mime_type = "application/zip" return Response(response=bytes(output.getbuffer()), mimetype=mime_type, headers={ "Content-Disposition": "attachment;filename=%s" % obj.__tablename__ + "-" + obj.hashref() + ".zip" }) # SQLAlchemy sessions are currently long lived - they persist for # the lifetime of the worker. To make sure that all operations # happen on the current data set, force expiry of all session's # objects here. # TODO: Figure out how to properly configure per-request sessions. @app.before_request def expire_session(): log.info("expiring session") #try: # db.session.commit() #except sqlalchemy.exc.InvalidRequestError: # # commit will fail if request was aborted - just ignore then # log.warn("encountered invalid request while expiring session") # pass db.session.expire_all() #db.session.close() return app, db
def __init__( self, environment, host, port, auth_credentials=None, tls_cert=None, tls_key=None, stats_csv_writer=None, delayed_start=False, ): """ Create WebUI instance and start running the web server in a separate greenlet (self.greenlet) Arguments: environment: Reference to the current Locust Environment host: Host/interface that the web server should accept connections to port: Port that the web server should listen to auth_credentials: If provided, it will enable basic auth with all the routes protected by default. Should be supplied in the format: "user:pass". tls_cert: A path to a TLS certificate tls_key: A path to a TLS private key delayed_start: Whether or not to delay starting web UI until `start()` is called. Delaying web UI start allows for adding Flask routes or Blueprints before accepting requests, avoiding errors. """ environment.web_ui = self self.stats_csv_writer = stats_csv_writer or StatsCSV( environment, stats_module.PERCENTILES_TO_REPORT) self.environment = environment self.host = host self.port = port self.tls_cert = tls_cert self.tls_key = tls_key app = Flask(__name__) CORS(app) self.app = app app.jinja_env.add_extension("jinja2.ext.do") app.debug = True app.root_path = os.path.dirname(os.path.abspath(__file__)) self.app.config["BASIC_AUTH_ENABLED"] = False self.auth = None self.greenlet = None self._swarm_greenlet = None if auth_credentials is not None: credentials = auth_credentials.split(":") if len(credentials) == 2: self.app.config["BASIC_AUTH_USERNAME"] = credentials[0] self.app.config["BASIC_AUTH_PASSWORD"] = credentials[1] self.app.config["BASIC_AUTH_ENABLED"] = True self.auth = BasicAuth() self.auth.init_app(self.app) else: raise AuthCredentialsError( "Invalid auth_credentials. It should be a string in the following format: 'user.pass'" ) if environment.runner: self.update_template_args() if not delayed_start: self.start() @app.route("/") @self.auth_required_if_enabled def index(): if not environment.runner: return make_response( "Error: Locust Environment does not have any runner", 500) self.update_template_args() return render_template("index.html", **self.template_args) @app.route("/swarm", methods=["POST"]) @self.auth_required_if_enabled def swarm(): assert request.method == "POST" parsed_options_dict = vars(environment.parsed_options ) if environment.parsed_options else {} for key, value in request.form.items(): if key == "user_count": # if we just renamed this field to "users" we wouldnt need this user_count = int(value) elif key == "spawn_rate": spawn_rate = float(value) elif key == "host": # Replace < > to guard against XSS environment.host = str(request.form["host"]).replace( "<", "").replace(">", "") elif key in parsed_options_dict: # cast to use same type that it already has in options. Wont work for None defaults parsed_options_dict[key] = type( parsed_options_dict[key])(value) if environment.shape_class: environment.runner.start_shape() return jsonify({ "success": True, "message": "Swarming started using shape class", "host": environment.host }) if self._swarm_greenlet is not None: self._swarm_greenlet.kill(block=True) self._swarm_greenlet = None self._swarm_greenlet = gevent.spawn(environment.runner.start, user_count, spawn_rate) self._swarm_greenlet.link_exception(greenlet_exception_handler) return jsonify({ "success": True, "message": "Swarming started", "host": environment.host }) @app.route("/stop") @self.auth_required_if_enabled def stop(): if self._swarm_greenlet is not None: self._swarm_greenlet.kill(block=True) self._swarm_greenlet = None environment.runner.stop() return jsonify({"success": True, "message": "Test stopped"}) @app.route("/stats/reset") @self.auth_required_if_enabled def reset_stats(): environment.events.reset_stats.fire() environment.runner.stats.reset_all() environment.runner.exceptions = {} return "ok" @app.route("/stats/report") @self.auth_required_if_enabled def stats_report(): res = get_html_report( self.environment, show_download_link=not request.args.get("download")) if request.args.get("download"): res = app.make_response(res) res.headers[ "Content-Disposition"] = "attachment;filename=report_%s.html" % time( ) return res def _download_csv_suggest_file_name(suggest_filename_prefix): """Generate csv file download attachment filename suggestion. Arguments: suggest_filename_prefix: Prefix of the filename to suggest for saving the download. Will be appended with timestamp. """ return f"{suggest_filename_prefix}_{time()}.csv" def _download_csv_response(csv_data, filename_prefix): """Generate csv file download response with 'csv_data'. Arguments: csv_data: CSV header and data rows. filename_prefix: Prefix of the filename to suggest for saving the download. Will be appended with timestamp. """ response = make_response(csv_data) response.headers["Content-type"] = "text/csv" response.headers[ "Content-disposition"] = f"attachment;filename={_download_csv_suggest_file_name(filename_prefix)}" return response @app.route("/stats/requests/csv") @self.auth_required_if_enabled def request_stats_csv(): data = StringIO() writer = csv.writer(data) self.stats_csv_writer.requests_csv(writer) return _download_csv_response(data.getvalue(), "requests") @app.route("/stats/requests_full_history/csv") @self.auth_required_if_enabled def request_stats_full_history_csv(): options = self.environment.parsed_options if options and options.stats_history_enabled: return send_file( os.path.abspath( self.stats_csv_writer.stats_history_file_name()), mimetype="text/csv", as_attachment=True, download_name=_download_csv_suggest_file_name( "requests_full_history"), etag=True, cache_timeout=None, conditional=True, last_modified=None, ) return make_response( "Error: Server was not started with option to generate full history.", 404) @app.route("/stats/failures/csv") @self.auth_required_if_enabled def failures_stats_csv(): data = StringIO() writer = csv.writer(data) self.stats_csv_writer.failures_csv(writer) return _download_csv_response(data.getvalue(), "failures") @app.route("/stats/requests") @self.auth_required_if_enabled @memoize(timeout=DEFAULT_CACHE_TIME, dynamic_timeout=True) def request_stats(): stats = [] for s in chain(sort_stats(self.environment.runner.stats.entries), [environment.runner.stats.total]): stats.append({ "method": s.method, "name": s.name, "safe_name": escape(s.name, quote=False), "num_requests": s.num_requests, "num_failures": s.num_failures, "avg_response_time": s.avg_response_time, "min_response_time": 0 if s.min_response_time is None else proper_round( s.min_response_time), "max_response_time": proper_round(s.max_response_time), "current_rps": s.current_rps, "current_fail_per_sec": s.current_fail_per_sec, "median_response_time": s.median_response_time, "ninetieth_response_time": s.get_response_time_percentile(0.9), "avg_content_length": s.avg_content_length, }) errors = [] for e in environment.runner.errors.values(): err_dict = e.to_dict() err_dict["name"] = escape(err_dict["name"]) err_dict["error"] = escape(err_dict["error"]) errors.append(err_dict) # Truncate the total number of stats and errors displayed since a large number of rows will cause the app # to render extremely slowly. Aggregate stats should be preserved. report = {"stats": stats[:500], "errors": errors[:500]} if len(stats) > 500: report["stats"] += [stats[-1]] if stats: report["total_rps"] = stats[len(stats) - 1]["current_rps"] report[ "fail_ratio"] = environment.runner.stats.total.fail_ratio report[ "current_response_time_percentile_95"] = environment.runner.stats.total.get_current_response_time_percentile( 0.95) report[ "current_response_time_percentile_50"] = environment.runner.stats.total.get_current_response_time_percentile( 0.5) is_distributed = isinstance(environment.runner, MasterRunner) if is_distributed: workers = [] for worker in environment.runner.clients.values(): workers.append({ "id": worker.id, "state": worker.state, "user_count": worker.user_count, "cpu_usage": worker.cpu_usage, "memory_usage": worker.memory_usage, }) report["workers"] = workers report["state"] = environment.runner.state report["user_count"] = environment.runner.user_count return jsonify(report) @app.route("/exceptions") @self.auth_required_if_enabled def exceptions(): return jsonify({ "exceptions": [{ "count": row["count"], "msg": row["msg"], "traceback": row["traceback"], "nodes": ", ".join(row["nodes"]), } for row in environment.runner.exceptions.values()] }) @app.route("/exceptions/csv") @self.auth_required_if_enabled def exceptions_csv(): data = StringIO() writer = csv.writer(data) self.stats_csv_writer.exceptions_csv(writer) return _download_csv_response(data.getvalue(), "exceptions")
from flask import Flask, request, jsonify from flask_restx import Resource, Api from flask_basicauth import BasicAuth app = Flask(__name__) # กำหนด username กับ password app.config['BASIC_AUTH_USERNAME'] = '******' app.config['BASIC_AUTH_PASSWORD'] = '******' api = Api(app) basic_auth = BasicAuth(app) # มาจาก class BasicAuth tasks = [{'id': 1, 'name': 'Do homework'}] # การเข้าถึง resource class TodoList(Resource): @basic_auth.required # decorator ถ้าจะใช้ function get() จะต้องผ่าน basic_auth ก่อน (ต้อง login ก่อน) def get(self): return jsonify({'tasks': tasks}) @basic_auth.required # decorator ถ้าจะใช้ function post() จะต้องผ่าน basic_auth ก่อน (ต้อง login ก่อน) def post(self): task = { 'id': len(tasks) + 1, 'name': api.payload['name'], } tasks.append(task) return api.payload, 201 api.add_resource(