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:
Manuel 2023-01-10 11:00:00 +01:00
parent c03f949344
commit 7749b7c159
Signed by: Manuel
GPG key ID: 4085037435E1F07A
5 changed files with 59 additions and 19 deletions

View file

@ -2,10 +2,12 @@ FROM thevlang/vlang:alpine AS build
WORKDIR /tmp/app
COPY . .
RUN v -prod .
RUN v -prod -o highscore-server .
FROM alpine:latest
RUN apk --no-cache add sqlite-dev
WORKDIR /app
COPY --from=build /tmp/app/highscore-server .
COPY --from=build /tmp/app/config.example.toml ./config.toml

View file

@ -15,6 +15,10 @@ struct Config {
db_path string
}
fn (config Config) copy() Config {
return config
}
fn load_config() Config {
config := toml.parse_file(path) or { panic(err) }

View file

@ -8,7 +8,7 @@ pub mut:
time i64
}
fn (mut app App) create_tables() bool {
fn (mut app App) create_tables() int {
return app.db.exec_none(
"CREATE TABLE IF NOT EXISTS ScoreRes (
id INTEGER PRIMARY KEY,
@ -16,7 +16,7 @@ fn (mut app App) create_tables() bool {
score INTEGER NOT NULL,
time SQLITE_INT64_TYPE NOT NULL
)"
) == 0
)
}
fn (mut app App) insert_score(score ScoreRes) {

View file

@ -2,36 +2,64 @@ module main
import vweb
import sqlite
import time
import os
struct App {
vweb.Context
pub mut:
db sqlite.DB
config Config
config shared Config
is_admin bool
}
fn main() {
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 {
println('Database Error!')
if !os.exists(app_config.db_path) {
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)
}
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 := '::'
if app.config.host != "" {
host = app.config.host
if 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{
host: host
port: app.config.port
port: app_config.port
family: .ip6
}) 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)
}

View file

@ -4,7 +4,7 @@ import vweb
import json
import time
struct Status {
struct ResultStatus {
status int
message string
}
@ -16,8 +16,10 @@ struct ErrorStatus {
}
pub fn (mut app App) index() vweb.Result {
if app.config.redirect {
app.redirect(app.config.redirect_url)
rlock app.config {
if app.config.redirect {
app.redirect(app.config.redirect_url)
}
}
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()})
return app.json(
Status {
ResultStatus {
200,
"Success"
}
@ -77,10 +79,14 @@ pub fn (mut app App) score_submit() vweb.Result {
}
fn (mut app App) auth() bool {
app.config = load_config()
auth_header := app.get_header('Authorization')
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
}