~koehr/k0r

45970d227153c07a0f2e0c4c8fe18d93cb935364 — koehr 2 years ago e6a0d12
start to implement URL expiry
4 files changed, 76 insertions(+), 13 deletions(-)

M Cargo.lock
M Cargo.toml
M src/db.rs
M src/main.rs
M Cargo.lock => Cargo.lock +50 -6
@@ 79,7 79,7 @@ dependencies = [
 "serde_urlencoded",
 "sha-1",
 "slab",
 "time",
 "time 0.2.24",
]

[[package]]


@@ 245,7 245,7 @@ dependencies = [
 "serde_json",
 "serde_urlencoded",
 "socket2",
 "time",
 "time 0.2.24",
 "tinyvec",
 "url",
]


@@ 474,6 474,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
 "libc",
 "num-integer",
 "num-traits",
 "time 0.1.44",
 "winapi 0.3.9",
]

[[package]]
name = "const_fn"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 486,7 499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f"
dependencies = [
 "percent-encoding",
 "time",
 "time 0.2.24",
 "version_check",
]



@@ 794,7 807,7 @@ checksum = "4060f4657be78b8e766215b02b18a2e862d83745545de804638e2b545e81aee6"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "wasi 0.10.1+wasi-snapshot-preview1",
 "wasi 0.10.0+wasi-snapshot-preview1",
]

[[package]]


@@ 982,6 995,7 @@ name = "k0r"
version = "0.1.0"
dependencies = [
 "actix-web",
 "chrono",
 "exitcode",
 "failure",
 "failure_derive",


@@ 1192,6 1206,25 @@ dependencies = [
]

[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
 "autocfg",
 "num-traits",
]

[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
 "autocfg",
]

[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1793,6 1826,17 @@ dependencies = [

[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
 "libc",
 "wasi 0.10.0+wasi-snapshot-preview1",
 "winapi 0.3.9",
]

[[package]]
name = "time"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "273d3ed44dca264b0d6b3665e8d48fb515042d42466fad93d2a45b90ec4058f7"


@@ 2035,9 2079,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"

[[package]]
name = "wasi"
version = "0.10.1+wasi-snapshot-preview1"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"

[[package]]
name = "wasm-bindgen"

M Cargo.toml => Cargo.toml +1 -0
@@ 32,6 32,7 @@ failure_derive = "0.1.1"
exitcode = "1.1.2"
human-panic = "1.0.3"
text_io = "0.1.8"
chrono = "0.4.19"

[build-dependencies]
ructe = { version = "0.13", features = ["mime03"] }

M src/db.rs => src/db.rs +20 -7
@@ 1,4 1,5 @@
use actix_web::{web, Error as AWError};
use chrono::Utc;
use failure::Error;
use failure_derive::Fail;
use futures::{Future, TryFutureExt};


@@ 27,7 28,7 @@ pub enum DBError {
pub enum DBValue {
    String(String),
    Number(i64),
    // Bool(bool),
    Bool(bool),
    None,
}



@@ 40,11 41,13 @@ pub struct UrlPostData {
    pub url: String,
    pub title: Option<String>,
    pub description: Option<String>,
    pub expiry: Option<i64>,
    pub key: String,
}

/// Possible database queries, used with db::query
pub enum Queries {
    ForceSync,
    NeedsInit,
    CountUsers,
    InitDB,


@@ 95,6 98,7 @@ URLs|title
URLs|url
URLs|user_id
URLs|visits
URLs|expiry
Users|api_key
Users|is_admin
Users|rate_limit


@@ 111,6 115,11 @@ Users|rowid",
    }
}

fn sync_database(conn: Connection) -> Result {
    conn.execute("PRAGMA wal_checkpoint(TRUNCATE);", NO_PARAMS)?;
    Ok(DBValue::Bool(true))
}

/// Initializes a new SQlite database with the default schema.
fn init_database(conn: Connection) -> Result {
    conn.execute_batch(


@@ 129,8 138,9 @@ fn init_database(conn: Connection) -> Result {
          visits      INTEGER DEFAULT 0,
          title       TEXT,
          description TEXT,
          created_at   DATETIME,
          user_id      INTEGER NOT NULL,
          expiry      DATETIME,
          created_at  DATETIME,
          user_id     INTEGER NOT NULL,
          FOREIGN KEY(user_id) REFERENCES Users(rowid)
        );
        COMMIT;",


@@ 147,7 157,7 @@ fn count_users(conn: Connection) -> Result {
    conn.query_row("SELECT COUNT(rowid) FROM USERS", NO_PARAMS, |row| {
        row.get(0)
    })
    .map(|v| DBValue::Number(v))
    .map(DBValue::Number)
    .map_err(|src| {
        let msg = "Could not check users.".to_owned();
        Error::from(DBError::SqliteError { msg, src })


@@ 174,10 184,11 @@ fn create_user(conn: Connection, rate_limit: i64, is_admin: bool) -> Result {
/// short_code is simply the base36 version of the table id
fn get_url(conn: Connection, short_code: &str) -> Result {
    let row_id = ShortCode::from_code(short_code)?.n;
    let now = Utc::now().timestamp();

    conn.query_row(
        "SELECT url FROM URLs WHERE rowid = ?",
        &[row_id as i64],
        "SELECT url FROM URLs WHERE rowid = ? and expiry > ?",
        &[row_id as i64, now],
        |row| row.get(0),
    )
    .map(DBValue::String)


@@ 195,7 206,7 @@ fn store_url(conn: Connection, data: &UrlPostData) -> Result {
        |row| row.get(0),
    )?;
    let _ = conn.execute_named(
        "INSERT INTO URLs VALUES(:url, 0, :title, :description, DATETIME('now'), :user_id)",
        "INSERT INTO URLs VALUES(:url, 0, :title, :description, DATETIME('now'), DATETIME(?), :user_id)",
        &[
            (":url", &data.url),
            (":title", data.title.as_ref().unwrap_or(&String::from(""))),


@@ 203,6 214,7 @@ fn store_url(conn: Connection, data: &UrlPostData) -> Result {
                ":description",
                data.description.as_ref().unwrap_or(&String::from("")),
            ),
            (":expiry", data.expiry.as_ref().unwrap_or(&i64::MAX)),
            (":user_id", &(user_id.to_string())),
        ],
    )?;


@@ 219,6 231,7 @@ pub fn query(
) -> impl Future<Output = std::result::Result<DBValue, AWError>> {
    let pool = pool.clone();
    web::block(move || match query {
        Queries::ForceSync => sync_database(pool.get()?),
        Queries::NeedsInit => check_database_schema(pool.get()?),
        Queries::CountUsers => count_users(pool.get()?),
        Queries::InitDB => init_database(pool.get()?),

M src/main.rs => src/main.rs +5 -0
@@ 166,6 166,11 @@ async fn init_db_pool(path_str: String) -> db::Pool {
        Err(err) => panic!("Failed to create super user! {}", err),
    }

    match db::query(&db_pool, db::Queries::ForceSync).await {
        Ok(_) => { /* all is good */ }
        Err(err) => panic!("Failed to sync database! {}", err)
    }

    db_pool
}