Handle CORS, Additional error cases, Simplified database

This commit is contained in:
Manuel 2023-01-12 19:34:28 +01:00
parent 859ac51f1a
commit d38cb1f9db
Signed by: SunRed
GPG Key ID: 4085037435E1F07A
5 changed files with 92 additions and 26 deletions

View File

@ -4,3 +4,6 @@ token = "asd"
redirect = false
redirect_url = ""
db_path = "./db/app.db"
origins = [
"*"
]

View File

@ -1,9 +1,10 @@
module main
import toml
import os
const (
path = './config.toml'
path = config_path()
)
struct Config {
@ -13,14 +14,20 @@ struct Config {
redirect bool
redirect_url string
db_path string
origins []string
}
fn (config Config) copy() Config {
return config
}
fn load_config() Config {
config := toml.parse_file(path) or { panic(err) }
fn load_config() !Config {
if !os.is_file(path) {
return error('Config file does not exist or is not a file')
}
config := toml.parse_file(path) or {
return error('Error while parsing config file:\n' + err.msg())
}
return Config{
host: config.value('host').string()
@ -29,5 +36,14 @@ fn load_config() Config {
redirect: config.value('redirect').bool()
redirect_url: config.value('redirect_url').string()
db_path: config.value('db_path').string()
origins: config.value('origins').array().as_strings()
}
}
fn config_path() string {
path_env := $env('CONFIG_PATH')
if path_env.trim_space().len != 0 {
return path_env
}
return './config.toml'
}

View File

@ -1,20 +1,18 @@
module main
[table: 'Score']
struct ScoreRes {
pub mut:
id int [primary; sql: serial]
player string
score int
time i64
id i64 [primary; sql: serial]
player string [nonull]
score int [nonull]
time i64 [nonull]
}
fn (mut app App) create_tables() int {
return app.db.exec_none('CREATE TABLE IF NOT EXISTS ScoreRes (
id INTEGER PRIMARY KEY,
player TEXT NOT NULL,
score INTEGER NOT NULL,
time SQLITE_INT64_TYPE NOT NULL
)')
fn (mut app App) create_tables() {
sql app.db {
create table ScoreRes
}
}
fn (mut app App) insert_score(score ScoreRes) ScoreRes {
@ -29,7 +27,7 @@ fn (mut app App) insert_score(score ScoreRes) ScoreRes {
fn (mut app App) get_scores() []ScoreRes {
return sql app.db {
select from ScoreRes order by score desc
select from ScoreRes order by score desc limit 1000
}
}

View File

@ -18,7 +18,7 @@ fn main() {
mut app_config := Config{}
lock app.config {
app.config = load_config()
app.config = load_config() or { panic('Could not load config file: ' + err.msg()) }
app_config = app.config.copy()
}
@ -33,12 +33,13 @@ fn main() {
panic(err)
}
sql_code := app.create_tables()
app.create_tables()
if sqlite.is_error(sql_code) {
println('Could not create tables!')
panic('Error code ' + sql_code.str())
}
// orm does not yet return Results
// if sqlite.is_error(sql_code) {
// println('Could not create tables!')
// panic('Error code ' + sql_code.str())
// }
mut host := '::'
if app_config.host != '' {

View File

@ -9,21 +9,24 @@ struct Status {
message string
}
[get; head]
pub fn (mut app App) index() vweb.Result {
rlock app.config {
if app.config.redirect {
app.redirect(app.config.redirect_url)
return app.redirect(app.config.redirect_url)
}
}
return app.text('Hello :)')
return app.not_found()
}
['/api/v1/score/list']
['/api/v1/score/list'; get; head]
pub fn (mut app App) score_list() vweb.Result {
app.add_cors_headers()
if !app.auth() {
app.set_status(401, '')
return app.json(Status{401, 'OAuth token is missing'})
return app.json(Status{401, 'Access token is wrong or missing'})
}
scores := app.get_scores()
@ -33,9 +36,11 @@ pub fn (mut app App) score_list() vweb.Result {
['/api/v1/score/submit'; post]
pub fn (mut app App) score_submit() vweb.Result {
app.add_cors_headers()
if !app.auth() {
app.set_status(401, '')
return app.json(Status{401, 'OAuth token is missing'})
return app.json(Status{401, 'Access token is wrong or missing'})
}
body := json.decode(Score, app.req.data) or {
@ -43,6 +48,18 @@ pub fn (mut app App) score_submit() vweb.Result {
return app.json(Status{400, 'Bad JSON object'})
}
if body.player.trim_space() == '' {
app.set_status(400, '')
return app.json(Status{400, 'Name cannot be empty'})
}
if body.score <= 0 {
app.set_status(400, '')
return app.json(Status{400, 'Illegal score'})
}
// TODO: Bad word filter
score := app.insert_score(ScoreRes{
player: body.player
score: body.score
@ -52,6 +69,37 @@ pub fn (mut app App) score_submit() vweb.Result {
return app.json(score)
}
pub fn (mut app App) add_cors_headers() {
origin := app.get_header('origin')
mut origins := []string{}
rlock app.config {
origins = app.config.origins.clone()
}
default_origin := origins[0] or { '*' }
allowed_origin := if origins.any(it == origin) { origin } else { default_origin }
app.add_header('Access-Control-Allow-Origin', allowed_origin)
app.add_header('Access-Control-Allow-Methods', 'OPTIONS, HEAD, GET, POST')
app.add_header('Access-Control-Allow-Headers', 'Authorization, Content-Type')
app.add_header('Access-Control-Max-Age', '86400')
}
pub fn (mut app App) handle_cors() vweb.Result {
app.set_status(204, '')
app.add_cors_headers()
return app.ok('')
}
['/api/v1/score/list'; options]
pub fn (mut app App) handle_score_list_cors() vweb.Result {
return app.handle_cors()
}
['/api/v1/score/submit'; options]
pub fn (mut app App) handle_score_submit_cors() vweb.Result {
return app.handle_cors()
}
fn (mut app App) auth() bool {
auth_header := app.get_header('Authorization')
token := auth_header.after('Bearer ')