Handle CORS, Additional error cases, Simplified database
This commit is contained in:
parent
859ac51f1a
commit
d38cb1f9db
5 changed files with 92 additions and 26 deletions
|
@ -4,3 +4,6 @@ token = "asd"
|
|||
redirect = false
|
||||
redirect_url = ""
|
||||
db_path = "./db/app.db"
|
||||
origins = [
|
||||
"*"
|
||||
]
|
||||
|
|
22
src/config.v
22
src/config.v
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
13
src/main.v
13
src/main.v
|
@ -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 != '' {
|
||||
|
|
58
src/web.v
58
src/web.v
|
@ -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 ')
|
||||
|
|
Loading…
Reference in a new issue