mirror of
https://github.com/chubin/cheat.sh.git
synced 2024-11-23 10:35:29 +03:00
queries logging, github buttons, ?T and ?q
This commit is contained in:
parent
c4b197e71b
commit
0609f0fb23
50
bin/srv.py
50
bin/srv.py
@ -27,7 +27,7 @@ app = Flask(__name__)
|
||||
MYDIR = os.path.abspath(os.path.dirname( os.path.dirname('__file__') ))
|
||||
sys.path.append("%s/lib/" % MYDIR)
|
||||
|
||||
from globals import LOG_FILE, TEMPLATES, STATIC, log, error
|
||||
from globals import FILE_QUERIES_LOG, LOG_FILE, TEMPLATES, STATIC, log, error
|
||||
|
||||
from cheat_wrapper import cheat_wrapper, save_cheatsheet
|
||||
|
||||
@ -47,6 +47,31 @@ def is_html_needed(user_agent):
|
||||
return False
|
||||
return True
|
||||
|
||||
def parse_args(args):
|
||||
result = {}
|
||||
|
||||
q = ""
|
||||
for key, val in args.items():
|
||||
if len(val) == 0:
|
||||
q += key
|
||||
continue
|
||||
|
||||
if q is None:
|
||||
return result
|
||||
if 'T' in q:
|
||||
result['no-terminal'] = True
|
||||
if 'q' in q:
|
||||
result['quiet'] = True
|
||||
|
||||
for key, val in args.items():
|
||||
if val == 'True':
|
||||
val = True
|
||||
if val == 'False':
|
||||
val = False
|
||||
result[key] = val
|
||||
|
||||
return result
|
||||
|
||||
@app.route('/files/<path:path>')
|
||||
def send_static(path):
|
||||
return send_from_directory(STATIC, path)
|
||||
@ -59,6 +84,11 @@ def send_favicon():
|
||||
def send_malformed():
|
||||
return send_from_directory(STATIC, 'malformed-response.html')
|
||||
|
||||
def log_query(ip, found, topic, user_agent):
|
||||
log_entry = "%s %s %s %s" % (ip, found, topic, user_agent)
|
||||
with open(FILE_QUERIES_LOG, 'a') as my_file:
|
||||
my_file.write(log_entry+"\n")
|
||||
|
||||
@app.route("/", methods=['GET', 'POST'])
|
||||
@app.route("/<path:topic>", methods=["GET", "POST"])
|
||||
def answer(topic = None):
|
||||
@ -76,6 +106,21 @@ def answer(topic = None):
|
||||
|
||||
user_agent = request.headers.get('User-Agent', '').lower()
|
||||
html_needed = is_html_needed(user_agent)
|
||||
options = parse_args(request.args)
|
||||
|
||||
if request.headers.getlist("X-Forwarded-For"):
|
||||
ip = request.headers.getlist("X-Forwarded-For")[0]
|
||||
if ip.startswith('::ffff:'):
|
||||
ip = ip[7:]
|
||||
else:
|
||||
ip = request.remote_addr
|
||||
if request.headers.getlist("X-Forwarded-For"):
|
||||
ip = request.headers.getlist("X-Forwarded-For")[0]
|
||||
if ip.startswith('::ffff:'):
|
||||
ip = ip[7:]
|
||||
else:
|
||||
ip = request.remote_addr
|
||||
|
||||
|
||||
if request.method == 'POST':
|
||||
for k, v in request.form.items():
|
||||
@ -109,8 +154,9 @@ def answer(topic = None):
|
||||
if topic is None:
|
||||
topic = ":firstpage"
|
||||
|
||||
answer = cheat_wrapper(topic, html=is_html_needed(user_agent))
|
||||
answer, found = cheat_wrapper(topic, request_options=options, html=is_html_needed(user_agent))
|
||||
|
||||
log_query(ip, found, topic, user_agent)
|
||||
return answer
|
||||
|
||||
server = WSGIServer(("", 8002), app) # log=None)
|
||||
|
@ -198,14 +198,26 @@ def split_paragraphs(text):
|
||||
return answer
|
||||
|
||||
def paragraph_contains(paragraph, keyword, insensitive=False, word_boundaries=True):
|
||||
regex = re.escape(keyword)
|
||||
if not word_boundaries:
|
||||
regex = r"\b%s\b" % keyword
|
||||
"""
|
||||
Several keywords can be joined together using ~
|
||||
For example: ~ssh~passphrase
|
||||
"""
|
||||
answer = True
|
||||
|
||||
if insensitive:
|
||||
answer = bool(re.search(regex, paragraph, re.IGNORECASE))
|
||||
if '~' in keyword:
|
||||
keywords = keyword.split('~')
|
||||
else:
|
||||
answer = bool(re.search(regex, paragraph))
|
||||
keywords = [keyword]
|
||||
|
||||
for keyword in keywords:
|
||||
regex = re.escape(keyword)
|
||||
if not word_boundaries:
|
||||
regex = r"\b%s\b" % keyword
|
||||
|
||||
if insensitive:
|
||||
answer = answer and bool(re.search(regex, paragraph, re.IGNORECASE))
|
||||
else:
|
||||
answer = answer and bool(re.search(regex, paragraph))
|
||||
|
||||
return answer
|
||||
|
||||
@ -322,9 +334,39 @@ def colorize_internal(topic, answer, html_needed):
|
||||
|
||||
return answer
|
||||
|
||||
def github_button(topic_type):
|
||||
repository = {
|
||||
"cheat.sheets": 'chubin/cheat.sheets',
|
||||
"cheat.sheets dir": 'chubin/cheat.sheets',
|
||||
"tldr": 'tldr-pages/tldr',
|
||||
"internal": '',
|
||||
"cheat": 'chrisallenlane/cheat',
|
||||
"search": '',
|
||||
"internal": '',
|
||||
"unknown": '',
|
||||
}
|
||||
|
||||
full_name = repository.get(topic_type, '')
|
||||
if not full_name:
|
||||
return ''
|
||||
|
||||
short_name = full_name.split('/',1)[1]
|
||||
button = (
|
||||
"<!-- Place this tag where you want the button to render. -->"
|
||||
'<a aria-label="Star %(full_name)s on GitHub" data-count-aria-label="# stargazers on GitHub"'
|
||||
' data-count-api="/repos/%(full_name)s#stargazers_count"'
|
||||
' data-count-href="/%(full_name)s/stargazers"'
|
||||
' data-icon="octicon-star"'
|
||||
' href="https://github.com/%(full_name)s"'
|
||||
' class="github-button">%(short_name)s</a>'
|
||||
) % locals()
|
||||
return button
|
||||
|
||||
#
|
||||
|
||||
def cheat_wrapper(query, highlight=True, html=False):
|
||||
def cheat_wrapper(query, request_options=None, html=False):
|
||||
|
||||
highlight = not bool(request_options and request_options.get('no-terminal'))
|
||||
|
||||
keyword = None
|
||||
if '~' in query:
|
||||
@ -346,6 +388,8 @@ def cheat_wrapper(query, highlight=True, html=False):
|
||||
search_mode = False
|
||||
|
||||
|
||||
found = True # if the page was found in the database
|
||||
editable = False # can generated page be edited on github (only cheat.sheets pages can)
|
||||
result = ""
|
||||
for topic, answer in answers:
|
||||
|
||||
@ -357,6 +401,9 @@ def cheat_wrapper(query, highlight=True, html=False):
|
||||
highlight = False
|
||||
|
||||
topic_type = get_topic_type(topic)
|
||||
if topic_type == 'unknown':
|
||||
found = False
|
||||
|
||||
if highlight:
|
||||
if topic_type.endswith(" dir"):
|
||||
pass
|
||||
@ -376,19 +423,29 @@ def cheat_wrapper(query, highlight=True, html=False):
|
||||
else:
|
||||
answer = pygments_highlight(answer, BashLexer(), Terminal256Formatter(style='native'))
|
||||
|
||||
if topic_type == "cheat.sheets":
|
||||
editable = True
|
||||
|
||||
if search_mode:
|
||||
result += "\n%s%s %s %s%s\n" % (colored.bg('dark_gray'), colored.attr("res_underlined"), topic, colored.attr("res_underlined"), colored.attr('reset'))
|
||||
if highlight:
|
||||
result += "\n%s%s %s %s%s\n" % (colored.bg('dark_gray'), colored.attr("res_underlined"), topic, colored.attr("res_underlined"), colored.attr('reset'))
|
||||
else:
|
||||
result += "[%s]" % topic
|
||||
|
||||
result += answer
|
||||
|
||||
if search_mode:
|
||||
result = result[1:]
|
||||
editable = False
|
||||
repository_button = ''
|
||||
else:
|
||||
repository_button = github_button(topic_type)
|
||||
|
||||
if html:
|
||||
result = result + "\n$"
|
||||
result = html_wrapper(result)
|
||||
title = "<title>cheat.sh/%s</title>" % topic
|
||||
title += '\n<link rel="stylesheet" href="/files/awesomplete.css" /><script src="/files/awesomplete.min.js" async></script>'
|
||||
# title += '\n<link rel="stylesheet" href="/files/awesomplete.css" />script src="/files/awesomplete.min.js" async></script>'
|
||||
# submit button: thanks to http://stackoverflow.com/questions/477691/
|
||||
submit_button = '<input type="submit" style="position: absolute; left: -9999px; width: 1px; height: 1px;" tabindex="-1" />'
|
||||
topic_list = """
|
||||
@ -397,10 +454,18 @@ def cheat_wrapper(query, highlight=True, html=False):
|
||||
""" % ("\n".join("<option value='%s'></option>" % x for x in get_topics_list()))
|
||||
|
||||
curl_line = "<span class='pre'>$ curl cheat.sh/</span>"
|
||||
form_html = '<form action="/" method="GET"/>%s%s<input type="text" value="%s" name="topic" list="topics" class="awesomplete_" autofocus autocomplete="off"/>%s</form>' % (submit_button, curl_line, query, topic_list)
|
||||
result = re.sub("<pre>", form_html + "<pre>", result)
|
||||
if query == ':firstpage':
|
||||
query = ""
|
||||
form_html = '<form action="/" method="GET"/>%s%s<input type="text" value="%s" name="topic" list="topics" autofocus autocomplete="off"/>%s</form>' % (submit_button, curl_line, query, topic_list)
|
||||
|
||||
edit_button = ''
|
||||
if editable:
|
||||
edit_page_link = 'https://github.com/chubin/cheat.sheets/edit/master/sheets/' + topic
|
||||
edit_button = '<pre style="position:absolute;padding-left:40em;overflow:visible;height:0;">[<a href="%s" style="color:cyan">edit</a>]</pre>' % edit_page_link
|
||||
result = re.sub("<pre>", edit_button + form_html + "<pre>", result)
|
||||
result = re.sub("<head>", "<head>" + title, result)
|
||||
result = result.replace('</body>', TWITTER_BUTTON + GITHUB_BUTTON + GITHUB_BUTTON_2 + GITHUB_BUTTON_FOOTER + '</body>')
|
||||
if not request_options.get('quiet'):
|
||||
result = result.replace('</body>', TWITTER_BUTTON + GITHUB_BUTTON + repository_button + GITHUB_BUTTON_FOOTER + '</body>')
|
||||
|
||||
return result
|
||||
return result, found
|
||||
|
||||
|
@ -6,6 +6,7 @@ MYDIR = os.path.abspath(os.path.dirname( os.path.dirname('__file__') ))
|
||||
ANSI2HTML = os.path.join( MYDIR, "share/ansi2html.sh" )
|
||||
|
||||
LOG_FILE = os.path.join( MYDIR, 'log/main.log' )
|
||||
FILE_QUERIES_LOG = os.path.join( MYDIR, 'log/queries.log' )
|
||||
TEMPLATES = os.path.join( MYDIR, 'share/templates' )
|
||||
STATIC = os.path.join( MYDIR, 'share/static' )
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user