Compare commits

...

10 commits

6 changed files with 74 additions and 16 deletions

2
Cargo.lock generated
View file

@ -740,7 +740,7 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]] [[package]]
name = "kosyncrs" name = "kosyncrs"
version = "1.1.1" version = "2.0.0"
dependencies = [ dependencies = [
"axum", "axum",
"serde", "serde",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "kosyncrs" name = "kosyncrs"
version = "1.1.1" version = "2.0.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -28,14 +28,14 @@ Connect to the database as user *kosync* (with psql) and create the database tab
```sql ```sql
CREATE TABLE users ( CREATE TABLE users (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
username TEXT NOT NULL, username TEXT UNIQUE NOT NULL,
password TEXT password TEXT
); );
CREATE TABLE progresses ( CREATE TABLE progresses (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
user_id BIGINT, user_id BIGINT,
document TEXT UNIQUE NOT NULL, document TEXT NOT NULL,
progress TEXT NOT NULL, progress TEXT NOT NULL,
percentage REAL NOT NULL, percentage REAL NOT NULL,
device TEXT NOT NULL, device TEXT NOT NULL,
@ -45,4 +45,4 @@ CREATE TABLE progresses (
REFERENCES users(id) ON DELETE CASCADE, REFERENCES users(id) ON DELETE CASCADE,
UNIQUE (user_id, document) UNIQUE (user_id, document)
); );
``` ```

52
python/redis2pg.py Normal file
View file

@ -0,0 +1,52 @@
#!/usr/bin/env python3
import psycopg2
import re
import redis
con = redis.Redis()
pgcon = psycopg2.connect("postgresql://kosync:mypassword@localhost/kosync")
pgcur = pgcon.cursor()
userkeys = con.keys('user:*:key')
for key in userkeys:
key = key.decode('utf-8')
match = re.search('user:(.*):key', key)
username = match.group(1)
password = con.get(key).decode('utf-8')
documentkeys = con.keys('user:' + username + ':document:*')
if len(documentkeys) == 0:
print("Skipped:", username)
continue
pgcur.execute("INSERT INTO users (username, password) VALUES (%s, %s) ON CONFLICT (username) DO UPDATE SET username = %s, password = %s",
(username, password, username, password))
pgcur.execute("SELECT id FROM users WHERE username = %s", (username,))
res = pgcur.fetchone()
user_id = res[0]
for dockey in documentkeys:
dockey = dockey.decode('utf-8')
match = re.search("user:.*:(.*)", dockey)
document = match.group(1)
docvals = con.hgetall(dockey)
progress = docvals[b"progress"].decode()
percentage = docvals[b"percentage"].decode()
device = docvals[b"device"].decode()
device_id = docvals[b"device_id"].decode()
timestamp = docvals[b"timestamp"].decode()
pgcur.execute("INSERT INTO progresses (user_id, document, progress, percentage, device, device_id, timestamp) VALUES (%s, %s, %s, %s, %s, %s, %s) ON CONFLICT (user_id, document) DO UPDATE SET user_id = %s, document = %s, progress = %s, percentage = %s, device = %s, device_id = %s, timestamp = %s",
(user_id, document, progress, percentage, device, device_id, timestamp, user_id, document, progress, percentage, device, device_id, timestamp))
pgcon.commit()
pgcur.close()
pgcon.close()

View file

@ -39,12 +39,15 @@ pub struct GetProgress {
timestamp: i64, timestamp: i64,
} }
const VERSION: &str = env!("CARGO_PKG_VERSION");
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let pg_url: String = env::var("PG_URL") let pg_url: String = env::var("PG_URL")
.ok() .ok()
.and_then(|v| v.parse().ok()) .and_then(|v| v.parse().ok())
.unwrap(); .unwrap();
let listen_port: String = env::var("PORT").ok().and_then(|v| v.parse().ok()).unwrap();
let db_pool = PgPoolOptions::new() let db_pool = PgPoolOptions::new()
.max_connections(64) .max_connections(64)
@ -61,10 +64,12 @@ async fn main() {
.route("/syncs/progress", put(update_progress)) .route("/syncs/progress", put(update_progress))
.route("/syncs/progress/{document}", get(get_progress)) .route("/syncs/progress/{document}", get(get_progress))
.route("/healthcheck", get(healthcheck)) .route("/healthcheck", get(healthcheck))
.route("/version", get(version))
.with_state(db_pool); .with_state(db_pool);
// run it with hyper on localhost:3003 let mut bind_with = "127.0.0.1:".to_owned();
let listener = tokio::net::TcpListener::bind("127.0.0.1:3003") bind_with += &listen_port;
let listener = tokio::net::TcpListener::bind(bind_with)
.await .await
.unwrap(); .unwrap();
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
@ -74,14 +79,15 @@ async fn root() -> &'static str {
"KOreader sync server" "KOreader sync server"
} }
async fn version() -> &'static str {
VERSION
}
async fn create_user( async fn create_user(
State(db_pool): State<PgPool>, State(db_pool): State<PgPool>,
Json(payload): Json<User>, Json(payload): Json<User>,
) -> (StatusCode, Json<Value>) { ) -> (StatusCode, Json<Value>) {
//let client = redis::Client::open("redis://127.0.0.1/").unwrap(); let username = payload.username.trim().to_owned();
//let mut con = client.get_connection().unwrap();
let username = payload.username;
let password = payload.password; let password = payload.password;
let row: (i64,) = sqlx::query_as("SELECT COUNT(id) FROM users WHERE username = $1") let row: (i64,) = sqlx::query_as("SELECT COUNT(id) FROM users WHERE username = $1")
@ -128,7 +134,7 @@ async fn authorize(db: impl PgExecutor<'_>, username: &str, password: &str) -> b
} }
async fn auth_user(State(db_pool): State<PgPool>, headers: HeaderMap) -> (StatusCode, Json<Value>) { async fn auth_user(State(db_pool): State<PgPool>, headers: HeaderMap) -> (StatusCode, Json<Value>) {
let username = headers["x-auth-user"].to_str().unwrap_or(""); let username = headers["x-auth-user"].to_str().unwrap_or("").trim();
let password = headers["x-auth-key"].to_str().unwrap_or(""); let password = headers["x-auth-key"].to_str().unwrap_or("");
let mut tx = db_pool.begin().await.unwrap(); let mut tx = db_pool.begin().await.unwrap();
@ -150,7 +156,7 @@ async fn update_progress(
headers: HeaderMap, headers: HeaderMap,
Json(payload): Json<UpdateProgress>, Json(payload): Json<UpdateProgress>,
) -> StatusCode { ) -> StatusCode {
let username = headers["x-auth-user"].to_str().unwrap_or(""); let username = headers["x-auth-user"].to_str().unwrap_or("").trim();
let password = headers["x-auth-key"].to_str().unwrap_or(""); let password = headers["x-auth-key"].to_str().unwrap_or("");
let mut tx = db_pool.begin().await.unwrap(); let mut tx = db_pool.begin().await.unwrap();
@ -196,7 +202,7 @@ async fn get_progress(
headers: HeaderMap, headers: HeaderMap,
Path(document): Path<String>, Path(document): Path<String>,
) -> (StatusCode, Json<Value>) { ) -> (StatusCode, Json<Value>) {
let username = headers["x-auth-user"].to_str().unwrap_or(""); let username = headers["x-auth-user"].to_str().unwrap_or("").trim();
let password = headers["x-auth-key"].to_str().unwrap_or(""); let password = headers["x-auth-key"].to_str().unwrap_or("");
let mut tx = db_pool.begin().await.unwrap(); let mut tx = db_pool.begin().await.unwrap();

View file

@ -2,7 +2,7 @@
Description=KOReader Sync Server Description=KOReader Sync Server
After=syslog.target After=syslog.target
After=network.target After=network.target
After=redis.service After=postgresql.service
[Service] [Service]
RestartSec=2s RestartSec=2s
@ -11,7 +11,7 @@ User=myuser
Group=users Group=users
WorkingDirectory=/home/myuser WorkingDirectory=/home/myuser
ExecStart=/usr/local/bin/kosyncrs ExecStart=/usr/local/bin/kosyncrs
Environment="PG_URL=postgresql://kosync:password@localhost/kosync" Environment=PG_URL=postgresql://kosync:password@localhost/kosync PORT=3003
Restart=always Restart=always
[Install] [Install]