Plugin system for editors in lexonomy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

134 lines
3.5 KiB

  1. #!/usr/bin/python3
  2. """
  3. This serves lexonomy plugins
  4. This almost just returns static files,
  5. it only does search-and-replace for
  6. $LOCATION$ -> location of plugin
  7. every plugin is its own folder and if folder is
  8. "myplugin" and this website's url is "http://example.com",
  9. then $LOCATION$ gets the value of http://example.com/myplugin
  10. """
  11. import sys
  12. import os.path
  13. import mimetypes
  14. import string
  15. import random
  16. import redis
  17. from flask import Flask, Response, request
  18. app = Flask(__name__)
  19. redis = redis.Redis(host='localhost', port=6379, db=0)
  20. URL = "//plugins.lexonomy.cjvt.si"
  21. REPLACE_STRING = "$LOCATION$"
  22. def check_cache(full_path):
  23. file_time = int(os.path.getmtime(full_path))
  24. old_file_time = 0
  25. status = False
  26. if redis.exists(full_path + ":date"):
  27. try:
  28. old_file_time = int(redis.get(full_path + ":date"))
  29. status = old_file_time == file_time
  30. except ValueError:
  31. pass
  32. redis.set(full_path + ":date", file_time)
  33. return status
  34. def generate_etag(N=12):
  35. return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))
  36. def return_file(path, plugin=None):
  37. full_path = path if plugin is None else"{}/{}".format(plugin, path)
  38. if not os.path.isfile(full_path):
  39. return "File not found", 404
  40. mt, _encoding = mimetypes.guess_type(path)
  41. # if in cache, load etag and content, set status code to 304
  42. if check_cache(full_path):
  43. result = redis.get(full_path + ":content")
  44. etag = str(redis.get(full_path + ":etag"))
  45. status_code = 304
  46. # else, make search-and-replace and save to redis (+etag)
  47. else:
  48. status_code = 200
  49. etag = generate_etag()
  50. with open(full_path, 'rb') as fp:
  51. result = fp.read()
  52. with open(".err", "a") as fpw:
  53. print(result, file=fpw)
  54. print(len(result), file=fpw)
  55. if plugin is not None:
  56. replace_with = "{}/{}".format(URL, plugin)
  57. try:
  58. content = result.decode('UTF-8')
  59. content_replaced = content.replace(REPLACE_STRING, replace_with)
  60. result = content_replaced.encode('UTF-8')
  61. except UnicodeDecodeError:
  62. pass
  63. redis.set(full_path + ":content", result)
  64. redis.set(full_path + ":etag", etag)
  65. # set headers
  66. headers = {
  67. 'Access-Control-Allow-Origin': '*',
  68. 'ETag': etag
  69. }
  70. # if browser does not have a cached etag -> 200
  71. if 'If-None-Match' not in request.headers:
  72. status_code = 200
  73. # then check if it etag is None -> 200
  74. elif request.headers['If-None-Match'] == "None":
  75. status_code = 200
  76. # then check if it is the latest etag, else -> 200
  77. elif request.headers['If-None-Match'] != etag:
  78. status_code = 200
  79. return Response(result, mimetype=mt, headers=headers, status=status_code)
  80. @app.route("/")
  81. def root():
  82. result = "<html><body><h2>Description</h2><pre>"
  83. result += sys.modules[__name__].__doc__
  84. result += "</pre><h2>Url</h2><p>"
  85. result += URL
  86. result += "</p></body></html>"
  87. return result
  88. @app.route("/plugin-loader.js")
  89. def plugin_loader():
  90. return return_file("./plugin-loader.js")
  91. @app.route("/<plugin>")
  92. def plugin_root(plugin):
  93. status = "" if os.path.isdir(plugin) else " NOT"
  94. return "Plugin {} was{} found".format(plugin, status)
  95. @app.route("/<plugin>/<path:path>")
  96. def plugin_file(plugin, path):
  97. return return_file(path, plugin=plugin)
  98. if __name__ == '__main__':
  99. app.run(host="0.0.0.0")