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 = false
|
||||||
redirect_url = ""
|
redirect_url = ""
|
||||||
db_path = "./db/app.db"
|
db_path = "./db/app.db"
|
||||||
|
origins = [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
|
22
src/config.v
22
src/config.v
|
@ -1,9 +1,10 @@
|
||||||
module main
|
module main
|
||||||
|
|
||||||
import toml
|
import toml
|
||||||
|
import os
|
||||||
|
|
||||||
const (
|
const (
|
||||||
path = './config.toml'
|
path = config_path()
|
||||||
)
|
)
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
|
@ -13,14 +14,20 @@ struct Config {
|
||||||
redirect bool
|
redirect bool
|
||||||
redirect_url string
|
redirect_url string
|
||||||
db_path string
|
db_path string
|
||||||
|
origins []string
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (config Config) copy() Config {
|
fn (config Config) copy() Config {
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_config() Config {
|
fn load_config() !Config {
|
||||||
config := toml.parse_file(path) or { panic(err) }
|
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{
|
return Config{
|
||||||
host: config.value('host').string()
|
host: config.value('host').string()
|
||||||
|
@ -29,5 +36,14 @@ fn load_config() Config {
|
||||||
redirect: config.value('redirect').bool()
|
redirect: config.value('redirect').bool()
|
||||||
redirect_url: config.value('redirect_url').string()
|
redirect_url: config.value('redirect_url').string()
|
||||||
db_path: config.value('db_path').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
|
module main
|
||||||
|
|
||||||
|
[table: 'Score']
|
||||||
struct ScoreRes {
|
struct ScoreRes {
|
||||||
pub mut:
|
pub mut:
|
||||||
id int [primary; sql: serial]
|
id i64 [primary; sql: serial]
|
||||||
player string
|
player string [nonull]
|
||||||
score int
|
score int [nonull]
|
||||||
time i64
|
time i64 [nonull]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut app App) create_tables() int {
|
fn (mut app App) create_tables() {
|
||||||
return app.db.exec_none('CREATE TABLE IF NOT EXISTS ScoreRes (
|
sql app.db {
|
||||||
id INTEGER PRIMARY KEY,
|
create table ScoreRes
|
||||||
player TEXT NOT NULL,
|
}
|
||||||
score INTEGER NOT NULL,
|
|
||||||
time SQLITE_INT64_TYPE NOT NULL
|
|
||||||
)')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut app App) insert_score(score ScoreRes) 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 {
|
fn (mut app App) get_scores() []ScoreRes {
|
||||||
return sql app.db {
|
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{}
|
mut app_config := Config{}
|
||||||
|
|
||||||
lock app.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()
|
app_config = app.config.copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,12 +33,13 @@ fn main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sql_code := app.create_tables()
|
app.create_tables()
|
||||||
|
|
||||||
if sqlite.is_error(sql_code) {
|
// orm does not yet return Results
|
||||||
println('Could not create tables!')
|
// if sqlite.is_error(sql_code) {
|
||||||
panic('Error code ' + sql_code.str())
|
// println('Could not create tables!')
|
||||||
}
|
// panic('Error code ' + sql_code.str())
|
||||||
|
// }
|
||||||
|
|
||||||
mut host := '::'
|
mut host := '::'
|
||||||
if app_config.host != '' {
|
if app_config.host != '' {
|
||||||
|
|
58
src/web.v
58
src/web.v
|
@ -9,21 +9,24 @@ struct Status {
|
||||||
message string
|
message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[get; head]
|
||||||
pub fn (mut app App) index() vweb.Result {
|
pub fn (mut app App) index() vweb.Result {
|
||||||
rlock app.config {
|
rlock app.config {
|
||||||
if app.config.redirect {
|
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 {
|
pub fn (mut app App) score_list() vweb.Result {
|
||||||
|
app.add_cors_headers()
|
||||||
|
|
||||||
if !app.auth() {
|
if !app.auth() {
|
||||||
app.set_status(401, '')
|
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()
|
scores := app.get_scores()
|
||||||
|
@ -33,9 +36,11 @@ pub fn (mut app App) score_list() vweb.Result {
|
||||||
|
|
||||||
['/api/v1/score/submit'; post]
|
['/api/v1/score/submit'; post]
|
||||||
pub fn (mut app App) score_submit() vweb.Result {
|
pub fn (mut app App) score_submit() vweb.Result {
|
||||||
|
app.add_cors_headers()
|
||||||
|
|
||||||
if !app.auth() {
|
if !app.auth() {
|
||||||
app.set_status(401, '')
|
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 {
|
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'})
|
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{
|
score := app.insert_score(ScoreRes{
|
||||||
player: body.player
|
player: body.player
|
||||||
score: body.score
|
score: body.score
|
||||||
|
@ -52,6 +69,37 @@ pub fn (mut app App) score_submit() vweb.Result {
|
||||||
return app.json(score)
|
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 {
|
fn (mut app App) auth() bool {
|
||||||
auth_header := app.get_header('Authorization')
|
auth_header := app.get_header('Authorization')
|
||||||
token := auth_header.after('Bearer ')
|
token := auth_header.after('Bearer ')
|
||||||
|
|
Loading…
Reference in a new issue