A copyright violation detector running on Wikimedia Cloud Services https://tools.wmflabs.org/copyvios/
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 

203 строки
6.4 KiB

  1. #! /usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import datetime
  4. from functools import wraps
  5. from hashlib import md5
  6. from json import dumps
  7. from logging import DEBUG, INFO, getLogger
  8. from logging.handlers import TimedRotatingFileHandler
  9. from multiprocessing import Value
  10. from os import path
  11. from time import asctime
  12. from traceback import format_exc
  13. from urllib import quote_plus, quote
  14. from earwigbot.bot import Bot
  15. from earwigbot.wiki.copyvios import globalize
  16. from flask import Flask, g, make_response, request, redirect, session
  17. from flask_mako import MakoTemplates, render_template, TemplateError
  18. from copyvios.api import format_api_error, handle_api_request
  19. from copyvios.checker import do_check
  20. from copyvios.cookies import parse_cookies
  21. from copyvios.misc import cache, get_notice
  22. from copyvios.settings import process_settings
  23. from copyvios.sites import update_sites
  24. from copyvios.auth import oauth_login_start, oauth_login_end, clear_login_session
  25. app = Flask(__name__)
  26. MakoTemplates(app)
  27. app.config.from_pyfile("config.py", True)
  28. app.config['SESSION_COOKIE_SECURE'] = True
  29. app.config['SESSION_COOKIE_SAMESITE'] = "Lax"
  30. app.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(days=365)
  31. hand = TimedRotatingFileHandler("logs/app.log", when="midnight", backupCount=7)
  32. hand.setLevel(DEBUG)
  33. app.logger.addHandler(hand)
  34. app.logger.info(u"Flask server started " + asctime())
  35. app._hash_cache = {}
  36. def catch_errors(func):
  37. @wraps(func)
  38. def inner(*args, **kwargs):
  39. try:
  40. return func(*args, **kwargs)
  41. except TemplateError as exc:
  42. app.logger.error(u"Caught exception:\n{0}".format(exc.text))
  43. return render_template("error.mako", traceback=exc.text)
  44. except Exception:
  45. app.logger.exception(u"Caught exception:")
  46. return render_template("error.mako", traceback=format_exc())
  47. return inner
  48. @app.before_first_request
  49. def setup_app():
  50. cache.bot = Bot(".earwigbot", 100)
  51. cache.langs, cache.projects = [], []
  52. cache.last_sites_update = 0
  53. cache.background_data = {}
  54. cache.last_background_updates = {}
  55. oauth_config = cache.bot.config.wiki.get('copyvios', {}).get('oauth', {})
  56. if oauth_config.get('consumer_token') is None:
  57. raise ValueError("No OAuth consumer token is configured (config.wiki.copyvios.oauth.consumer_token).")
  58. if oauth_config.get('consumer_secret') is None:
  59. raise ValueError("No OAuth consumer secret is configured (config.wiki.copyvios.oauth.consumer_secret).")
  60. globalize(num_workers=8)
  61. @app.before_request
  62. def prepare_request():
  63. g._db = None
  64. g.cookies = parse_cookies(
  65. request.script_root or "/", request.environ.get("HTTP_COOKIE"))
  66. g.new_cookies = []
  67. @app.after_request
  68. def add_new_cookies(response):
  69. for cookie in g.new_cookies:
  70. response.headers.add("Set-Cookie", cookie)
  71. return response
  72. @app.after_request
  73. def write_access_log(response):
  74. msg = u"%s %s %s %s -> %s"
  75. app.logger.debug(msg, asctime(), request.method, request.path,
  76. request.values.to_dict(), response.status_code)
  77. return response
  78. @app.teardown_appcontext
  79. def close_databases(error):
  80. if g._db:
  81. g._db.close()
  82. def external_url_handler(error, endpoint, values):
  83. if endpoint == "static" and "file" in values:
  84. fpath = path.join(app.static_folder, values["file"])
  85. mtime = path.getmtime(fpath)
  86. cache = app._hash_cache.get(fpath)
  87. if cache and cache[0] == mtime:
  88. hashstr = cache[1]
  89. else:
  90. with open(fpath, "rb") as f:
  91. hashstr = md5(f.read()).hexdigest()
  92. app._hash_cache[fpath] = (mtime, hashstr)
  93. return "/static/{0}?v={1}".format(values["file"], hashstr)
  94. raise error
  95. app.url_build_error_handlers.append(external_url_handler)
  96. @app.route("/")
  97. @catch_errors
  98. def index():
  99. notice = get_notice()
  100. update_sites()
  101. query = do_check()
  102. if query.submitted and query.error == "not logged in":
  103. return redirect("/login?next=" + quote("/?" + request.query_string), 302)
  104. return render_template(
  105. "index.mako", notice=notice, query=query, result=query.result,
  106. turnitin_result=query.turnitin_result)
  107. @app.route("/login", methods=["GET", "POST"])
  108. @catch_errors
  109. def login():
  110. try:
  111. redirect_url = oauth_login_start() if request.method == "POST" else None
  112. if redirect_url:
  113. return redirect(redirect_url, 302)
  114. except Exception as e:
  115. app.log_exception(e)
  116. print e.message
  117. kwargs = {"error": e.message}
  118. else:
  119. if session.get("username") is not None:
  120. return redirect("/", 302)
  121. kwargs = {"error": request.args.get("error")}
  122. return render_template("login.mako", **kwargs)
  123. @app.route("/logout", methods=["GET", "POST"])
  124. @catch_errors
  125. def logout():
  126. if request.method == "POST":
  127. clear_login_session()
  128. return redirect("/", 302)
  129. else:
  130. return render_template("logout.mako")
  131. @app.route("/oauth-callback")
  132. @catch_errors
  133. def oauth_callback():
  134. try:
  135. next_url = oauth_login_end()
  136. except Exception as e:
  137. app.log_exception(e)
  138. return redirect("/login?error=" + quote_plus(e.message), 302)
  139. else:
  140. return redirect(next_url, 302)
  141. @app.route("/settings", methods=["GET", "POST"])
  142. @catch_errors
  143. def settings():
  144. status = process_settings() if request.method == "POST" else None
  145. update_sites()
  146. default = cache.bot.wiki.get_site()
  147. kwargs = {"status": status, "default_lang": default.lang,
  148. "default_project": default.project}
  149. return render_template("settings.mako", **kwargs)
  150. @app.route("/api")
  151. @catch_errors
  152. def api():
  153. return render_template("api.mako", help=True)
  154. @app.route("/api.json")
  155. @catch_errors
  156. def api_json():
  157. if not request.args:
  158. return render_template("api.mako", help=True)
  159. format = request.args.get("format", "json")
  160. if format in ["json", "jsonfm"]:
  161. update_sites()
  162. try:
  163. result = handle_api_request()
  164. except Exception as exc:
  165. result = format_api_error("unhandled_exception", exc)
  166. else:
  167. errmsg = u"Unknown format: '{0}'".format(format)
  168. result = format_api_error("unknown_format", errmsg)
  169. if format == "jsonfm":
  170. return render_template("api.mako", help=False, result=result)
  171. resp = make_response(dumps(result))
  172. resp.mimetype = "application/json"
  173. resp.headers["Access-Control-Allow-Origin"] = "*"
  174. return resp
  175. if __name__ == '__main__':
  176. app.run()