diff --git a/crystallib/webserver/heroweb/analytics_controller.v b/crystallib/webserver/heroweb/analytics_controller.v deleted file mode 100644 index 87328e99f..000000000 --- a/crystallib/webserver/heroweb/analytics_controller.v +++ /dev/null @@ -1,31 +0,0 @@ -module heroweb - -import json -import veb -import freeflowuniverse.crystallib.core.pathlib -import freeflowuniverse.crystallib.webserver.log {Log} - -@[POST; '/analytics/:name'] -pub fn (mut app App) analytics_document_post(mut ctx Context, name string) veb.Result { - infoptr := app.db.infopointers[name] or { - return ctx.not_found() - } - - new_log := json.decode(Log, ctx.req.data) or { - return ctx.server_error(err.str()) - } - app.db.logger.new_log(Log{...new_log, subject: ctx.user_id.str()}) or { - return ctx.server_error(err.str()) - } - - return ctx.json('ok') -} - -@['/analytics/:name'] -pub fn (mut app App) analytics_document_get(mut ctx Context, name string) veb.Result { - logs := app.db.logger.get_logs(object: name) or { - return ctx.not_found() - } - - return ctx.json(logs) -} diff --git a/crystallib/webserver/heroweb/assets_controller.v b/crystallib/webserver/heroweb/assets_controller.v deleted file mode 100644 index 1bf99dbd5..000000000 --- a/crystallib/webserver/heroweb/assets_controller.v +++ /dev/null @@ -1,39 +0,0 @@ -module heroweb - -import veb -import freeflowuniverse.crystallib.core.pathlib - -@['/assets'] -pub fn (mut app App) assets(mut ctx Context) veb.Result { - ptrs := app.db.authorized_ptrs(ctx.user_id, .read) or { - return ctx.server_error(err.str()) - } - docs := ptrs.map(Document{ - title: it.name.title() - description: it.description - url: '/view/asset/${it.name}' - }) - - return ctx.json(docs) -} - -// @['/document/:name'] -// pub fn (app &App) document(mut ctx Context, name string) veb.Result { -// infoptr := app.db.infopointers[name] or { -// return ctx.not_found() -// } - -// path := pathlib.get(infoptr.path_content) - -// file := if path.is_file() { -// path -// } else { -// // mount the folder 'static' at path '/public', the path has to start with '/' -// pathlib.get_file(path: '${path.path}/index.html') or { -// return ctx.server_error(err.str()) -// } -// } - -// ctx.set_content_type('application/pdf') -// return ctx.file(file.path) -// } diff --git a/crystallib/webserver/heroweb/controller_assets.v b/crystallib/webserver/heroweb/controller_assets.v new file mode 100644 index 000000000..34a10d41f --- /dev/null +++ b/crystallib/webserver/heroweb/controller_assets.v @@ -0,0 +1,61 @@ +module heroweb + +import veb +import freeflowuniverse.crystallib.core.pathlib +import freeflowuniverse.crystallib.webserver.log +import time + +@['/assets'] +pub fn (mut app App) assets(mut ctx Context) veb.Result { + ptrs := app.db.authorized_ptrs(ctx.user_id, .read) or { + return ctx.server_error(err.str()) + } + docs := ptrs.map(Document{ + title: it.name.title() + description: it.description + url: '/view/asset/${it.name}' + }) + + return ctx.json(docs) +} + +// this endpoint serves assets dynamically +@['/asset/:paths...'] +pub fn (app &App) asset(mut ctx Context, paths string) veb.Result { + name := paths.all_before('/') + infoptr := app.db.infopointers[name] or { + return ctx.not_found() + } + + mut path := pathlib.get('${infoptr.path_content}${paths.all_after(name)}') + println('debugzopath ${path}') + + logger := log.new('logger.sqlite') or { + return ctx.server_error(err.str()) + } + // if infoptr.cat == .website && ctx.req.url.ends_with('.html') { + // logger.new_log(log.Log{ + // object: infoptr.name + // timestamp: time.now() + // subject: ctx.user_id.str() + // message: 'access path ${ctx.req.url}' + // event: 'view' + // }) or { + // return ctx.server_error(err.str()) + // } + // } + // println('debugzopath ${infoptr}') + + file := if path.is_file() { + path + } else { + // mount the folder 'static' at path '/public', the path has to start with '/' + pathlib.get_file(path: '${path.path}/index.html') or { + return ctx.server_error(err.str()) + } + } + println('debugzopath2 ${file}') + + // ctx.set_content_type('application/pdf') + return ctx.file(file.path) +} diff --git a/crystallib/webserver/heroweb/authentication_controller.v b/crystallib/webserver/heroweb/controller_authentication.v similarity index 100% rename from crystallib/webserver/heroweb/authentication_controller.v rename to crystallib/webserver/heroweb/controller_authentication.v diff --git a/crystallib/webserver/heroweb/controller_log.v b/crystallib/webserver/heroweb/controller_log.v new file mode 100644 index 000000000..d89074b8e --- /dev/null +++ b/crystallib/webserver/heroweb/controller_log.v @@ -0,0 +1,39 @@ +module heroweb + +import json +import veb +import time +import freeflowuniverse.crystallib.core.pathlib +import freeflowuniverse.crystallib.webserver.log + +@[POST; '/log/:name'] +pub fn (mut app App) log_post(mut ctx Context, name string) veb.Result { + infoptr := app.db.infopointers[name] or { + return ctx.not_found() + } + + // new_log := json.decode(log.Log, ctx.req.data) or { + // return ctx.server_error(err.str()) + // } + // app.db.new_log(log.Log{...new_log, + // subject: ctx.user_id.str() + // timestamp: time.now() + // }) or { + // return ctx.server_error(err.str()) + // } + + return ctx.json('ok') +} + +@[GET; '/logs'] +pub fn (mut app App) logs(mut ctx Context) veb.Result { + // logs := app.db.filter_logs( + // event: ctx.query['event'] or { '' } + // subject: ctx.query['subject'] or { '' } + // object: ctx.query['object'] or { '' } + // ) or { + // return ctx.server_error(err.str()) + // } + + return ctx.json('logs') +} diff --git a/crystallib/webserver/heroweb/factory.v b/crystallib/webserver/heroweb/factory.v index 6e95da2eb..77b2ea400 100755 --- a/crystallib/webserver/heroweb/factory.v +++ b/crystallib/webserver/heroweb/factory.v @@ -8,57 +8,14 @@ import freeflowuniverse.crystallib.data.markdownparser import freeflowuniverse.crystallib.ui.console import freeflowuniverse.crystallib.clients.mailclient import freeflowuniverse.crystallib.webserver.auth.jwt -import freeflowuniverse.crystallib.webserver.log +import freeflowuniverse.crystallib.webserver.log {Logger} import db.sqlite -pub fn (app &App) index(mut ctx Context) veb.Result { - doc := Doc{ - navbar: Navbar{ - brand: NavItem{ - href: "/", - text: "OurWorld", - class_name: "brand" - }, - items: [ - NavItem{ - href: "/about", - text: "About Us", - class_name: "nav-item" - }, - NavItem{ - href: "/projects", - text: "Projects", - class_name: "nav-item" - }, - NavItem{ - href: "/contact", - text: "Contact", - class_name: "nav-item" - } - ] - }, - content: markdownparser.new(path: '${os.dir(@FILE)}/content/index.md') or { - return ctx.server_error(err.str()) - } - } - return ctx.html($tmpl('./templates/index.html')) -} - -pub fn (app &App) dashboard(mut ctx Context) veb.Result { - user := app.db.users[ctx.user_id] - return ctx.html($tmpl('./templates/dashboard.html')) -} - //the path is pointing to the instructions pub fn new(path string) !&App { mut app := &App{ db: WebDB{ - logger: log.new( - backend: log.new_backend( - db: - sqlite.connect('${os.dir(@FILE)}/logger.sqlite')! - )! - )! + // Logger: log.new('${os.dir(@FILE)}/logger.sqlite')! } } @@ -86,7 +43,7 @@ fn (mut app App) mount_middlewares() { // must be authorized to fetch infopointers and pointed assets app.route_use('/infopointer/:path...', handler: app.is_authorized) - app.route_use('/assets/:path...', handler: app.is_authorized) + app.route_use('/asset/:path...', handler: app.is_authorized) } fn (mut app App) play(path string) ! { @@ -100,6 +57,7 @@ fn (mut app App) play(path string) ! { } fn (mut app App) run_infopointer_heroscripts() ! { + // QUESTION: we have some .mmd files in our collections, not sure what they do app.static_mime_types['.mmd'] = 'txt/plain' for key, ptr in app.db.infopointers{ app.db.infopointer_run(key)! @@ -107,16 +65,17 @@ fn (mut app App) run_infopointer_heroscripts() ! { continue } - mut asset := pathlib.get(ptr.path_content) - if !asset.exists() { - return error('Asset ${ptr.name} does not exist on path ${ptr.path_content}') - } - if asset.is_file() { - app.serve_static('/assets/${ptr.name}.${asset.extension()}', asset.path)! - } else if asset.is_dir() { - app.mount_static_folder_at(asset.path, '/assets/${ptr.name}')! - } else { - return error('unsupported path ${asset}') - } + // QUESTION: this was serving files statically. Might there be a case where we nneed to reintroduce this? + // mut asset := pathlib.get(ptr.path_content) + // if !asset.exists() { + // return error('Asset ${ptr.name} does not exist on path ${ptr.path_content}') + // } + // if asset.is_file() { + // app.serve_static('/assets/${ptr.name}.${asset.extension()}', asset.path)! + // } else if asset.is_dir() { + // app.mount_static_folder_at(asset.path, '/assets/${ptr.name}')! + // } else { + // return error('unsupported path ${asset}') + // } } } \ No newline at end of file diff --git a/crystallib/webserver/heroweb/middleware.v b/crystallib/webserver/heroweb/middleware.v index 77f60128c..9b24f4b65 100644 --- a/crystallib/webserver/heroweb/middleware.v +++ b/crystallib/webserver/heroweb/middleware.v @@ -1,7 +1,9 @@ module heroweb import freeflowuniverse.crystallib.webserver.auth.jwt +import freeflowuniverse.crystallib.webserver.log import veb +import time // middleware to set user id to context from jwt pub fn (app App) set_user(mut ctx Context) bool { @@ -40,16 +42,17 @@ pub fn (app App) is_logged_in(mut ctx Context) bool { // middleware to check if user is authorized to access an infopointer or asset pub fn (app App) is_authorized(mut ctx Context) bool { + println('running is authorized') if !app.is_logged_in(mut ctx) { return false } if ctx.user_id == 0 { - panic('this should never happen') + panic('this should never happen as user must be logged in to reach this stage') } - infoptr_name := if ctx.req.url.starts_with('/assets/') { - ctx.req.url.all_after('/assets/').all_before('/') + infoptr_name := if ctx.req.url.starts_with('/asset/') { + ctx.req.url.all_after('/asset/').all_before('/') } else if ctx.req.url.starts_with('/infopointer/') { ctx.req.url.all_after('/infopointer/').all_before('/') } else { @@ -71,14 +74,6 @@ pub fn (app App) is_authorized(mut ctx Context) bool { return false } { - // app.db.logger.new_log(Log{ - // object: infoptr_name - // subject: ctx.user_id.str() or { - // return ctx.server_error(err.str()) - // } - // message: 'access path ${app.req.url}' - // event: 'view' - // }) return true } else { // ctx.res.set_status(.unauthorized) diff --git a/crystallib/webserver/heroweb/model.v b/crystallib/webserver/heroweb/model.v index 663c8d147..d97609aa6 100644 --- a/crystallib/webserver/heroweb/model.v +++ b/crystallib/webserver/heroweb/model.v @@ -10,6 +10,7 @@ import veb pub struct App { veb.StaticHandler veb.Middleware[Context] + veb.Controller jwt_secret string = jwt.create_secret() mut: db WebDB @@ -19,9 +20,9 @@ pub: } pub struct WebDB { + // Logger pub mut: authenticator StatelessAuthenticator - logger Logger users map[u16]&User groups map[string]&Group acls map[string]&ACL diff --git a/crystallib/webserver/heroweb/play_infopointers.v b/crystallib/webserver/heroweb/play_infopointers.v index df30b75bc..ee8d27297 100644 --- a/crystallib/webserver/heroweb/play_infopointers.v +++ b/crystallib/webserver/heroweb/play_infopointers.v @@ -52,6 +52,7 @@ pub fn (mut db WebDB) play_infopointers(mut plbook playbook.PlayBook) ! { 'slides' { InfoType.slides } 'pdf' { InfoType.pdf } 'wiki' { InfoType.wiki } + 'website' { InfoType.website } else { println('Invalid category') // Handle the error or set a default value diff --git a/crystallib/webserver/heroweb/slides.v b/crystallib/webserver/heroweb/slides.v index ab2f4d7ab..23eb0d3ab 100644 --- a/crystallib/webserver/heroweb/slides.v +++ b/crystallib/webserver/heroweb/slides.v @@ -6,6 +6,7 @@ pub struct Slides { pub: url string name string + log_endpoint string } pub fn (slides Slides) html() string { diff --git a/crystallib/webserver/heroweb/templates/logs.html b/crystallib/webserver/heroweb/templates/logs.html new file mode 100644 index 000000000..8f86f09a4 --- /dev/null +++ b/crystallib/webserver/heroweb/templates/logs.html @@ -0,0 +1,135 @@ + + + + + + View Logs + + + + +
+

View Logs

+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + +
TimestampEventSubjectObjectMessage
+
+ + \ No newline at end of file diff --git a/crystallib/webserver/heroweb/templates/slides.html b/crystallib/webserver/heroweb/templates/slides.html index edaf1aa0b..3ed1f3224 100644 --- a/crystallib/webserver/heroweb/templates/slides.html +++ b/crystallib/webserver/heroweb/templates/slides.html @@ -117,7 +117,7 @@ let socket; function sendInteraction(action, data) { - fetch("/analytics/@{slides.name}", { + fetch("@{slides.log_endpoint}", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/crystallib/webserver/heroweb/view.v b/crystallib/webserver/heroweb/view.v new file mode 100755 index 000000000..306f7d202 --- /dev/null +++ b/crystallib/webserver/heroweb/view.v @@ -0,0 +1,51 @@ +module heroweb + +import veb +import os +import freeflowuniverse.crystallib.core.playbook +import freeflowuniverse.crystallib.core.pathlib +import freeflowuniverse.crystallib.data.markdownparser +import freeflowuniverse.crystallib.ui.console +import freeflowuniverse.crystallib.clients.mailclient +import freeflowuniverse.crystallib.webserver.auth.jwt +import freeflowuniverse.crystallib.webserver.log +import db.sqlite + +pub fn (app &App) index(mut ctx Context) veb.Result { + doc := Doc{ + navbar: Navbar{ + brand: NavItem{ + href: "/", + text: "OurWorld", + class_name: "brand" + }, + items: [ + NavItem{ + href: "/about", + text: "About Us", + class_name: "nav-item" + }, + NavItem{ + href: "/projects", + text: "Projects", + class_name: "nav-item" + }, + NavItem{ + href: "/contact", + text: "Contact", + class_name: "nav-item" + } + ] + }, + content: markdownparser.new(path: '${os.dir(@FILE)}/content/index.md') or { + return ctx.server_error(err.str()) + } + } + return ctx.html($tmpl('./templates/index.html')) +} + +pub fn (app &App) dashboard(mut ctx Context) veb.Result { + user := app.db.users[ctx.user_id] + return ctx.html($tmpl('./templates/dashboard.html')) +} + diff --git a/crystallib/webserver/heroweb/assets_view.v b/crystallib/webserver/heroweb/view_assets.v similarity index 86% rename from crystallib/webserver/heroweb/assets_view.v rename to crystallib/webserver/heroweb/view_assets.v index 0666bdc75..7560e6d75 100644 --- a/crystallib/webserver/heroweb/assets_view.v +++ b/crystallib/webserver/heroweb/view_assets.v @@ -17,13 +17,14 @@ pub fn (app &App) view_asset(mut ctx Context, name string) veb.Result { infoptr := app.db.infopointers[name] if infoptr.cat == .website { - return ctx.redirect('/assets/${name}') + return ctx.redirect('/asset/${name}/index.html') } html := match infoptr.cat { .pdf { slides := Slides{ - url:'/assets/${name}.pdf' + url:'/asset/${name}' + log_endpoint: '/log/${name}' name: name } slides.html() diff --git a/crystallib/webserver/heroweb/authentication_view.v b/crystallib/webserver/heroweb/view_authentication.v similarity index 100% rename from crystallib/webserver/heroweb/authentication_view.v rename to crystallib/webserver/heroweb/view_authentication.v diff --git a/crystallib/webserver/heroweb/kanban_view.v b/crystallib/webserver/heroweb/view_kanban.v similarity index 100% rename from crystallib/webserver/heroweb/kanban_view.v rename to crystallib/webserver/heroweb/view_kanban.v diff --git a/crystallib/webserver/heroweb/view_log.v b/crystallib/webserver/heroweb/view_log.v new file mode 100644 index 000000000..da33a4de2 --- /dev/null +++ b/crystallib/webserver/heroweb/view_log.v @@ -0,0 +1,11 @@ +module heroweb + +import json +import veb +import freeflowuniverse.crystallib.core.pathlib +import freeflowuniverse.crystallib.webserver.log + +@[GET; '/view/logs'] +pub fn (mut app App) view_logs(mut ctx Context) veb.Result { + return ctx.html($tmpl('./templates/logs.html')) +} diff --git a/crystallib/webserver/log/app.v b/crystallib/webserver/log/app.v new file mode 100644 index 000000000..4431539f7 --- /dev/null +++ b/crystallib/webserver/log/app.v @@ -0,0 +1,26 @@ +module log + +import db.sqlite +import veb + +// pub struct App { +// Logger +// } + +// pub struct Context { +// veb.Context +// } + +// pub fn new_app(db_path string) !&App { +// return &App{ +// Logger: log.new( +// DBBackend: log.new_backend( +// db: sqlite.connect(db_path)! +// )! +// )! +// } +// } + +// pub fn (mut app App) index(mut ctx Context) veb.Result { +// return ctx.html('hello') +// } \ No newline at end of file diff --git a/crystallib/webserver/log/controller.v b/crystallib/webserver/log/controller.v index 7af76d09d..15da2a94f 100644 --- a/crystallib/webserver/log/controller.v +++ b/crystallib/webserver/log/controller.v @@ -4,34 +4,34 @@ import vweb import db.sqlite import json -pub struct Controller { - vweb.Context -pub mut: - db sqlite.DB -} +// pub struct Controller { +// vweb.Context +// pub mut: +// db sqlite.DB +// } -@[post] -pub fn (mut ctrl Controller) log() vweb.Result { - log := json.decode(Log, ctrl.req.data) or { - ctrl.set_status(400, '') - return ctrl.text('Failed to decode request data.') - } - sql ctrl.db { - insert log into Log - } or { - ctrl.set_status(500, '') - return ctrl.text('Failed insert log into db.') - } - return ctrl.ok('') -} +// @[post] +// pub fn (mut ctrl Controller) log() vweb.Result { +// log := json.decode(Log, ctrl.req.data) or { +// ctrl.set_status(400, '') +// return ctrl.text('Failed to decode request data.') +// } +// sql ctrl.db { +// insert log into Log +// } or { +// ctrl.set_status(500, '') +// return ctrl.text('Failed insert log into db.') +// } +// return ctrl.ok('') +// } -@[get] -pub fn (mut ctrl Controller) logs() vweb.Result { - logs := sql ctrl.db { - select from Log - } or { - ctrl.set_status(500, '') - return ctrl.text('Failed insert log into db.') - } - return ctrl.json(logs) -} +// @[get] +// pub fn (mut ctrl Controller) logs() vweb.Result { +// logs := sql ctrl.db { +// select from Log +// } or { +// ctrl.set_status(500, '') +// return ctrl.text('Failed insert log into db.') +// } +// return ctrl.json(logs) +// } diff --git a/crystallib/webserver/log/controller_test.v b/crystallib/webserver/log/controller_test.v index 64f99163a..93565a72e 100644 --- a/crystallib/webserver/log/controller_test.v +++ b/crystallib/webserver/log/controller_test.v @@ -6,29 +6,29 @@ import db.sqlite import time import net.http -const port = 8080 -const url = 'http://localhost:${port}' +// const port = 8080 +// const url = 'http://localhost:${port}' -fn testsuite_begin() { - mut db := sqlite.connect('test.sqlite')! - sql db { - create table Log - } or { panic(err) } - ctrl := &Controller{ - db: db - } - spawn vweb.run(ctrl, 8080) -} +// fn testsuite_begin() { +// mut db := sqlite.connect('test.sqlite')! +// sql db { +// create table Log +// } or { panic(err) } +// ctrl := &Controller{ +// db: db +// } +// spawn vweb.run(ctrl, 8080) +// } -fn test_log() { - log := Log{ - subject: 'test_user' - object: 'test_asset' - event: 'access' - timestamp: time.now() - } - data := json.encode(log) - req := http.new_request(.post, '${analytics.url}/log', data) - resp := req.do()! - assert resp.status_code == 200 -} +// fn test_log() { +// log := Log{ +// subject: 'test_user' +// object: 'test_asset' +// event: 'access' +// timestamp: time.now() +// } +// data := json.encode(log) +// req := http.new_request(.post, '${analytics.url}/log', data) +// resp := req.do()! +// assert resp.status_code == 200 +// } diff --git a/crystallib/webserver/log/factory.v b/crystallib/webserver/log/factory.v index 5f79c4ee0..f506f3b09 100644 --- a/crystallib/webserver/log/factory.v +++ b/crystallib/webserver/log/factory.v @@ -1,18 +1,16 @@ module log -pub struct Logger { -pub mut: - backend DBBackend -} +import db.sqlite -@[params] -pub struct LoggerConfig { -pub: - backend ?DBBackend +pub struct Logger { + db_path string + // DBBackend } -pub fn new(config LoggerConfig) !Logger { +pub fn new(db_path string) !Logger { + // db := sqlite.connect(db_path)! return Logger{ - backend: config.backend or { new_backend()! } + db_path: db_path + // DBBackend: new_backend(db: db)! } } diff --git a/crystallib/webserver/log/log_controller.v b/crystallib/webserver/log/log_controller.v new file mode 100644 index 000000000..048e8002b --- /dev/null +++ b/crystallib/webserver/log/log_controller.v @@ -0,0 +1,70 @@ +module log + +import json +import veb +import freeflowuniverse.crystallib.core.pathlib + +// @[POST; '/log/:name'] +// pub fn (mut app App) log_post(mut ctx Context, name string) veb.Result { +// infoptr := app.db.infopointers[name] or { +// return ctx.not_found() +// } + +// new_log := json.decode(Log, ctx.req.data) or { +// return ctx.server_error(err.str()) +// } +// app.db.logger.new_log(Log{...new_log, subject: ctx.user_id.str()}) or { +// return ctx.server_error(err.str()) +// } + +// return ctx.json('ok') +// } + +// @[GET; '/logs'] +// pub fn (mut app App) logs(mut ctx Context) veb.Result { +// // Initialize the query parameters if present +// mut query := 'SELECT * FROM Log WHERE' +// event_filter := ctx.query['event'] or { '' } +// if event_filter != '' { +// query += ' event = ${event_filter}' +// } + +// subject_filter := ctx.query['subject'] or { '' } +// if subject_filter != '' { +// query += ' subject = ${subject_filter}' +// } + +// object_filter := ctx.query['object'] or { '' } +// if object_filter != '' { +// query += ' subject = ${object_filter}' +// } + +// response := app.db.exec(query) or { +// return ctx.server_error(err.str()) +// } + +// if response.len == 0 { +// panic('response shouldnt be 0') +// } +// println('resp ${response}') + +// // return response[0].vals + +// // Build the SQL query dynamically based on the provided filters +// logs := sql app.db { +// select from Log where +// object == object_filter && +// (event_filter == '' || event.contains(event_filter)) && +// (subject_filter == '' || subject.contains(subject_filter)) && +// (message_filter == '' || message.contains(message_filter)) +// } or { +// return ctx.not_found() +// } + +// // Return the filtered logs or a message if no logs match the criteria +// if logs.len == 0 { +// return ctx.text('No logs found matching the criteria') +// } + +// return ctx.json(logs) +// } diff --git a/crystallib/webserver/log/logger.v b/crystallib/webserver/log/logger.v index 42c51155a..c0da7edb9 100644 --- a/crystallib/webserver/log/logger.v +++ b/crystallib/webserver/log/logger.v @@ -1,14 +1,55 @@ module log -pub fn (mut logger Logger) new_log(log Log) ! { - sql logger.backend.db { +import db.sqlite + +pub fn (logger Logger) new_log(log Log) ! { + db := sqlite.connect(logger.db_path)! + + sql db { insert log into Log }! } -pub fn (mut logger Logger) get_logs(log Log) ![]Log { - logs := sql logger.backend.db { - select from Log - }! +pub struct LogFilter { + Log + matches_all bool + limit int +} + +pub fn (logger Logger) filter_logs(filter LogFilter) ![]Log { + db := sqlite.connect(logger.db_path)! + mut select_stmt := 'select * from Log' + + mut matchers := []string{} + if filter.event != '' { + matchers << 'event == ${filter.event}' + } + + if filter.subject != '' { + matchers << 'subject == ${filter.subject}' + } + + if filter.object != '' { + matchers << 'object == ${filter.object}' + } + + if matchers.len > 0 { + matchers_str := if filter.matches_all { + matchers.join(' AND ') + } else { + matchers.join(' OR ') + } + select_stmt += ' where ${matchers_str}' + } + + responses := db.exec(select_stmt)! + + mut logs := []Log{} + for response in responses { + logs << sql db { + select from Log where id == response.vals[0].int() + }! + } + return logs } \ No newline at end of file diff --git a/crystallib/webserver/log/model.v b/crystallib/webserver/log/model.v index eb939215e..852b2021f 100644 --- a/crystallib/webserver/log/model.v +++ b/crystallib/webserver/log/model.v @@ -3,6 +3,8 @@ module log import time pub struct Log { + id int @[primary; sql: serial] +pub: timestamp time.Time pub mut: event string diff --git a/examples/webserver/herowebexample/heroscripts/threefold_info.hero b/examples/webserver/herowebexample/heroscripts/threefold_info.hero index 6cd65471c..f56a3975d 100644 --- a/examples/webserver/herowebexample/heroscripts/threefold_info.hero +++ b/examples/webserver/herowebexample/heroscripts/threefold_info.hero @@ -5,9 +5,9 @@ hero_url_reset: false content_path: '~/hero/www/info/tech' acl: 'heroweb' - cat: 'wiki' + cat: 'website' description: ' - Wiki for technology of threedold + Website for technology of threedold ' //will have 2 documents inside