about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-07-09 20:14:27 -0400
committerJune McEnroe <june@causal.agency>2020-07-09 20:14:27 -0400
commitfb214d0c8096a345f5da30900d9ced0aa777cdc3 (patch)
tree8458f7beca535e49ed531880b32fefc194d7de8b
parentAdd events page stub (diff)
downloadscooper-fb214d0c8096a345f5da30900d9ced0aa777cdc3.tar.gz
scooper-fb214d0c8096a345f5da30900d9ced0aa777cdc3.zip
Implement very basic events listing
-rw-r--r--contexts.c5
-rw-r--r--events.c78
-rw-r--r--server.c3
-rw-r--r--server.h11
4 files changed, 90 insertions, 7 deletions
diff --git a/contexts.c b/contexts.c
index c75202a..d432cb1 100644
--- a/contexts.c
+++ b/contexts.c
@@ -29,7 +29,7 @@ const char *ContextsQuery = SQL(
 );
 
 enum kcgi_err pageContexts(struct kreq *req) {
-	if (!req->fieldmap[Network]) return httpFail(req, KHTTP_404);
+	if (!req->fieldmap[Network]) return httpFail(req, KHTTP_400);
 	enum kcgi_err error = httpHead(req, KHTTP_200, KMIME_TEXT_HTML);
 	if (req->method == KMETHOD_HEAD) return error;
 
@@ -59,7 +59,8 @@ enum kcgi_err pageContexts(struct kreq *req) {
 		);
 		if (!href) err(EX_OSERR, "khttp_urlpart");
 
-		error = khtml_elem(&html, KELEM_LI)
+		error = 0
+			|| khtml_elem(&html, KELEM_LI)
 			|| khtml_attr(&html, KELEM_A, KATTR_HREF, href, KATTR__MAX)
 			|| khtml_puts(&html, context)
 			|| khtml_closeelem(&html, 2);
diff --git a/events.c b/events.c
index 9262225..04fdbf8 100644
--- a/events.c
+++ b/events.c
@@ -21,9 +21,59 @@
 
 #include "server.h"
 
+const char *EventsAfterQuery = SQL(
+	SELECT
+		events.event,
+		events.time,
+		events.type,
+		names.nick,
+		names.user,
+		names.host,
+		events.target,
+		events.message
+	FROM events
+	JOIN contexts USING (context)
+	JOIN names USING (name)
+	WHERE contexts.network = :network
+		AND contexts.name = :context
+		AND coalesce(contexts.query = :query, true)
+		AND events.time >= strftime('%s', :time)
+	ORDER BY events.time
+	LIMIT :limit;
+);
+
+const char *EventsBeforeQuery = SQL(
+	WITH before AS (
+		SELECT
+			events.event,
+			events.time,
+			events.type,
+			names.nick,
+			names.user,
+			names.host,
+			events.target,
+			events.message
+		FROM events
+		JOIN contexts USING (context)
+		JOIN names USING (name)
+		WHERE contexts.network = :network
+			AND contexts.name = :context
+			AND coalesce(contexts.query = :query, true)
+			AND events.time < strftime('%s', :time)
+		ORDER BY events.time DESC
+		LIMIT :limit
+	)
+	SELECT *
+	FROM before
+	ORDER BY time;
+);
+
 enum kcgi_err pageEvents(struct kreq *req) {
 	if (!req->fieldmap[Network] || !req->fieldmap[Context]) {
-		return httpFail(req, KHTTP_404);
+		return httpFail(req, KHTTP_400);
+	}
+	if (!req->fieldmap[After] && !req->fieldmap[Before]) {
+		return httpFail(req, KHTTP_400);
 	}
 
 	enum kcgi_err error = httpHead(req, KHTTP_200, KMIME_TEXT_HTML);
@@ -31,6 +81,9 @@ enum kcgi_err pageEvents(struct kreq *req) {
 
 	const char *network = req->fieldmap[Network]->parsed.s;
 	const char *context = req->fieldmap[Context]->parsed.s;
+	const char *time = req->fieldmap[Before]
+		? req->fieldmap[Before]->parsed.s
+		: req->fieldmap[After]->parsed.s;
 
 	struct khtmlreq html;
 	error = error
@@ -41,5 +94,26 @@ enum kcgi_err pageEvents(struct kreq *req) {
 		|| htmlSearch(&html, network, context);
 	if (error) return error;
 
-	return khtml_close(&html);
+	sqlite3_stmt *events = stmt.eventsAfter;
+	if (req->fieldmap[Before]) events = stmt.eventsBefore;
+
+	dbBindText(events, ":network", network);
+	dbBindText(events, ":context", context);
+	if (pagePublic) dbBindInt(events, ":query", false);
+	dbBindText(events, ":time", time);
+	dbBindInt(events, ":limit", pageLimit);
+
+	int result;
+	while (SQLITE_ROW == (result = sqlite3_step(events))) {
+		const char *msg = (const char *)sqlite3_column_text(events, 7);
+		if (!msg) continue;
+		error = 0
+			|| khtml_puts(&html, msg)
+			|| khtml_elem(&html, KELEM_BR);
+		if (error) break;
+	}
+	if (result != SQLITE_DONE) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db));
+	sqlite3_reset(events);
+
+	return error || khtml_close(&html);
 }
diff --git a/server.c b/server.c
index de18e7d..3dcdea3 100644
--- a/server.c
+++ b/server.c
@@ -46,6 +46,7 @@ const struct kvalid Keys[KeysLen] = {
 };
 
 bool pagePublic;
+int pageLimit = 100;
 
 static enum kcgi_err request(struct kreq *req) {
 	if (req->method != KMETHOD_HEAD && req->method != KMETHOD_GET) {
@@ -94,6 +95,8 @@ int main(int argc, char *argv[]) {
 
 	prepare(&stmt.networks, NetworksQuery);
 	prepare(&stmt.contexts, ContextsQuery);
+	prepare(&stmt.eventsAfter, EventsAfterQuery);
+	prepare(&stmt.eventsBefore, EventsBeforeQuery);
 	
 	if (fastCGI) {
 		struct kfcgi *fcgi;
diff --git a/server.h b/server.h
index 9b665d7..0e77f7c 100644
--- a/server.h
+++ b/server.h
@@ -37,20 +37,23 @@ extern sqlite3 *db;
 
 extern const char *NetworksQuery;
 extern const char *ContextsQuery;
-extern const char *EventsQuery;
+extern const char *EventsAfterQuery;
+extern const char *EventsBeforeQuery;
 extern const char *SearchQuery;
 
 extern struct Statements {
 	sqlite3_stmt *networks;
 	sqlite3_stmt *contexts;
-	sqlite3_stmt *events;
+	sqlite3_stmt *eventsAfter;
+	sqlite3_stmt *eventsBefore;
 	sqlite3_stmt *search;
 } stmt;
 
 static inline void dbClose(void) {
 	if (stmt.networks) sqlite3_finalize(stmt.networks);
 	if (stmt.contexts) sqlite3_finalize(stmt.contexts);
-	if (stmt.events) sqlite3_finalize(stmt.events);
+	if (stmt.eventsAfter) sqlite3_finalize(stmt.eventsAfter);
+	if (stmt.eventsBefore) sqlite3_finalize(stmt.eventsBefore);
 	if (stmt.search) sqlite3_finalize(stmt.search);
 	sqlite3_close(db);
 }
@@ -92,6 +95,7 @@ extern const char *Pages[PagesLen];
 	X(Network, "network", kvalid_stringne) \
 	X(Context, "context", kvalid_stringne) \
 	X(After, "after", kvalid_stringne) \
+	X(Before, "before", kvalid_stringne) \
 	X(Query, "query", kvalid_stringne)
 
 enum {
@@ -104,6 +108,7 @@ enum {
 extern const struct kvalid Keys[KeysLen];
 
 extern bool pagePublic;
+extern int pageLimit;
 
 enum kcgi_err pageNetworks(struct kreq *req);
 enum kcgi_err pageContexts(struct kreq *req);