feat(server): added endpoints for listing & uploading build logs

main
Jef Roosens 2022-05-07 15:10:07 +02:00
parent 58c1ecd25e
commit 7e01dbafec
Signed by untrusted user: Jef Roosens
GPG Key ID: B75D4F293C7052DB
2 changed files with 108 additions and 0 deletions

99
src/server/logs.v 100644
View File

@ -0,0 +1,99 @@
module server
import web
import net.http
import net.urllib
import response { new_data_response, new_response }
import db
import time
import os
import util
['/api/logs'; get]
fn (mut app App) get_logs() web.Result {
if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
}
logs := app.db.get_build_logs()
return app.json(http.Status.ok, new_data_response(logs))
}
// parse_query_time unescapes an HTTP query parameter & tries to parse it as a
// time.Time struct.
fn parse_query_time(query string) ?time.Time {
unescaped := urllib.query_unescape(query) ?
t := time.parse(unescaped) ?
return t
}
['/api/logs'; post]
fn (mut app App) post_log() web.Result {
if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
}
// Parse query params
start_time := parse_query_time(app.query['startTime']) or {
return app.json(http.Status.bad_request, new_response('Invalid or missing start time.'))
}
end_time := time.parse(app.query['endTime'].replace('_', ' ')) or {
return app.json(http.Status.bad_request, new_response('Invalid or missing end time.'))
}
if 'exitCode' !in app.query {
return app.json(http.Status.bad_request, new_response('Missing exit code.'))
}
exit_code := app.query['exitCode'].int()
if 'arch' !in app.query {
return app.json(http.Status.bad_request, new_response("Missing parameter 'arch'."))
}
arch := app.query['arch']
repo := app.db.get_git_repo(app.query['repo'].int()) or {
return app.json(http.Status.bad_request, new_response('Unknown repo.'))
}
// Store log in db
log := db.BuildLog{
repo: repo
start_time: start_time
end_time: end_time
exit_code: exit_code
}
app.db.add_build_log(log)
repo_logs_dir := os.join_path(app.conf.data_dir, logs_dir_name, repo.id.str(), arch)
// Create the logs directory of it doesn't exist
if !os.exists(repo_logs_dir) {
os.mkdir_all(repo_logs_dir) or {
app.lerror("Couldn't create dir '$repo_logs_dir'.")
return app.json(http.Status.internal_server_error, new_response('An error occured while processing the request.'))
}
}
// Stream log contents to correct file
file_name := start_time.custom_format('YYYY-MM-DD_HH-mm-ss')
full_path := os.join_path_single(repo_logs_dir, file_name)
if length := app.req.header.get(.content_length) {
util.reader_to_file(mut app.reader, length.int(), full_path) or {
app.lerror('An error occured while receiving logs: $err.msg()')
return app.json(http.Status.internal_server_error, new_response('Failed to upload logs.'))
}
} else {
return app.status(http.Status.length_required)
}
return app.json(http.Status.ok, new_response('Logs added successfully.'))
}

View File

@ -12,6 +12,7 @@ const (
log_file_name = 'vieter.log' log_file_name = 'vieter.log'
repo_dir_name = 'repos' repo_dir_name = 'repos'
db_file_name = 'vieter.sqlite' db_file_name = 'vieter.sqlite'
logs_dir_name = 'logs'
) )
struct App { struct App {
@ -37,6 +38,14 @@ pub fn server(conf Config) ? {
os.mkdir_all(conf.data_dir) or { util.exit_with_message(1, 'Failed to create data directory.') } os.mkdir_all(conf.data_dir) or { util.exit_with_message(1, 'Failed to create data directory.') }
logs_dir := os.join_path_single(conf.data_dir, server.logs_dir_name)
if !os.exists(logs_dir) {
os.mkdir(os.join_path_single(conf.data_dir, server.logs_dir_name)) or {
util.exit_with_message(1, 'Failed to create logs directory.')
}
}
mut logger := log.Log{ mut logger := log.Log{
level: log_level level: log_level
} }