Several improvements
* Make Dockerfile build and run correctly on alpine, fix missing sqlite * Make config shared to avoid reading from file on each request * Correctly handle Signals to gracefully stop database connection and to speed up shutdown, relevant for docker deployment * Correctly check for sqlite error on table creation
This commit is contained in:
parent
c03f949344
commit
7749b7c159
5 changed files with 59 additions and 19 deletions
|
@ -2,10 +2,12 @@ FROM thevlang/vlang:alpine AS build
|
||||||
|
|
||||||
WORKDIR /tmp/app
|
WORKDIR /tmp/app
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN v -prod .
|
RUN v -prod -o highscore-server .
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
|
RUN apk --no-cache add sqlite-dev
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build /tmp/app/highscore-server .
|
COPY --from=build /tmp/app/highscore-server .
|
||||||
COPY --from=build /tmp/app/config.example.toml ./config.toml
|
COPY --from=build /tmp/app/config.example.toml ./config.toml
|
||||||
|
|
|
@ -15,6 +15,10 @@ struct Config {
|
||||||
db_path string
|
db_path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (config Config) copy() Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
fn load_config() Config {
|
fn load_config() Config {
|
||||||
config := toml.parse_file(path) or { panic(err) }
|
config := toml.parse_file(path) or { panic(err) }
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ pub mut:
|
||||||
time i64
|
time i64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut app App) create_tables() bool {
|
fn (mut app App) create_tables() int {
|
||||||
return app.db.exec_none(
|
return app.db.exec_none(
|
||||||
"CREATE TABLE IF NOT EXISTS ScoreRes (
|
"CREATE TABLE IF NOT EXISTS ScoreRes (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
|
@ -16,7 +16,7 @@ fn (mut app App) create_tables() bool {
|
||||||
score INTEGER NOT NULL,
|
score INTEGER NOT NULL,
|
||||||
time SQLITE_INT64_TYPE NOT NULL
|
time SQLITE_INT64_TYPE NOT NULL
|
||||||
)"
|
)"
|
||||||
) == 0
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut app App) insert_score(score ScoreRes) {
|
fn (mut app App) insert_score(score ScoreRes) {
|
||||||
|
|
46
src/main.v
46
src/main.v
|
@ -2,36 +2,64 @@ module main
|
||||||
|
|
||||||
import vweb
|
import vweb
|
||||||
import sqlite
|
import sqlite
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
vweb.Context
|
vweb.Context
|
||||||
pub mut:
|
pub mut:
|
||||||
db sqlite.DB
|
db sqlite.DB
|
||||||
config Config
|
config shared Config
|
||||||
is_admin bool
|
is_admin bool
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
mut app := &App{}
|
mut app := &App{}
|
||||||
|
mut app_config := Config{}
|
||||||
|
|
||||||
app.config = load_config()
|
lock app.config {
|
||||||
|
app.config = load_config()
|
||||||
|
app_config = app.config.copy()
|
||||||
|
}
|
||||||
|
|
||||||
app.db = sqlite.connect(app.config.db_path) or {
|
if !os.exists(app_config.db_path) {
|
||||||
println('Database Error!')
|
println("Creating database file at " + app_config.db_path)
|
||||||
|
mut file := os.create(app_config.db_path) or { panic(err) }
|
||||||
|
file.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.db = sqlite.connect(app_config.db_path) or {
|
||||||
|
println('Database error!')
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.create_tables()
|
sql_code := app.create_tables()
|
||||||
|
|
||||||
|
if sqlite.is_error(sql_code) {
|
||||||
|
println('Could not create tables!')
|
||||||
|
panic('Error code ' + sql_code.str())
|
||||||
|
}
|
||||||
|
|
||||||
mut host := '::'
|
mut host := '::'
|
||||||
|
if app_config.host != "" {
|
||||||
if app.config.host != "" {
|
host = app_config.host
|
||||||
host = app.config.host
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle graceful shutdown for Docker
|
||||||
|
os.signal_opt(os.Signal.int, app.shutdown) or { panic(err) }
|
||||||
|
os.signal_opt(os.Signal.term, app.shutdown) or { panic(err) }
|
||||||
|
|
||||||
vweb.run_at(app, vweb.RunParams{
|
vweb.run_at(app, vweb.RunParams{
|
||||||
host: host
|
host: host
|
||||||
port: app.config.port
|
port: app_config.port
|
||||||
family: .ip6
|
family: .ip6
|
||||||
}) or { panic(err) }
|
}) or { panic(err) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut app App) shutdown(sig os.Signal) {
|
||||||
|
app.db.close() or { panic(err) }
|
||||||
|
println("Shut down database gracefully")
|
||||||
|
println("Exiting...")
|
||||||
|
time.sleep(1e+9) // Sleep one second
|
||||||
|
exit(0)
|
||||||
|
}
|
||||||
|
|
20
src/web.v
20
src/web.v
|
@ -4,7 +4,7 @@ import vweb
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
|
||||||
struct Status {
|
struct ResultStatus {
|
||||||
status int
|
status int
|
||||||
message string
|
message string
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,10 @@ struct ErrorStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut app App) index() vweb.Result {
|
pub fn (mut app App) index() vweb.Result {
|
||||||
if app.config.redirect {
|
rlock app.config {
|
||||||
app.redirect(app.config.redirect_url)
|
if app.config.redirect {
|
||||||
|
app.redirect(app.config.redirect_url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.text("Hello :)")
|
return app.text("Hello :)")
|
||||||
|
@ -69,7 +71,7 @@ pub fn (mut app App) score_submit() vweb.Result {
|
||||||
app.insert_score(ScoreRes{player:body.player, score:body.score, time:time.now().unix_time()})
|
app.insert_score(ScoreRes{player:body.player, score:body.score, time:time.now().unix_time()})
|
||||||
|
|
||||||
return app.json(
|
return app.json(
|
||||||
Status {
|
ResultStatus {
|
||||||
200,
|
200,
|
||||||
"Success"
|
"Success"
|
||||||
}
|
}
|
||||||
|
@ -77,10 +79,14 @@ pub fn (mut app App) score_submit() vweb.Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut app App) auth() bool {
|
fn (mut app App) auth() bool {
|
||||||
app.config = load_config()
|
|
||||||
|
|
||||||
auth_header := app.get_header('Authorization')
|
auth_header := app.get_header('Authorization')
|
||||||
token := auth_header.after('Bearer ')
|
token := auth_header.after('Bearer ')
|
||||||
|
|
||||||
return token == app.config.token
|
mut config_token := ""
|
||||||
|
|
||||||
|
rlock app.config {
|
||||||
|
config_token = app.config.token
|
||||||
|
}
|
||||||
|
|
||||||
|
return config_token.len != 0 && token == config_token
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue