commit
ec3f34804f
@ -0,0 +1,3 @@
|
||||
__pycache__
|
||||
.idea
|
||||
*.pyc
|
@ -0,0 +1,73 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
import eventlet
|
||||
import eventlet.wsgi
|
||||
|
||||
import time
|
||||
import os
|
||||
import getopt
|
||||
import sys
|
||||
import datetime
|
||||
from flask import Flask, render_template, request, send_file, send_from_directory, safe_join, abort
|
||||
|
||||
# Local
|
||||
import store
|
||||
from nocache import nocache
|
||||
|
||||
datastore = store.ChangeDetectionStore()
|
||||
|
||||
app = Flask(__name__, static_url_path='/static')
|
||||
app.config['STATIC_RESOURCES'] = "/app/static"
|
||||
|
||||
# app.config['SECRET_KEY'] = 'secret!'
|
||||
|
||||
# Disables caching of the templates
|
||||
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
||||
|
||||
|
||||
@app.route("/", methods=['GET'])
|
||||
@nocache
|
||||
def main_page():
|
||||
return render_template("watch-overview.html", watches=datastore.data['watching'])
|
||||
|
||||
|
||||
@app.route("/static/<string:group>/<string:filename>", methods=['GET'])
|
||||
@nocache
|
||||
def static_content(group, filename):
|
||||
try:
|
||||
return send_from_directory("/app/static/{}".format(group), filename=filename)
|
||||
except FileNotFoundError:
|
||||
abort(404)
|
||||
|
||||
|
||||
def main(argv):
|
||||
ssl_mode = False
|
||||
port = 5000
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(argv, "sp:")
|
||||
except getopt.GetoptError:
|
||||
print('backend.py -s SSL enable -p [port]')
|
||||
sys.exit(2)
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt == '-s':
|
||||
ssl_mode = True
|
||||
|
||||
if opt == '-p':
|
||||
port = arg
|
||||
|
||||
# @todo finalise SSL config, but this should get you in the right direction if you need it.
|
||||
if ssl_mode:
|
||||
eventlet.wsgi.server(eventlet.wrap_ssl(eventlet.listen(('', port)),
|
||||
certfile='cert.pem',
|
||||
keyfile='privkey.pem',
|
||||
server_side=True), app)
|
||||
|
||||
else:
|
||||
eventlet.wsgi.server(eventlet.listen(('', port)), app)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
@ -0,0 +1,7 @@
|
||||
FROM python:3.8-buster
|
||||
RUN mkdir -p /app/dev
|
||||
COPY sleep.py /
|
||||
CMD [ "python", "/sleep.py" ]
|
||||
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
docker stop tss-node
|
||||
docker rm tss-node
|
||||
docker build -t tss-node .
|
||||
|
@ -0,0 +1,18 @@
|
||||
aiohttp==1.3.1
|
||||
async-timeout==1.1.0
|
||||
chardet==2.3.0
|
||||
multidict==2.1.4
|
||||
python-engineio
|
||||
python_socketio==1.8.4
|
||||
six==1.10.0
|
||||
yarl==0.9.2
|
||||
eventlet==0.19.0
|
||||
flask
|
||||
|
||||
# Actual connecting to services
|
||||
pytz
|
||||
phpserialize==1.3.0
|
||||
redis>=2.6.2
|
||||
pymysql==0.8
|
||||
bleach==1.5.0
|
||||
html5lib==0.9999999 # via bleach
|
@ -0,0 +1,9 @@
|
||||
import time
|
||||
import sys
|
||||
|
||||
|
||||
while True:
|
||||
# Wait for 5 seconds
|
||||
print('To stderr.', file=sys.stderr)
|
||||
|
||||
time.sleep(2)
|
@ -0,0 +1,14 @@
|
||||
|
||||
from flask import make_response
|
||||
from functools import wraps, update_wrapper
|
||||
from datetime import datetime
|
||||
|
||||
def nocache(view):
|
||||
@wraps(view)
|
||||
def no_cache(*args, **kwargs):
|
||||
response = make_response(view(*args, **kwargs))
|
||||
response.headers['hmm'] = datetime.now()
|
||||
|
||||
return response
|
||||
|
||||
return update_wrapper(no_cache, view)
|
@ -0,0 +1,24 @@
|
||||
aiohttp
|
||||
async-timeout
|
||||
chardet==2.3.0
|
||||
multidict
|
||||
python-engineio
|
||||
six==1.10.0
|
||||
yarl
|
||||
flask
|
||||
gevent-websocket
|
||||
eventlet
|
||||
requests
|
||||
|
||||
# Actual connecting to services
|
||||
pytz
|
||||
phpserialize==1.3.0
|
||||
redis>=2.6.2
|
||||
pymysql==0.8
|
||||
bleach==3.2.1
|
||||
html5lib==0.9999999 # via bleach
|
||||
|
||||
flask_socketio~=5.0
|
||||
|
||||
# @notes
|
||||
# - Dont install socketio, it interferes with flask_socketio
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* -- BASE STYLES --
|
||||
* Most of these are inherited from Base, but I want to change a few.
|
||||
*/
|
||||
body {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #1b98f8;
|
||||
}
|
||||
a.github-link {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
section.content {
|
||||
padding-top: 3em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import json
|
||||
import uuid
|
||||
# Is there an existing library to ensure some data store (JSON etc) is in sync with CRUD methods?
|
||||
# Open a github issue if you know something :)
|
||||
|
||||
class ChangeDetectionStore:
|
||||
|
||||
def __init__(self):
|
||||
try:
|
||||
with open('/datastore/url-watches.json') as json_file:
|
||||
self.data = json.load(json_file)
|
||||
for p in self.data['watching']:
|
||||
print('url: ' + p['url'])
|
||||
print('')
|
||||
|
||||
# First time ran, doesnt exist.
|
||||
except (FileNotFoundError, json.decoder.JSONDecodeError):
|
||||
print ("Resetting JSON store")
|
||||
|
||||
self.data = {}
|
||||
self.data['watching'] = []
|
||||
self.data['watching'].append({
|
||||
'url': 'https://changedetection.io',
|
||||
'tag': 'general',
|
||||
'uuid': str(uuid.uuid4())
|
||||
})
|
||||
|
||||
with open('/datastore/url-watches.json', 'w') as json_file:
|
||||
json.dump(self.data, json_file)
|
||||
|
||||
|
||||
|
||||
def sync_to_json(self):
|
||||
with open('/datastore/url-watches.json', 'w') as json_file:
|
||||
json.dump(self.data, json_file)
|
||||
|
||||
# body of the constructor
|
@ -0,0 +1,45 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="A layout example that shows off a responsive email layout.">
|
||||
<title>Email – Layout Examples – Pure</title>
|
||||
<link rel="stylesheet" href="/static/css/pure-min.css">
|
||||
<link rel="stylesheet" href="/static/css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
|
||||
<a class="pure-menu-heading" href="">ChangeDetection</a>
|
||||
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-item pure-menu-selected"><a class="github-link " href="https://github.com/"
|
||||
data-hotkey="g d" aria-label="Homepage "
|
||||
data-ga-click="Header, go to dashboard, icon:logo">
|
||||
<svg class="octicon octicon-mark-github v-align-middle" height="32" viewBox="0 0 16 16" version="1.1"
|
||||
width="32" aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
</svg>
|
||||
</a></li>
|
||||
<li class="pure-menu-item"><a href="#" class="pure-menu-link">Tour</a></li>
|
||||
<li class="pure-menu-item"><a href="#" class="pure-menu-link">Sign Up</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="content">
|
||||
<header>
|
||||
{% block header %}{% endblock %}
|
||||
</header>
|
||||
<!--
|
||||
{% for message in get_flashed_messages() %}
|
||||
<div class="flash">{{ message }}</div>
|
||||
{% endfor %}
|
||||
-->
|
||||
{% block content %}{% endblock %}
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,42 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="box">
|
||||
|
||||
<form class="pure-form">
|
||||
<fieldset>
|
||||
<legend>Add new change detection watch</legend>
|
||||
<input type="url" placeholder="https://..." />
|
||||
<input type="text" placeholder="tag" size="10" />
|
||||
<button type="submit" class="pure-button pure-button-primary">Save</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<table class="pure-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>URL</th>
|
||||
<th>Last Checked</th>
|
||||
<th>Status</th>
|
||||
<th>op</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
{% for watch in watches %}
|
||||
<tr class="pure-table-odd">
|
||||
<td>1</td>
|
||||
<td>{{ watch.url }}</td>
|
||||
<td>2021/2/2 14:00:00</td>
|
||||
<td>No Change</td>
|
||||
<td><button type="submit" class="pure-button pure-button-primary">Recheck</button> <button type="submit" class="pure-button pure-button-primary">Delete</button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,2 @@
|
||||
Empty dir, please keep, this is used to store your data!
|
||||
|
@ -0,0 +1 @@
|
||||
{"watching": [{"url": "https://changedetection.io", "tag": "general", "uuid": "3ba44e22-af8b-4dfd-8227-f9be55bd5788"}]}
|
@ -0,0 +1,18 @@
|
||||
version: "2"
|
||||
services:
|
||||
|
||||
backend:
|
||||
build: ./backend/dev-docker
|
||||
container_name: changedection-backend
|
||||
volumes:
|
||||
- ./backend:/app
|
||||
- ./datastore:/datastore
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:5000:5000"
|
||||
|
||||
networks:
|
||||
- changenet
|
||||
|
||||
networks:
|
||||
changenet:
|
Loading…
Reference in new issue