Browse Source

use postgresql

master
hole-thu 3 years ago
parent
commit
c9659acd6e
  1. 4
      Cargo.toml
  2. 23
      README.md
  3. 0
      migrations/postgres/.gitkeep
  4. 6
      migrations/postgres/00000000000000_diesel_initial_setup/down.sql
  5. 36
      migrations/postgres/00000000000000_diesel_initial_setup/up.sql
  6. 2
      migrations/postgres/2022-03-21-155550_create_posts/down.sql
  7. 23
      migrations/postgres/2022-03-21-155550_create_posts/up.sql
  8. 2
      migrations/postgres/2022-03-21-162914_create_users/down.sql
  9. 7
      migrations/postgres/2022-03-21-162914_create_users/up.sql
  10. 2
      migrations/postgres/2022-03-21-164718_create_comments/down.sql
  11. 14
      migrations/postgres/2022-03-21-164718_create_comments/up.sql
  12. 0
      migrations/sqlite3/2022-03-11-065048_create_posts/down.sql
  13. 2
      migrations/sqlite3/2022-03-11-065048_create_posts/up.sql
  14. 0
      migrations/sqlite3/2022-03-15-061041_create_users/down.sql
  15. 0
      migrations/sqlite3/2022-03-15-061041_create_users/up.sql
  16. 0
      migrations/sqlite3/2022-03-15-104943_create_comments/down.sql
  17. 0
      migrations/sqlite3/2022-03-15-104943_create_comments/up.sql
  18. 3
      src/api/operation.rs
  19. 3
      src/api/post.rs
  20. 4
      src/db_conn.rs
  21. 16
      src/models.rs
  22. 29
      src/schema.rs
  23. 50
      tools/migdb.py

4
Cargo.toml

@ -8,7 +8,7 @@ license = "AGPL-3.0"
[dependencies] [dependencies]
rocket = { version = "0.5.0-rc.1", features = ["json"] } rocket = { version = "0.5.0-rc.1", features = ["json"] }
diesel = { version = "1.4.8", features = ["sqlite", "chrono", "r2d2"] } diesel = { version = "1.4.8", features = ["postgres", "chrono"] }
redis = { version="0.21.5", features = ["aio", "async-std-comp"] } redis = { version="0.21.5", features = ["aio", "async-std-comp"] }
chrono = { version="0.*", features =["serde"] } chrono = { version="0.*", features =["serde"] }
rand = "0.*" rand = "0.*"
@ -17,4 +17,4 @@ sha2 = "0.*"
[dependencies.rocket_sync_db_pools] [dependencies.rocket_sync_db_pools]
version = "0.1.0-rc.1" version = "0.1.0-rc.1"
features = ["diesel_sqlite_pool"] features = ["diesel_postgres_pool"]

23
README.md

@ -1,2 +1,25 @@
# hole-backend-rust # hole-backend-rust
## 部署
### prepare database
```sql
CREATE USER hole CREATEDB;
ALTER USER hole WITH PASSWORD "hole_pass";
```
```
$ diesel setup
```
```sql
\c hole_v2
CREATE EXTENSION pg_trgm;
```
```
$ diesel run
$ python3 tools/migdb.py
```

0
migrations/postgres/.gitkeep

6
migrations/postgres/00000000000000_diesel_initial_setup/down.sql

@ -0,0 +1,6 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
DROP FUNCTION IF EXISTS diesel_set_updated_at();

36
migrations/postgres/00000000000000_diesel_initial_setup/up.sql

@ -0,0 +1,36 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
-- Sets up a trigger for the given table to automatically set a column called
-- `updated_at` whenever the row is modified (unless `updated_at` was included
-- in the modified columns)
--
-- # Example
--
-- ```sql
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
--
-- SELECT diesel_manage_updated_at('users');
-- ```
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
BEGIN
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
BEGIN
IF (
NEW IS DISTINCT FROM OLD AND
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
) THEN
NEW.updated_at := current_timestamp;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

2
migrations/postgres/2022-03-21-155550_create_posts/down.sql

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE posts;

23
migrations/postgres/2022-03-21-155550_create_posts/up.sql

@ -0,0 +1,23 @@
-- Your SQL goes here
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
author_hash VARCHAR NOT NULL,
content TEXT NOT NULL,
cw VARCHAR NOT NULL DEFAULT '',
author_title VARCHAR NOT NULL DEFAULT '',
is_tmp BOOLEAN NOT NULL DEFAULT FALSE,
n_attentions INTEGER NOT NULL DEFAULT 0,
n_comments INTEGER NOT NULL DEFAULT 0,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_comment_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
is_reported BOOLEAN NOT NULL DEFAULT FALSE,
hot_score INTEGER NOT NULL DEFAULT 0,
allow_search BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX posts_last_comment_time_idx ON posts (last_comment_time);
CREATE INDEX posts_hot_idx ON posts (hot_score);
CREATE INDEX posts_author_idx ON posts (author_title);
CREATE INDEX posts_cw_idx ON posts (cw);
CREATE INDEX posts_search_text_trgm_idx ON posts USING gin(content gin_trgm_ops);

2
migrations/postgres/2022-03-21-162914_create_users/down.sql

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE users;

7
migrations/postgres/2022-03-21-162914_create_users/up.sql

@ -0,0 +1,7 @@
-- Your SQL goes here
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL UNIQUE,
token VARCHAR NOT NULL UNIQUE,
is_admin BOOLEAN NOT NULL DEFAULT FALSE
);

2
migrations/postgres/2022-03-21-164718_create_comments/down.sql

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE comments;

14
migrations/postgres/2022-03-21-164718_create_comments/up.sql

@ -0,0 +1,14 @@
-- Your SQL goes here
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
author_hash VARCHAR NOT NULL,
author_title VARCHAR NOT NULL DEFAULT '',
is_tmp BOOLEAN NOT NULL DEFAULT FALSE,
content TEXT NOT NULL,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
allow_search BOOLEAN NOT NULL DEFAULT FALSE,
post_id INTEGER NOT NULL REFERENCES posts(id)
);
CREATE INDEX comments_postId_idx ON comments (post_id);

0
migrations/2022-03-11-065048_create_posts/down.sql → migrations/sqlite3/2022-03-11-065048_create_posts/down.sql

2
migrations/2022-03-11-065048_create_posts/up.sql → migrations/sqlite3/2022-03-11-065048_create_posts/up.sql

@ -14,7 +14,7 @@ CREATE TABLE posts (
is_deleted BOOLEAN NOT NULL DEFAULT FALSE, is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
is_reported BOOLEAN NOT NULL DEFAULT FALSE, is_reported BOOLEAN NOT NULL DEFAULT FALSE,
hot_score INTEGER NOT NULL DEFAULT 0, hot_score INTEGER NOT NULL DEFAULT 0,
allow_search BOOLEAN NOT NULL DEFAULT '' allow_search BOOLEAN NOT NULL DEFAULT FALSE
); );
CREATE INDEX posts_last_comment_time_idx ON posts (`last_comment_time`); CREATE INDEX posts_last_comment_time_idx ON posts (`last_comment_time`);
CREATE INDEX posts_hot_idx ON posts (`hot_score`) CREATE INDEX posts_hot_idx ON posts (`hot_score`)

0
migrations/2022-03-15-061041_create_users/down.sql → migrations/sqlite3/2022-03-15-061041_create_users/down.sql

0
migrations/2022-03-15-061041_create_users/up.sql → migrations/sqlite3/2022-03-15-061041_create_users/up.sql

0
migrations/2022-03-15-104943_create_comments/down.sql → migrations/sqlite3/2022-03-15-104943_create_comments/down.sql

0
migrations/2022-03-15-104943_create_comments/up.sql → migrations/sqlite3/2022-03-15-104943_create_comments/up.sql

3
src/api/operation.rs

@ -18,11 +18,12 @@ pub async fn delete(di: Form<DeleteInput>, user: CurrentUser, db: Db) -> API<Val
"cid" => { "cid" => {
let c = Comment::get(&db, di.id).await.m()?; let c = Comment::get(&db, di.id).await.m()?;
c.soft_delete(&user, &db).await?; c.soft_delete(&user, &db).await?;
let p = Post::get(&db, c.post_id).await.m()?;
p.change_n_comments(&db, -1).await.m()?;
} }
"pid" => { "pid" => {
let p = Post::get(&db, di.id).await.m()?; let p = Post::get(&db, di.id).await.m()?;
p.soft_delete(&user, &db).await?; p.soft_delete(&user, &db).await?;
p.change_n_comments(&db, -1).await.m()?;
} }
_ => return Err(APIError::PcError(NotAllowed)), _ => return Err(APIError::PcError(NotAllowed)),
} }

3
src/api/post.rs

@ -140,7 +140,7 @@ pub async fn get_list(
#[post("/dopost", data = "<poi>")] #[post("/dopost", data = "<poi>")]
pub async fn publish_post(poi: Form<PostInput>, user: CurrentUser, db: Db) -> JsonAPI { pub async fn publish_post(poi: Form<PostInput>, user: CurrentUser, db: Db) -> JsonAPI {
let r = Post::create( let p = Post::create(
&db, &db,
NewPost { NewPost {
content: poi.text.to_string(), content: poi.text.to_string(),
@ -156,7 +156,6 @@ pub async fn publish_post(poi: Form<PostInput>, user: CurrentUser, db: Db) -> Js
.m()?; .m()?;
// TODO: attention // TODO: attention
Ok(json!({ Ok(json!({
"data": r,
"code": 0 "code": 0
})) }))
} }

4
src/db_conn.rs

@ -1,7 +1,7 @@
use rocket_sync_db_pools::{database, diesel}; use rocket_sync_db_pools::{database, diesel};
pub type Conn = diesel::SqliteConnection; pub type Conn = diesel::pg::PgConnection;
#[database("sqlite_v2")] #[database("pg_v2")]
pub struct Db(Conn); pub struct Db(Conn);

16
src/models.rs

@ -20,6 +20,7 @@ macro_rules! get {
macro_rules! get_multi { macro_rules! get_multi {
($table:ident) => { ($table:ident) => {
pub async fn get_multi(db: &Db, ids: Vec<i32>) -> QueryResult<Vec<Self>> { pub async fn get_multi(db: &Db, ids: Vec<i32>) -> QueryResult<Vec<Self>> {
// can use eq(any()) for postgres
db.run(move |c| $table::table.filter($table::id.eq_any(ids)).load(c)) db.run(move |c| $table::table.filter($table::id.eq_any(ids)).load(c))
.await .await
} }
@ -106,9 +107,9 @@ impl Post {
.await .await
} }
pub async fn create(db: &Db, new_post: NewPost) -> QueryResult<usize> { pub async fn create(db: &Db, new_post: NewPost) -> QueryResult<Self> {
// TODO: tags // TODO: tags
db.run(move |c| insert_into(posts::table).values(&new_post).execute(c)) db.run(move |c| insert_into(posts::table).values(&new_post).get_result(c))
.await .await
} }
@ -169,6 +170,7 @@ pub struct Comment {
pub content: String, pub content: String,
pub create_time: NaiveDateTime, pub create_time: NaiveDateTime,
pub is_deleted: bool, pub is_deleted: bool,
pub allow_search: bool,
pub post_id: i32, pub post_id: i32,
} }
@ -187,9 +189,13 @@ impl Comment {
set_deleted!(comments); set_deleted!(comments);
pub async fn create(db: &Db, new_comment: NewComment) -> QueryResult<usize> { pub async fn create(db: &Db, new_comment: NewComment) -> QueryResult<Self> {
db.run(move |c| insert_into(comments::table).values(&new_comment).execute(c)) db.run(move |c| {
.await insert_into(comments::table)
.values(&new_comment)
.get_result(c)
})
.await
} }
pub async fn gets_by_post_id(db: &Db, post_id: i32) -> QueryResult<Vec<Self>> { pub async fn gets_by_post_id(db: &Db, post_id: i32) -> QueryResult<Vec<Self>> {

29
src/schema.rs

@ -1,40 +1,41 @@
table! { table! {
comments (id) { comments (id) {
id -> Integer, id -> Int4,
author_hash -> Text, author_hash -> Varchar,
author_title -> Text, author_title -> Varchar,
is_tmp -> Bool, is_tmp -> Bool,
content -> Text, content -> Text,
create_time -> Timestamp, create_time -> Timestamp,
is_deleted -> Bool, is_deleted -> Bool,
post_id -> Integer, allow_search -> Bool,
post_id -> Int4,
} }
} }
table! { table! {
posts (id) { posts (id) {
id -> Integer, id -> Int4,
author_hash -> Text, author_hash -> Varchar,
content -> Text, content -> Text,
cw -> Text, cw -> Varchar,
author_title -> Text, author_title -> Varchar,
is_tmp -> Bool, is_tmp -> Bool,
n_attentions -> Integer, n_attentions -> Int4,
n_comments -> Integer, n_comments -> Int4,
create_time -> Timestamp, create_time -> Timestamp,
last_comment_time -> Timestamp, last_comment_time -> Timestamp,
is_deleted -> Bool, is_deleted -> Bool,
is_reported -> Bool, is_reported -> Bool,
hot_score -> Integer, hot_score -> Int4,
allow_search -> Bool, allow_search -> Bool,
} }
} }
table! { table! {
users (id) { users (id) {
id -> Integer, id -> Int4,
name -> Text, name -> Varchar,
token -> Text, token -> Varchar,
is_admin -> Bool, is_admin -> Bool,
} }
} }

50
tools/migdb.py

@ -1,18 +1,25 @@
import sqlite3 import sqlite3
import psycopg2
from datetime import datetime from datetime import datetime
db_old = sqlite3.connect('hole.db') db_old = sqlite3.connect('hole.db')
db_new = sqlite3.connect('hole_v2.db') # change hole_pass to your real password
db_new = psycopg2.connect("postgres://hole:hole_pass@localhost/hole_v2")
c_old = db_old.cursor() c_old = db_old.cursor()
c_new = db_new.cursor() c_new = db_new.cursor()
searchable = {}
post_d = {}
dt = datetime.now()
def mig_post(): def mig_post():
rs = c_old.execute( rs = c_old.execute(
'SELECT id, name_hash, content, cw, author_title, ' 'SELECT id, name_hash, content, cw, author_title, '
'likenum, n_comments, timestamp, comment_timestamp, ' 'likenum, n_comments, timestamp, comment_timestamp, '
'deleted, is_reported, hot_score, allow_search ' 'deleted, is_reported, hot_score, allow_search '
'FROM post WHERE deleted = false' 'FROM post ORDER BY id'
) )
for r in rs: for r in rs:
@ -22,12 +29,29 @@ def mig_post():
r[8] = r[8] or r[7] # comment_timestamp r[8] = r[8] or r[7] # comment_timestamp
r[7] = datetime.fromtimestamp(r[7]) r[7] = datetime.fromtimestamp(r[7])
r[8] = datetime.fromtimestamp(r[8]) r[8] = datetime.fromtimestamp(r[8])
r[10] = r[10] or False # comment r[9] = bool(r[9])
r.insert(4, r[2].startswith('[tmp]\n')) r[10] = bool(r[10] or False) # comment
r[12] = bool(r[12])
searchable[r[0]] = r[12]
r.insert(5, r[2].startswith('[tmp]\n'))
# print(r)
post_d[r[0]] = r[1:]
max_id = r[0]
for i in range(1, max_id + 1):
r = post_d.get(i, [
'', '', '', '', False, 0, 0, dt, dt, True, False, 0, False
])
c_new.execute( c_new.execute(
'INSERT OR REPLACE INTO posts VALUES({})'.format(','.join(['?'] * 14)), (
'INSERT INTO posts VALUES({}) '
'ON CONFLICT (id) DO NOTHING'
).format(','.join(["DEFAULT"] + ['%s'] * 13)),
r r
) )
db_new.commit() db_new.commit()
@ -35,8 +59,10 @@ def mig_user():
rs = c_old.execute('SELECT name, token FROM user') rs = c_old.execute('SELECT name, token FROM user')
for r in rs: for r in rs:
# print(r)
c_new.execute( c_new.execute(
'INSERT OR REPLACE INTO users(name, token) VALUES(?, ?)', 'INSERT INTO users(name, token) VALUES(%s, %s) '
'ON CONFLICT (name) DO NOTHING',
r r
) )
db_new.commit() db_new.commit()
@ -57,11 +83,13 @@ def mig_comment():
r = list(r) r = list(r)
r[2] = r[2] or '' r[2] = r[2] or ''
r[4] = datetime.fromtimestamp(r[4]) r[4] = datetime.fromtimestamp(r[4])
r[5] = r[5] or False r[5] = bool(r[5] or False)
r.insert(2, r[3].startswith('[tmp]\n')) r.insert(6, searchable[r[6]])
r.insert(3, r[3].startswith('[tmp]\n'))
# print(r)
c_new.execute( c_new.execute(
'INSERT OR REPLACE INTO comments VALUES({})'.format(','.join(['?'] * 8)), 'INSERT INTO comments VALUES({})'.format(','.join(["DEFAULT"] + ['%s'] * 8)),
r r[1:]
) )
if not r: if not r:
break break
@ -71,8 +99,8 @@ def mig_comment():
if __name__ == '__main__': if __name__ == '__main__':
mig_post()
mig_user() mig_user()
mig_post()
mig_comment() mig_comment()
pass pass

Loading…
Cancel
Save