WWW service status tracker

By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 app.py

View raw Download
text/x-script.python • 8.5 kiB
Python script, ASCII text executable
        
            
1
import datetime
2
3
import celery
4
import flask
5
from flask_sqlalchemy import SQLAlchemy
6
from flask_bcrypt import Bcrypt
7
from flask_migrate import Migrate
8
9
from sqlalchemy.orm import declarative_base
10
from celery import shared_task
11
import httpx
12
13
app = flask.Flask(__name__)
14
from celery import Celery, Task
15
16
17
def celery_init_app(app_) -> Celery:
18
class FlaskTask(Task):
19
def __call__(self, *args: object, **kwargs: object) -> object:
20
with app_.app_context():
21
return self.run(*args, **kwargs)
22
23
celery_app = Celery(app_.name, task_cls=FlaskTask)
24
celery_app.config_from_object(app_.config["CELERY"])
25
celery_app.set_default()
26
app_.extensions["celery"] = celery_app
27
return celery_app
28
29
30
app.config.from_mapping(
31
CELERY=dict(
32
broker_url="redis://localhost:6379",
33
result_backend="redis://localhost:6379"
34
),
35
)
36
celery_app = celery_init_app(app)
37
38
app.config["SQLALCHEMY_DATABASE_URI"] = \
39
"postgresql://echo:1234@localhost:5432/echo"
40
db = SQLAlchemy(app)
41
bcrypt = Bcrypt(app)
42
migrate = Migrate(app, db)
43
app.config["SESSION_TYPE"] = "filesystem"
44
app.config["SECRET_KEY"] = "super secret"
45
46
with app.app_context():
47
class User(db.Model):
48
username = db.Column(db.String(64), unique=True, nullable=False, primary_key=True)
49
password = db.Column(db.String(72), nullable=False)
50
admin = db.Column(db.Boolean, nullable=False, default=False)
51
52
applications = db.relationship("Application", back_populates="owner")
53
54
def __init__(self, username, password, admin=False):
55
self.username = username
56
self.password = bcrypt.generate_password_hash(password).decode("utf-8")
57
self.admin = admin
58
59
class Application(db.Model):
60
id = db.Column(db.Integer, primary_key=True, autoincrement=True, unique=True, default=0)
61
name = db.Column(db.String(64), unique=True, nullable=False)
62
owner_name = db.Column(db.String(64), db.ForeignKey("user.username"), nullable=False)
63
64
owner = db.relationship("User", back_populates="applications")
65
66
endpoints = db.relationship("Endpoint", back_populates="application")
67
68
def __init__(self, name, owner):
69
self.name = name
70
self.owner_name = owner.username
71
72
class Endpoint(db.Model):
73
id = db.Column(db.Integer, unique=True, nullable=False, primary_key=True, autoincrement=True)
74
application_id = db.Column(db.Integer, db.ForeignKey("application.id"), nullable=False)
75
address = db.Column(db.String(2048), nullable=False)
76
name = db.Column(db.String(64), nullable=False)
77
comment = db.Column(db.String(2048), nullable=True)
78
ping_interval = db.Column(db.Integer, default=300, nullable=False)
79
80
application = db.relationship("Application", back_populates="endpoints")
81
82
def __init__(self, application, name, address, ping_interval, comment=""):
83
self.application_id = application.id
84
self.name = name
85
self.address = address
86
self.comment = comment
87
self.ping_interval = ping_interval
88
89
90
class Status(db.Model):
91
id = db.Column(db.Integer, unique=True, nullable=False, autoincrement=True, primary_key=True, default=0)
92
endpoint_id = db.Column(db.Integer, nullable=False)
93
time = db.Column(db.DateTime, default=datetime.datetime.utcnow)
94
95
status = db.Column(db.SmallInteger, nullable=False)
96
97
def __init__(self, endpoint_id, status):
98
self.endpoint_id = endpoint_id
99
self.status = status
100
101
102
@celery.shared_task(name="ping")
103
def ping(id, address, next_ping):
104
url = address
105
print(f"Pinging {url}")
106
response = httpx.get(url, verify=False)
107
reading = Status(id, response.status_code)
108
db.session.add(reading)
109
db.session.commit()
110
111
# Schedule the next ping
112
ping.apply_async(args=(id, address, next_ping), countdown=next_ping)
113
114
@celery.shared_task(name="ping_all")
115
def ping_all():
116
endpoints = Endpoint.query.all()
117
for endpoint in endpoints:
118
ping.delay(endpoint.id, endpoint.address, endpoint.ping_interval)
119
120
121
task = ping_all.delay()
122
123
print()
124
print()
125
print(task)
126
print()
127
print()
128
129
130
@app.context_processor
131
def default():
132
return {
133
"session": flask.session,
134
}
135
136
137
@app.route("/")
138
def dashboard():
139
return flask.render_template("dashboard.html", apps=Application.query.all())
140
141
142
@app.route("/login", methods=["GET"])
143
def login():
144
return flask.render_template("login.html")
145
146
147
@app.route("/signup", methods=["GET"])
148
def signup():
149
return flask.render_template("signup.html")
150
151
152
@app.route("/new-app", methods=["GET"])
153
def new_app():
154
if not flask.session.get("username"):
155
return flask.redirect("/login", code=303)
156
return flask.render_template("new-app.html")
157
158
159
@app.route("/new-app", methods=["POST"])
160
def new_app_post():
161
if not flask.session.get("username"):
162
return flask.redirect("/login", code=303)
163
if Application.query.filter_by(name=flask.request.form["name"]).first():
164
flask.flash("Application already exists")
165
return flask.redirect("/new-app", code=303)
166
167
new_app_ = Application(
168
flask.request.form["name"],
169
db.session.get(User, flask.session["username"]),
170
)
171
db.session.add(new_app_)
172
db.session.commit()
173
return flask.redirect("/", code=303)
174
175
176
@app.route("/login", methods=["POST"])
177
def login_post():
178
user = db.session.get(User, flask.request.form["username"])
179
if not user:
180
flask.flash("Username doesn't exist")
181
return flask.redirect("/signup", code=303)
182
if not bcrypt.check_password_hash(user.password, flask.request.form["password"]):
183
flask.flash("Wrong password")
184
return flask.redirect("/signup", code=303)
185
186
flask.session["username"] = user.username
187
return flask.redirect("/", code=303)
188
189
190
@app.route("/logout")
191
def logout():
192
flask.session.pop("username", None)
193
return flask.redirect("/", code=303)
194
195
196
@app.route("/signup", methods=["POST"])
197
def signup_post():
198
if flask.request.form["password"] != flask.request.form["password2"]:
199
flask.flash("Passwords do not match")
200
return flask.redirect("/signup", code=303)
201
if db.session.get(User, flask.request.form["username"]):
202
flask.flash("Username already exists")
203
return flask.redirect("/signup", code=303)
204
if len(flask.request.form["password"]) < 8:
205
flask.flash("Password must be at least 8 characters")
206
return flask.redirect("/signup", code=303)
207
if len(flask.request.form["username"]) < 4:
208
flask.flash("Username must be at least 4 characters")
209
return flask.redirect("/signup", code=303)
210
211
new_user = User(
212
flask.request.form["username"],
213
flask.request.form["password"],
214
)
215
db.session.add(new_user)
216
db.session.commit()
217
flask.session["username"] = new_user.username
218
return flask.redirect("/", code=303)
219
220
221
@app.route("/app/<int:app_id>/")
222
def app_info(app_id):
223
app_ = db.session.get(Application, app_id)
224
return flask.render_template("app.html", app=app_)
225
226
227
@app.route("/app/<int:app_id>/edit/")
228
def app_editor(app_id):
229
if flask.session.get("username") != db.session.get(Application, app_id).owner_name:
230
flask.abort(403)
231
app_ = db.session.get(Application, app_id)
232
return flask.render_template("app-editor.html", app=app_)
233
234
235
@app.route("/app/<int:app_id>/edit/<int:endpoint_id>", methods=["POST"])
236
def endpoint_edit(app_id, endpoint_id):
237
if flask.session.get("username") != db.session.get(Application, app_id).owner_name:
238
flask.abort(403)
239
endpoint = db.session.get(Endpoint, endpoint_id)
240
if flask.request.form.get("delete") == "delete":
241
db.session.delete(endpoint)
242
db.session.commit()
243
else:
244
endpoint.name = flask.request.form["name"]
245
endpoint.address = flask.request.form["url"]
246
endpoint.comment = flask.request.form["comment"]
247
db.session.commit()
248
return flask.redirect(f"/app/{app_id}/edit", code=303)
249
250
251
@app.route("/app/<int:app_id>/add-endpoint", methods=["POST"])
252
def app_add_endpoint(app_id):
253
if flask.session.get("username") != db.session.get(Application, app_id).owner_name:
254
flask.abort(403)
255
app_ = db.session.get(Application, app_id)
256
endpoint = Endpoint(app_,
257
flask.request.form["name"],
258
flask.request.form["url"],
259
300,
260
flask.request.form["comment"])
261
db.session.add(endpoint)
262
db.session.commit()
263
return flask.redirect(f"/app/{app_id}/edit", code=303)
264
265