diff --git a/Dockerfile b/Dockerfile index 9a7cacc..e280385 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,39 @@ FROM node:10-jessie as node #If encounter Invalid cross-device error -run on host 'echo N | sudo tee /sys/module/overlay/parameters/metacopy' COPY ui /app/ui -RUN cd /app/ui && yarn && yarn build +WORKDIR /app/ui +RUN yarn +RUN yarn build FROM rust:1.33 as rust -COPY server /app/server + +# create a new empty shell project +WORKDIR /app +RUN USER=root cargo new server +WORKDIR /app/server + +# copy over your manifests +COPY server/Cargo.toml server/Cargo.lock ./ + +# this build step will cache your dependencies +RUN mkdir -p ./src/bin \ + && echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs +RUN cargo build --release --bin lemmy +RUN ls ./target/release/.fingerprint/ +RUN rm -r ./target/release/.fingerprint/server-* + +# copy your source tree +# RUN rm -rf ./src/ +COPY server/src ./src/ +COPY server/migrations ./migrations/ + +# build for release +RUN cargo build --frozen --release --bin lemmy +RUN mv /app/server/target/release/lemmy /app/lemmy + +# The output image +# FROM debian:stable-slim +# RUN apt-get -y update && apt-get install -y postgresql-client +# COPY --from=rust /app/server/target/release/lemmy /app/lemmy COPY --from=node /app/ui/dist /app/dist -RUN cd /app/server && cargo build --release -RUN mv /app/server/target/release/lemmy /app/ -WORKDIR /app/ EXPOSE 8536 diff --git a/README.md b/README.md index 1c518a4..75baef5 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,11 @@ Made with [Rust](https://www.rust-lang.org), [Actix](https://actix.rs/), [Infern ## Features - TBD -- -the name -Lead singer from motorhead. -The old school video game. -The furry rodents. +## Why's it called Lemmy? +- Lead singer from [motorhead](https://invidio.us/watch?v=pWB5JZRGl0U). +- The old school [video game](https://en.wikipedia.org/wiki/Lemmings_(video_game)). +- The [furry rodents](http://sunchild.fpwc.org/lemming-the-little-giant-of-the-north/). Goals r/ censorship diff --git a/docker-compose.yml b/docker-compose.yml index 940cd0f..03c7288 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,9 +10,9 @@ services: POSTGRES_DB: rrr healthcheck: test: ["CMD-SHELL", "pg_isready -U rrr"] - interval: 30s - timeout: 30s - retries: 3 + interval: 5s + timeout: 5s + retries: 20 lemmy: build: context: . @@ -22,6 +22,7 @@ services: environment: LEMMY_FRONT_END_DIR: /app/dist DATABASE_URL: postgres://rrr:rrr@db:5432/rrr + restart: always depends_on: db: condition: service_healthy diff --git a/server/Cargo.lock b/server/Cargo.lock index ce4e175..36d2a90 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1359,6 +1359,7 @@ dependencies = [ "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "jsonwebtoken 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/server/Cargo.toml b/server/Cargo.toml index fd3d577..93bd6ac 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -24,4 +24,5 @@ rand = "0.6.5" strum = "0.14.0" strum_macros = "0.14.0" jsonwebtoken = "*" -regex = "1" +regex = "*" +lazy_static = "*" diff --git a/server/migrations/2019-02-26-002946_create_user/down.sql b/server/migrations/2019-02-26-002946_create_user/down.sql index 606be6e..67a280d 100644 --- a/server/migrations/2019-02-26-002946_create_user/down.sql +++ b/server/migrations/2019-02-26-002946_create_user/down.sql @@ -1 +1,2 @@ -drop table user_ +drop table user_ban; +drop table user_; diff --git a/server/migrations/2019-02-26-002946_create_user/up.sql b/server/migrations/2019-02-26-002946_create_user/up.sql index 80e6e92..ea2c923 100644 --- a/server/migrations/2019-02-26-002946_create_user/up.sql +++ b/server/migrations/2019-02-26-002946_create_user/up.sql @@ -6,9 +6,18 @@ create table user_ ( password_encrypted text not null, email text unique, icon bytea, + admin boolean default false not null, + banned boolean default false not null, published timestamp not null default now(), updated timestamp, unique(name, fedi_name) ); +create table user_ban ( + id serial primary key, + user_id int references user_ on update cascade on delete cascade not null, + published timestamp not null default now(), + unique (user_id) +); + insert into user_ (name, fedi_name, password_encrypted) values ('admin', 'TBD', 'TBD'); diff --git a/server/migrations/2019-02-27-170003_create_community/down.sql b/server/migrations/2019-02-27-170003_create_community/down.sql index f293dfa..219588d 100644 --- a/server/migrations/2019-02-27-170003_create_community/down.sql +++ b/server/migrations/2019-02-27-170003_create_community/down.sql @@ -1,3 +1,5 @@ +drop table site; +drop table community_user_ban;; drop table community_moderator; drop table community_follower; drop table community; diff --git a/server/migrations/2019-02-27-170003_create_community/up.sql b/server/migrations/2019-02-27-170003_create_community/up.sql index 46b4df5..2d6856b 100644 --- a/server/migrations/2019-02-27-170003_create_community/up.sql +++ b/server/migrations/2019-02-27-170003_create_community/up.sql @@ -38,6 +38,7 @@ create table community ( description text, category_id int references category on update cascade on delete cascade not null, creator_id int references user_ on update cascade on delete cascade not null, + removed boolean default false, published timestamp not null default now(), updated timestamp ); @@ -46,14 +47,33 @@ create table community_moderator ( id serial primary key, community_id int references community on update cascade on delete cascade not null, user_id int references user_ on update cascade on delete cascade not null, - published timestamp not null default now() + published timestamp not null default now(), + unique (community_id, user_id) ); create table community_follower ( id serial primary key, community_id int references community on update cascade on delete cascade not null, user_id int references user_ on update cascade on delete cascade not null, - published timestamp not null default now() + published timestamp not null default now(), + unique (community_id, user_id) +); + +create table community_user_ban ( + id serial primary key, + community_id int references community on update cascade on delete cascade not null, + user_id int references user_ on update cascade on delete cascade not null, + published timestamp not null default now(), + unique (community_id, user_id) ); insert into community (name, title, category_id, creator_id) values ('main', 'The Default Community', 1, 1); + +create table site ( + id serial primary key, + name varchar(20) not null unique, + description text, + creator_id int references user_ on update cascade on delete cascade not null, + published timestamp not null default now(), + updated timestamp +); diff --git a/server/migrations/2019-03-03-163336_create_post/up.sql b/server/migrations/2019-03-03-163336_create_post/up.sql index aaa6911..c3b7c0b 100644 --- a/server/migrations/2019-03-03-163336_create_post/up.sql +++ b/server/migrations/2019-03-03-163336_create_post/up.sql @@ -5,6 +5,8 @@ create table post ( body text, creator_id int references user_ on update cascade on delete cascade not null, community_id int references community on update cascade on delete cascade not null, + removed boolean default false, + locked boolean default false, published timestamp not null default now(), updated timestamp ); diff --git a/server/migrations/2019-03-05-233828_create_comment/up.sql b/server/migrations/2019-03-05-233828_create_comment/up.sql index aa20d35..214d50a 100644 --- a/server/migrations/2019-03-05-233828_create_comment/up.sql +++ b/server/migrations/2019-03-05-233828_create_comment/up.sql @@ -4,6 +4,7 @@ create table comment ( post_id int references post on update cascade on delete cascade not null, parent_id int references comment on update cascade on delete cascade, content text not null, + removed boolean default false, published timestamp not null default now(), updated timestamp ); diff --git a/server/migrations/2019-03-30-212058_create_post_view/up.sql b/server/migrations/2019-03-30-212058_create_post_view/up.sql index 95789c7..ecf3280 100644 --- a/server/migrations/2019-03-30-212058_create_post_view/up.sql +++ b/server/migrations/2019-03-30-212058_create_post_view/up.sql @@ -30,7 +30,8 @@ select ap.*, u.id as user_id, coalesce(pl.score, 0) as my_vote, -(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed +(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed, +u.admin or (select cm.id::bool from community_moderator cm where u.id = cm.user_id and cm.community_id = ap.community_id) as am_mod from user_ u cross join all_post ap left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id @@ -41,33 +42,7 @@ select ap.*, null as user_id, null as my_vote, -null as subscribed +null as subscribed, +null as am_mod from all_post ap ; - -/* The old post view */ -/* create view post_view as */ -/* select */ -/* u.id as user_id, */ -/* pl.score as my_vote, */ -/* p.id as id, */ -/* p.name as name, */ -/* p.url, */ -/* p.body, */ -/* p.creator_id, */ -/* (select name from user_ where p.creator_id = user_.id) creator_name, */ -/* p.community_id, */ -/* (select name from community where p.community_id = community.id) as community_name, */ -/* (select count(*) from comment where comment.post_id = p.id) as number_of_comments, */ -/* coalesce(sum(pl.score) over (partition by p.id), 0) as score, */ -/* count (case when pl.score = 1 then 1 else null end) over (partition by p.id) as upvotes, */ -/* count (case when pl.score = -1 then 1 else null end) over (partition by p.id) as downvotes, */ -/* hot_rank(coalesce(sum(pl.score) over (partition by p.id) , 0), p.published) as hot_rank, */ -/* p.published, */ -/* p.updated */ -/* from user_ u */ -/* cross join post p */ -/* left join post_like pl on u.id = pl.user_id and p.id = pl.post_id; */ - - - diff --git a/server/migrations/2019-04-03-155205_create_community_view/down.sql b/server/migrations/2019-04-03-155205_create_community_view/down.sql index 6c7e870..67d12f6 100644 --- a/server/migrations/2019-04-03-155205_create_community_view/down.sql +++ b/server/migrations/2019-04-03-155205_create_community_view/down.sql @@ -1,3 +1,5 @@ drop view community_view; drop view community_moderator_view; drop view community_follower_view; +drop view community_user_ban_view; +drop view site_view; diff --git a/server/migrations/2019-04-03-155205_create_community_view/up.sql b/server/migrations/2019-04-03-155205_create_community_view/up.sql index 7c60874..1b73af5 100644 --- a/server/migrations/2019-04-03-155205_create_community_view/up.sql +++ b/server/migrations/2019-04-03-155205_create_community_view/up.sql @@ -13,7 +13,8 @@ with all_community as select ac.*, u.id as user_id, -cf.id::boolean as subscribed +cf.id::boolean as subscribed, +u.admin or (select cm.id::bool from community_moderator cm where u.id = cm.user_id and cm.community_id = ac.id) as am_mod from user_ u cross join all_community ac left join community_follower cf on u.id = cf.user_id and ac.id = cf.community_id @@ -23,7 +24,8 @@ union all select ac.*, null as user_id, -null as subscribed +null as subscribed, +null as am_mod from all_community ac ; @@ -38,3 +40,17 @@ select *, (select name from user_ u where cf.user_id = u.id) as user_name, (select name from community c where cf.community_id = c.id) as community_name from community_follower cf; + +create view community_user_ban_view as +select *, +(select name from user_ u where cm.user_id = u.id) as user_name, +(select name from community c where cm.community_id = c.id) as community_name +from community_user_ban cm; + +create view site_view as +select *, +(select name from user_ u where s.creator_id = u.id) as creator_name, +(select count(*) from user_) as number_of_users, +(select count(*) from post) as number_of_posts, +(select count(*) from comment) as number_of_comments +from site s; diff --git a/server/migrations/2019-04-03-155309_create_comment_view/up.sql b/server/migrations/2019-04-03-155309_create_comment_view/up.sql index a4d2be9..a73b618 100644 --- a/server/migrations/2019-04-03-155309_create_comment_view/up.sql +++ b/server/migrations/2019-04-03-155309_create_comment_view/up.sql @@ -3,7 +3,9 @@ with all_comment as ( select c.*, - (select name from user_ where c.creator_id = user_.id) creator_name, + (select community_id from post p where p.id = c.post_id), + (select cb.id::bool from community_user_ban cb where c.creator_id = cb.user_id) as banned, + (select name from user_ where c.creator_id = user_.id) as creator_name, coalesce(sum(cl.score), 0) as score, count (case when cl.score = 1 then 1 else null end) as upvotes, count (case when cl.score = -1 then 1 else null end) as downvotes @@ -15,7 +17,8 @@ with all_comment as select ac.*, u.id as user_id, -coalesce(cl.score, 0) as my_vote +coalesce(cl.score, 0) as my_vote, +u.admin or (select cm.id::bool from community_moderator cm, post p where u.id = cm.user_id and ac.post_id = p.id and p.community_id = cm.community_id) as am_mod from user_ u cross join all_comment ac left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id @@ -25,6 +28,7 @@ union all select ac.*, null as user_id, - null as my_vote + null as my_vote, + null as am_mod from all_comment ac ; diff --git a/server/migrations/2019-04-07-003142_create_moderation_logs/down.sql b/server/migrations/2019-04-07-003142_create_moderation_logs/down.sql new file mode 100644 index 0000000..888a87f --- /dev/null +++ b/server/migrations/2019-04-07-003142_create_moderation_logs/down.sql @@ -0,0 +1,8 @@ +drop table mod_remove_post; +drop table mod_lock_post; +drop table mod_remove_comment; +drop table mod_remove_community; +drop table mod_ban; +drop table mod_ban_from_community; +drop table mod_add; +drop table mod_add_community; diff --git a/server/migrations/2019-04-07-003142_create_moderation_logs/up.sql b/server/migrations/2019-04-07-003142_create_moderation_logs/up.sql new file mode 100644 index 0000000..3b320d8 --- /dev/null +++ b/server/migrations/2019-04-07-003142_create_moderation_logs/up.sql @@ -0,0 +1,76 @@ +create table mod_remove_post ( + id serial primary key, + mod_user_id int references user_ on update cascade on delete cascade not null, + post_id int references post on update cascade on delete cascade not null, + reason text, + removed boolean default true, + when_ timestamp not null default now() +); + +create table mod_lock_post ( + id serial primary key, + mod_user_id int references user_ on update cascade on delete cascade not null, + post_id int references post on update cascade on delete cascade not null, + locked boolean default true, + when_ timestamp not null default now() +); + +create table mod_remove_comment ( + id serial primary key, + mod_user_id int references user_ on update cascade on delete cascade not null, + comment_id int references comment on update cascade on delete cascade not null, + reason text, + removed boolean default true, + when_ timestamp not null default now() +); + +create table mod_remove_community ( + id serial primary key, + mod_user_id int references user_ on update cascade on delete cascade not null, + community_id int references community on update cascade on delete cascade not null, + reason text, + removed boolean default true, + expires timestamp, + when_ timestamp not null default now() +); + +-- TODO make sure you can't ban other mods +create table mod_ban_from_community ( + id serial primary key, + mod_user_id int references user_ on update cascade on delete cascade not null, + other_user_id int references user_ on update cascade on delete cascade not null, + community_id int references community on update cascade on delete cascade not null, + reason text, + banned boolean default true, + expires timestamp, + when_ timestamp not null default now() +); + +create table mod_ban ( + id serial primary key, + mod_user_id int references user_ on update cascade on delete cascade not null, + other_user_id int references user_ on update cascade on delete cascade not null, + reason text, + banned boolean default true, + expires timestamp, + when_ timestamp not null default now() +); + +create table mod_add_community ( + id serial primary key, + mod_user_id int references user_ on update cascade on delete cascade not null, + other_user_id int references user_ on update cascade on delete cascade not null, + community_id int references community on update cascade on delete cascade not null, + removed boolean default false, + when_ timestamp not null default now() +); + +-- When removed is false that means kicked +create table mod_add ( + id serial primary key, + mod_user_id int references user_ on update cascade on delete cascade not null, + other_user_id int references user_ on update cascade on delete cascade not null, + removed boolean default false, + when_ timestamp not null default now() +); + diff --git a/server/migrations/2019-04-08-015947_create_user_view/up.sql b/server/migrations/2019-04-08-015947_create_user_view/up.sql index 69d052d..08eb56c 100644 --- a/server/migrations/2019-04-08-015947_create_user_view/up.sql +++ b/server/migrations/2019-04-08-015947_create_user_view/up.sql @@ -2,10 +2,11 @@ create view user_view as select id, name, fedi_name, +admin, +banned, published, (select count(*) from post p where p.creator_id = u.id) as number_of_posts, (select coalesce(sum(score), 0) from post p, post_like pl where u.id = p.creator_id and p.id = pl.post_id) as post_score, (select count(*) from comment c where c.creator_id = u.id) as number_of_comments, (select coalesce(sum(score), 0) from comment c, comment_like cl where u.id = c.creator_id and c.id = cl.comment_id) as comment_score from user_ u; - diff --git a/server/migrations/2019-04-11-144915_create_mod_views/down.sql b/server/migrations/2019-04-11-144915_create_mod_views/down.sql new file mode 100644 index 0000000..95018f3 --- /dev/null +++ b/server/migrations/2019-04-11-144915_create_mod_views/down.sql @@ -0,0 +1,8 @@ +drop view mod_remove_post_view; +drop view mod_lock_post_view; +drop view mod_remove_comment_view; +drop view mod_remove_community_view; +drop view mod_ban_from_community_view; +drop view mod_ban_view; +drop view mod_add_community_view; +drop view mod_add_view; diff --git a/server/migrations/2019-04-11-144915_create_mod_views/up.sql b/server/migrations/2019-04-11-144915_create_mod_views/up.sql new file mode 100644 index 0000000..908028d --- /dev/null +++ b/server/migrations/2019-04-11-144915_create_mod_views/up.sql @@ -0,0 +1,61 @@ +create view mod_remove_post_view as +select mrp.*, +(select name from user_ u where mrp.mod_user_id = u.id) as mod_user_name, +(select name from post p where mrp.post_id = p.id) as post_name, +(select c.id from post p, community c where mrp.post_id = p.id and p.community_id = c.id) as community_id, +(select c.name from post p, community c where mrp.post_id = p.id and p.community_id = c.id) as community_name +from mod_remove_post mrp; + +create view mod_lock_post_view as +select mlp.*, +(select name from user_ u where mlp.mod_user_id = u.id) as mod_user_name, +(select name from post p where mlp.post_id = p.id) as post_name, +(select c.id from post p, community c where mlp.post_id = p.id and p.community_id = c.id) as community_id, +(select c.name from post p, community c where mlp.post_id = p.id and p.community_id = c.id) as community_name +from mod_lock_post mlp; + +create view mod_remove_comment_view as +select mrc.*, +(select name from user_ u where mrc.mod_user_id = u.id) as mod_user_name, +(select c.id from comment c where mrc.comment_id = c.id) as comment_user_id, +(select name from user_ u, comment c where mrc.comment_id = c.id and u.id = c.creator_id) as comment_user_name, +(select content from comment c where mrc.comment_id = c.id) as comment_content, +(select p.id from post p, comment c where mrc.comment_id = c.id and c.post_id = p.id) as post_id, +(select p.name from post p, comment c where mrc.comment_id = c.id and c.post_id = p.id) as post_name, +(select co.id from comment c, post p, community co where mrc.comment_id = c.id and c.post_id = p.id and p.community_id = co.id) as community_id, +(select co.name from comment c, post p, community co where mrc.comment_id = c.id and c.post_id = p.id and p.community_id = co.id) as community_name +from mod_remove_comment mrc; + +create view mod_remove_community_view as +select mrc.*, +(select name from user_ u where mrc.mod_user_id = u.id) as mod_user_name, +(select c.name from community c where mrc.community_id = c.id) as community_name +from mod_remove_community mrc; + +create view mod_ban_from_community_view as +select mb.*, +(select name from user_ u where mb.mod_user_id = u.id) as mod_user_name, +(select name from user_ u where mb.other_user_id = u.id) as other_user_name, +(select name from community c where mb.community_id = c.id) as community_name +from mod_ban_from_community mb; + +create view mod_ban_view as +select mb.*, +(select name from user_ u where mb.mod_user_id = u.id) as mod_user_name, +(select name from user_ u where mb.other_user_id = u.id) as other_user_name +from mod_ban_from_community mb; + + +create view mod_add_community_view as +select ma.*, +(select name from user_ u where ma.mod_user_id = u.id) as mod_user_name, +(select name from user_ u where ma.other_user_id = u.id) as other_user_name, +(select name from community c where ma.community_id = c.id) as community_name +from mod_add_community ma; + + +create view mod_add_view as +select ma.*, +(select name from user_ u where ma.mod_user_id = u.id) as mod_user_name, +(select name from user_ u where ma.other_user_id = u.id) as other_user_name +from mod_add ma; diff --git a/server/src/actions/comment.rs b/server/src/actions/comment.rs index ff50285..f6eee5f 100644 --- a/server/src/actions/comment.rs +++ b/server/src/actions/comment.rs @@ -22,6 +22,7 @@ pub struct Comment { pub post_id: i32, pub parent_id: Option, pub content: String, + pub removed: Option, pub published: chrono::NaiveDateTime, pub updated: Option } @@ -33,6 +34,7 @@ pub struct CommentForm { pub post_id: i32, pub parent_id: Option, pub content: String, + pub removed: Option, pub updated: Option } @@ -135,6 +137,8 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + admin: false, + banned: false, updated: None }; @@ -146,6 +150,7 @@ mod tests { description: None, category_id: 1, creator_id: inserted_user.id, + removed: None, updated: None }; @@ -157,6 +162,8 @@ mod tests { url: None, body: None, community_id: inserted_community.id, + removed: None, + locked: None, updated: None }; @@ -166,6 +173,7 @@ mod tests { content: "A test comment".into(), creator_id: inserted_user.id, post_id: inserted_post.id, + removed: None, parent_id: None, updated: None }; @@ -177,6 +185,7 @@ mod tests { content: "A test comment".into(), creator_id: inserted_user.id, post_id: inserted_post.id, + removed: Some(false), parent_id: None, published: inserted_comment.published, updated: None @@ -187,6 +196,7 @@ mod tests { creator_id: inserted_user.id, post_id: inserted_post.id, parent_id: Some(inserted_comment.id), + removed: None, updated: None }; diff --git a/server/src/actions/comment_view.rs b/server/src/actions/comment_view.rs index 3b4e00b..d6d9e40 100644 --- a/server/src/actions/comment_view.rs +++ b/server/src/actions/comment_view.rs @@ -13,14 +13,18 @@ table! { post_id -> Int4, parent_id -> Nullable, content -> Text, + removed -> Nullable, published -> Timestamp, updated -> Nullable, + community_id -> Int4, + banned -> Nullable, creator_name -> Varchar, score -> BigInt, upvotes -> BigInt, downvotes -> BigInt, user_id -> Nullable, my_vote -> Nullable, + am_mod -> Nullable, } } @@ -32,14 +36,18 @@ pub struct CommentView { pub post_id: i32, pub parent_id: Option, pub content: String, + pub removed: Option, pub published: chrono::NaiveDateTime, pub updated: Option, + pub community_id: i32, + pub banned: Option, pub creator_name: String, pub score: i64, pub upvotes: i64, pub downvotes: i64, pub user_id: Option, pub my_vote: Option, + pub am_mod: Option, } impl CommentView { @@ -130,6 +138,8 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + admin: false, + banned: false, updated: None }; @@ -141,6 +151,7 @@ mod tests { description: None, category_id: 1, creator_id: inserted_user.id, + removed: None, updated: None }; @@ -152,6 +163,8 @@ mod tests { url: None, body: None, community_id: inserted_community.id, + removed: None, + locked: None, updated: None }; @@ -162,6 +175,7 @@ mod tests { creator_id: inserted_user.id, post_id: inserted_post.id, parent_id: None, + removed: None, updated: None }; @@ -181,7 +195,10 @@ mod tests { content: "A test comment 32".into(), creator_id: inserted_user.id, post_id: inserted_post.id, + community_id: inserted_community.id, parent_id: None, + removed: Some(false), + banned: None, published: inserted_comment.published, updated: None, creator_name: inserted_user.name.to_owned(), @@ -189,7 +206,8 @@ mod tests { downvotes: 0, upvotes: 1, user_id: None, - my_vote: None + my_vote: None, + am_mod: None, }; let expected_comment_view_with_user = CommentView { @@ -197,7 +215,10 @@ mod tests { content: "A test comment 32".into(), creator_id: inserted_user.id, post_id: inserted_post.id, + community_id: inserted_community.id, parent_id: None, + removed: Some(false), + banned: None, published: inserted_comment.published, updated: None, creator_name: inserted_user.name.to_owned(), @@ -206,6 +227,7 @@ mod tests { upvotes: 1, user_id: Some(inserted_user.id), my_vote: Some(1), + am_mod: None, }; let read_comment_views_no_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, None, 999).unwrap(); diff --git a/server/src/actions/community.rs b/server/src/actions/community.rs index 1c6343d..ac33193 100644 --- a/server/src/actions/community.rs +++ b/server/src/actions/community.rs @@ -1,9 +1,9 @@ extern crate diesel; -use schema::{community, community_moderator, community_follower}; +use schema::{community, community_moderator, community_follower, community_user_ban, site}; use diesel::*; use diesel::result::Error; use serde::{Deserialize, Serialize}; -use {Crud, Followable, Joinable}; +use {Crud, Followable, Joinable, Bannable}; #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] #[table_name="community"] @@ -14,6 +14,7 @@ pub struct Community { pub description: Option, pub category_id: i32, pub creator_id: i32, + pub removed: Option, pub published: chrono::NaiveDateTime, pub updated: Option } @@ -26,44 +27,10 @@ pub struct CommunityForm { pub description: Option, pub category_id: i32, pub creator_id: i32, + pub removed: Option, pub updated: Option } -#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] -#[belongs_to(Community)] -#[table_name = "community_moderator"] -pub struct CommunityModerator { - pub id: i32, - pub community_id: i32, - pub user_id: i32, - pub published: chrono::NaiveDateTime, -} - -#[derive(Insertable, AsChangeset, Clone)] -#[table_name="community_moderator"] -pub struct CommunityModeratorForm { - pub community_id: i32, - pub user_id: i32, -} - -#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] -#[belongs_to(Community)] -#[table_name = "community_follower"] -pub struct CommunityFollower { - pub id: i32, - pub community_id: i32, - pub user_id: i32, - pub published: chrono::NaiveDateTime, -} - -#[derive(Insertable, AsChangeset, Clone)] -#[table_name="community_follower"] -pub struct CommunityFollowerForm { - pub community_id: i32, - pub user_id: i32, -} - - impl Crud for Community { fn read(conn: &PgConnection, community_id: i32) -> Result { use schema::community::dsl::*; @@ -92,20 +59,21 @@ impl Crud for Community { } } -impl Followable for CommunityFollower { - fn follow(conn: &PgConnection, community_follower_form: &CommunityFollowerForm) -> Result { - use schema::community_follower::dsl::*; - insert_into(community_follower) - .values(community_follower_form) - .get_result::(conn) - } - fn ignore(conn: &PgConnection, community_follower_form: &CommunityFollowerForm) -> Result { - use schema::community_follower::dsl::*; - diesel::delete(community_follower - .filter(community_id.eq(&community_follower_form.community_id)) - .filter(user_id.eq(&community_follower_form.user_id))) - .execute(conn) - } +#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] +#[belongs_to(Community)] +#[table_name = "community_moderator"] +pub struct CommunityModerator { + pub id: i32, + pub community_id: i32, + pub user_id: i32, + pub published: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone)] +#[table_name="community_moderator"] +pub struct CommunityModeratorForm { + pub community_id: i32, + pub user_id: i32, } impl Joinable for CommunityModerator { @@ -125,6 +93,120 @@ impl Joinable for CommunityModerator { } } +#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] +#[belongs_to(Community)] +#[table_name = "community_user_ban"] +pub struct CommunityUserBan { + pub id: i32, + pub community_id: i32, + pub user_id: i32, + pub published: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone)] +#[table_name="community_user_ban"] +pub struct CommunityUserBanForm { + pub community_id: i32, + pub user_id: i32, +} + +impl Bannable for CommunityUserBan { + fn ban(conn: &PgConnection, community_user_ban_form: &CommunityUserBanForm) -> Result { + use schema::community_user_ban::dsl::*; + insert_into(community_user_ban) + .values(community_user_ban_form) + .get_result::(conn) + } + + fn unban(conn: &PgConnection, community_user_ban_form: &CommunityUserBanForm) -> Result { + use schema::community_user_ban::dsl::*; + diesel::delete(community_user_ban + .filter(community_id.eq(community_user_ban_form.community_id)) + .filter(user_id.eq(community_user_ban_form.user_id))) + .execute(conn) + } +} + +#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] +#[belongs_to(Community)] +#[table_name = "community_follower"] +pub struct CommunityFollower { + pub id: i32, + pub community_id: i32, + pub user_id: i32, + pub published: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone)] +#[table_name="community_follower"] +pub struct CommunityFollowerForm { + pub community_id: i32, + pub user_id: i32, +} + +impl Followable for CommunityFollower { + fn follow(conn: &PgConnection, community_follower_form: &CommunityFollowerForm) -> Result { + use schema::community_follower::dsl::*; + insert_into(community_follower) + .values(community_follower_form) + .get_result::(conn) + } + fn ignore(conn: &PgConnection, community_follower_form: &CommunityFollowerForm) -> Result { + use schema::community_follower::dsl::*; + diesel::delete(community_follower + .filter(community_id.eq(&community_follower_form.community_id)) + .filter(user_id.eq(&community_follower_form.user_id))) + .execute(conn) + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] +#[table_name="site"] +pub struct Site { + pub id: i32, + pub name: String, + pub description: Option, + pub creator_id: i32, + pub published: chrono::NaiveDateTime, + pub updated: Option +} + +#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] +#[table_name="site"] +pub struct SiteForm { + pub name: String, + pub description: Option, + pub creator_id: i32, + pub updated: Option +} + +impl Crud for Site { + fn read(conn: &PgConnection, _site_id: i32) -> Result { + use schema::site::dsl::*; + site.first::(conn) + } + + fn delete(conn: &PgConnection, site_id: i32) -> Result { + use schema::site::dsl::*; + diesel::delete(site.find(site_id)) + .execute(conn) + } + + fn create(conn: &PgConnection, new_site: &SiteForm) -> Result { + use schema::site::dsl::*; + insert_into(site) + .values(new_site) + .get_result::(conn) + } + + fn update(conn: &PgConnection, site_id: i32, new_site: &SiteForm) -> Result { + use schema::site::dsl::*; + diesel::update(site.find(site_id)) + .set(new_site) + .get_result::(conn) + } +} + #[cfg(test)] mod tests { use establish_connection; @@ -136,11 +218,13 @@ mod tests { let conn = establish_connection(); let new_user = UserForm { - name: "bob".into(), + name: "bobbee".into(), fedi_name: "rrf".into(), preferred_username: None, password_encrypted: "nope".into(), email: None, + admin: false, + banned: false, updated: None }; @@ -152,6 +236,7 @@ mod tests { title: "nada".to_owned(), description: None, category_id: 1, + removed: None, updated: None, }; @@ -164,11 +249,11 @@ mod tests { title: "nada".to_owned(), description: None, category_id: 1, + removed: Some(false), published: inserted_community.published, updated: None }; - let community_follower_form = CommunityFollowerForm { community_id: inserted_community.id, user_id: inserted_user.id @@ -176,6 +261,7 @@ mod tests { let inserted_community_follower = CommunityFollower::follow(&conn, &community_follower_form).unwrap(); + let expected_community_follower = CommunityFollower { id: inserted_community_follower.id, community_id: inserted_community.id, @@ -197,10 +283,25 @@ mod tests { published: inserted_community_user.published }; + let community_user_ban_form = CommunityUserBanForm { + community_id: inserted_community.id, + user_id: inserted_user.id + }; + + let inserted_community_user_ban = CommunityUserBan::ban(&conn, &community_user_ban_form).unwrap(); + + let expected_community_user_ban = CommunityUserBan { + id: inserted_community_user_ban.id, + community_id: inserted_community.id, + user_id: inserted_user.id, + published: inserted_community_user_ban.published + }; + let read_community = Community::read(&conn, inserted_community.id).unwrap(); let updated_community = Community::update(&conn, inserted_community.id, &new_community).unwrap(); let ignored_community = CommunityFollower::ignore(&conn, &community_follower_form).unwrap(); let left_community = CommunityModerator::leave(&conn, &community_user_form).unwrap(); + let unban = CommunityUserBan::unban(&conn, &community_user_ban_form).unwrap(); let num_deleted = Community::delete(&conn, inserted_community.id).unwrap(); User_::delete(&conn, inserted_user.id).unwrap(); @@ -209,8 +310,10 @@ mod tests { assert_eq!(expected_community, updated_community); assert_eq!(expected_community_follower, inserted_community_follower); assert_eq!(expected_community_user, inserted_community_user); + assert_eq!(expected_community_user_ban, inserted_community_user_ban); assert_eq!(1, ignored_community); assert_eq!(1, left_community); + assert_eq!(1, unban); // assert_eq!(2, loaded_count); assert_eq!(1, num_deleted); diff --git a/server/src/actions/community_view.rs b/server/src/actions/community_view.rs index 185484b..078e7a6 100644 --- a/server/src/actions/community_view.rs +++ b/server/src/actions/community_view.rs @@ -2,6 +2,7 @@ extern crate diesel; use diesel::*; use diesel::result::Error; use serde::{Deserialize, Serialize}; +use {SortType}; table! { community_view (id) { @@ -11,6 +12,7 @@ table! { description -> Nullable, category_id -> Int4, creator_id -> Int4, + removed -> Nullable, published -> Timestamp, updated -> Nullable, creator_name -> Varchar, @@ -20,6 +22,7 @@ table! { number_of_comments -> BigInt, user_id -> Nullable, subscribed -> Nullable, + am_mod -> Nullable, } } @@ -45,6 +48,32 @@ table! { } } +table! { + community_user_ban_view (id) { + id -> Int4, + community_id -> Int4, + user_id -> Int4, + published -> Timestamp, + user_name -> Varchar, + community_name -> Varchar, + } +} + +table! { + site_view (id) { + id -> Int4, + name -> Varchar, + description -> Nullable, + creator_id -> Int4, + published -> Timestamp, + updated -> Nullable, + creator_name -> Varchar, + number_of_users -> BigInt, + number_of_posts -> BigInt, + number_of_comments -> BigInt, + } +} + #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] #[table_name="community_view"] pub struct CommunityView { @@ -54,6 +83,7 @@ pub struct CommunityView { pub description: Option, pub category_id: i32, pub creator_id: i32, + pub removed: Option, pub published: chrono::NaiveDateTime, pub updated: Option, pub creator_name: String, @@ -63,6 +93,7 @@ pub struct CommunityView { pub number_of_comments: i64, pub user_id: Option, pub subscribed: Option, + pub am_mod: Option, } impl CommunityView { @@ -83,20 +114,30 @@ impl CommunityView { query.first::(conn) } - pub fn list_all(conn: &PgConnection, from_user_id: Option) -> Result, Error> { + pub fn list(conn: &PgConnection, from_user_id: Option, sort: SortType, limit: Option) -> Result, Error> { use actions::community_view::community_view::dsl::*; let mut query = community_view.into_boxed(); + + // The view lets you pass a null user_id, if you're not logged in - if let Some(from_user_id) = from_user_id { - query = query.filter(user_id.eq(from_user_id)) - .order_by((subscribed.desc(), number_of_subscribers.desc())); - } else { - query = query.filter(user_id.is_null()) - .order_by(number_of_subscribers.desc()); + + match sort { + SortType::New => query = query.order_by(published.desc()).filter(user_id.is_null()), + SortType::TopAll => { + match from_user_id { + Some(from_user_id) => query = query.filter(user_id.eq(from_user_id)).order_by((subscribed.desc(), number_of_subscribers.desc())), + None => query = query.order_by(number_of_subscribers.desc()).filter(user_id.is_null()) + } + } + _ => () }; - query.load::(conn) + if let Some(limit) = limit { + query = query.limit(limit); + }; + + query.filter(removed.eq(false)).load::(conn) } } @@ -146,3 +187,58 @@ impl CommunityFollowerView { community_follower_view.filter(user_id.eq(from_user_id)).load::(conn) } } + + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="community_user_ban_view"] +pub struct CommunityUserBanView { + pub id: i32, + pub community_id: i32, + pub user_id: i32, + pub published: chrono::NaiveDateTime, + pub user_name : String, + pub community_name: String, +} + +impl CommunityUserBanView { + pub fn for_community(conn: &PgConnection, from_community_id: i32) -> Result, Error> { + use actions::community_view::community_user_ban_view::dsl::*; + community_user_ban_view.filter(community_id.eq(from_community_id)).load::(conn) + } + + pub fn for_user(conn: &PgConnection, from_user_id: i32) -> Result, Error> { + use actions::community_view::community_user_ban_view::dsl::*; + community_user_ban_view.filter(user_id.eq(from_user_id)).load::(conn) + } + + pub fn get(conn: &PgConnection, from_user_id: i32, from_community_id: i32) -> Result { + use actions::community_view::community_user_ban_view::dsl::*; + community_user_ban_view + .filter(user_id.eq(from_user_id)) + .filter(community_id.eq(from_community_id)) + .first::(conn) + } +} + + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="site_view"] +pub struct SiteView { + pub id: i32, + pub name: String, + pub description: Option, + pub creator_id: i32, + pub published: chrono::NaiveDateTime, + pub updated: Option, + pub creator_name: String, + pub number_of_users: i64, + pub number_of_posts: i64, + pub number_of_comments: i64, +} + +impl SiteView { + pub fn read(conn: &PgConnection) -> Result { + use actions::community_view::site_view::dsl::*; + site_view.first::(conn) + } +} diff --git a/server/src/actions/mod.rs b/server/src/actions/mod.rs index 819d5cd..ece1e88 100644 --- a/server/src/actions/mod.rs +++ b/server/src/actions/mod.rs @@ -7,3 +7,5 @@ pub mod comment_view; pub mod category; pub mod community_view; pub mod user_view; +pub mod moderator; +pub mod moderator_views; diff --git a/server/src/actions/moderator.rs b/server/src/actions/moderator.rs new file mode 100644 index 0000000..a97b212 --- /dev/null +++ b/server/src/actions/moderator.rs @@ -0,0 +1,655 @@ +extern crate diesel; +use schema::{mod_remove_post, mod_lock_post, mod_remove_comment, mod_remove_community, mod_ban_from_community, mod_ban, mod_add_community, mod_add}; +use diesel::*; +use diesel::result::Error; +use serde::{Deserialize, Serialize}; +use {Crud}; + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] +#[table_name="mod_remove_post"] +pub struct ModRemovePost { + pub id: i32, + pub mod_user_id: i32, + pub post_id: i32, + pub reason: Option, + pub removed: Option, + pub when_: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] +#[table_name="mod_remove_post"] +pub struct ModRemovePostForm { + pub mod_user_id: i32, + pub post_id: i32, + pub reason: Option, + pub removed: Option, +} + +impl Crud for ModRemovePost { + fn read(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_remove_post::dsl::*; + mod_remove_post.find(from_id) + .first::(conn) + } + + fn delete(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_remove_post::dsl::*; + diesel::delete(mod_remove_post.find(from_id)) + .execute(conn) + } + + fn create(conn: &PgConnection, form: &ModRemovePostForm) -> Result { + use schema::mod_remove_post::dsl::*; + insert_into(mod_remove_post) + .values(form) + .get_result::(conn) + } + + fn update(conn: &PgConnection, from_id: i32, form: &ModRemovePostForm) -> Result { + use schema::mod_remove_post::dsl::*; + diesel::update(mod_remove_post.find(from_id)) + .set(form) + .get_result::(conn) + } +} + + + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] +#[table_name="mod_lock_post"] +pub struct ModLockPost { + pub id: i32, + pub mod_user_id: i32, + pub post_id: i32, + pub locked: Option, + pub when_: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] +#[table_name="mod_lock_post"] +pub struct ModLockPostForm { + pub mod_user_id: i32, + pub post_id: i32, + pub locked: Option, +} + +impl Crud for ModLockPost { + fn read(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_lock_post::dsl::*; + mod_lock_post.find(from_id) + .first::(conn) + } + + fn delete(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_lock_post::dsl::*; + diesel::delete(mod_lock_post.find(from_id)) + .execute(conn) + } + + fn create(conn: &PgConnection, form: &ModLockPostForm) -> Result { + use schema::mod_lock_post::dsl::*; + insert_into(mod_lock_post) + .values(form) + .get_result::(conn) + } + + fn update(conn: &PgConnection, from_id: i32, form: &ModLockPostForm) -> Result { + use schema::mod_lock_post::dsl::*; + diesel::update(mod_lock_post.find(from_id)) + .set(form) + .get_result::(conn) + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] +#[table_name="mod_remove_comment"] +pub struct ModRemoveComment { + pub id: i32, + pub mod_user_id: i32, + pub comment_id: i32, + pub reason: Option, + pub removed: Option, + pub when_: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] +#[table_name="mod_remove_comment"] +pub struct ModRemoveCommentForm { + pub mod_user_id: i32, + pub comment_id: i32, + pub reason: Option, + pub removed: Option, +} + +impl Crud for ModRemoveComment { + fn read(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_remove_comment::dsl::*; + mod_remove_comment.find(from_id) + .first::(conn) + } + + fn delete(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_remove_comment::dsl::*; + diesel::delete(mod_remove_comment.find(from_id)) + .execute(conn) + } + + fn create(conn: &PgConnection, form: &ModRemoveCommentForm) -> Result { + use schema::mod_remove_comment::dsl::*; + insert_into(mod_remove_comment) + .values(form) + .get_result::(conn) + } + + fn update(conn: &PgConnection, from_id: i32, form: &ModRemoveCommentForm) -> Result { + use schema::mod_remove_comment::dsl::*; + diesel::update(mod_remove_comment.find(from_id)) + .set(form) + .get_result::(conn) + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] +#[table_name="mod_remove_community"] +pub struct ModRemoveCommunity { + pub id: i32, + pub mod_user_id: i32, + pub community_id: i32, + pub reason: Option, + pub removed: Option, + pub expires: Option, + pub when_: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] +#[table_name="mod_remove_community"] +pub struct ModRemoveCommunityForm { + pub mod_user_id: i32, + pub community_id: i32, + pub reason: Option, + pub removed: Option, + pub expires: Option, +} + +impl Crud for ModRemoveCommunity { + fn read(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_remove_community::dsl::*; + mod_remove_community.find(from_id) + .first::(conn) + } + + fn delete(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_remove_community::dsl::*; + diesel::delete(mod_remove_community.find(from_id)) + .execute(conn) + } + + fn create(conn: &PgConnection, form: &ModRemoveCommunityForm) -> Result { + use schema::mod_remove_community::dsl::*; + insert_into(mod_remove_community) + .values(form) + .get_result::(conn) + } + + fn update(conn: &PgConnection, from_id: i32, form: &ModRemoveCommunityForm) -> Result { + use schema::mod_remove_community::dsl::*; + diesel::update(mod_remove_community.find(from_id)) + .set(form) + .get_result::(conn) + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] +#[table_name="mod_ban_from_community"] +pub struct ModBanFromCommunity { + pub id: i32, + pub mod_user_id: i32, + pub other_user_id: i32, + pub community_id: i32, + pub reason: Option, + pub banned: Option, + pub expires: Option, + pub when_: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] +#[table_name="mod_ban_from_community"] +pub struct ModBanFromCommunityForm { + pub mod_user_id: i32, + pub other_user_id: i32, + pub community_id: i32, + pub reason: Option, + pub banned: Option, + pub expires: Option, +} + +impl Crud for ModBanFromCommunity { + fn read(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_ban_from_community::dsl::*; + mod_ban_from_community.find(from_id) + .first::(conn) + } + + fn delete(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_ban_from_community::dsl::*; + diesel::delete(mod_ban_from_community.find(from_id)) + .execute(conn) + } + + fn create(conn: &PgConnection, form: &ModBanFromCommunityForm) -> Result { + use schema::mod_ban_from_community::dsl::*; + insert_into(mod_ban_from_community) + .values(form) + .get_result::(conn) + } + + fn update(conn: &PgConnection, from_id: i32, form: &ModBanFromCommunityForm) -> Result { + use schema::mod_ban_from_community::dsl::*; + diesel::update(mod_ban_from_community.find(from_id)) + .set(form) + .get_result::(conn) + } +} + + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] +#[table_name="mod_ban"] +pub struct ModBan { + pub id: i32, + pub mod_user_id: i32, + pub other_user_id: i32, + pub reason: Option, + pub banned: Option, + pub expires: Option, + pub when_: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] +#[table_name="mod_ban"] +pub struct ModBanForm { + pub mod_user_id: i32, + pub other_user_id: i32, + pub reason: Option, + pub banned: Option, + pub expires: Option, +} + +impl Crud for ModBan { + fn read(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_ban::dsl::*; + mod_ban.find(from_id) + .first::(conn) + } + + fn delete(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_ban::dsl::*; + diesel::delete(mod_ban.find(from_id)) + .execute(conn) + } + + fn create(conn: &PgConnection, form: &ModBanForm) -> Result { + use schema::mod_ban::dsl::*; + insert_into(mod_ban) + .values(form) + .get_result::(conn) + } + + fn update(conn: &PgConnection, from_id: i32, form: &ModBanForm) -> Result { + use schema::mod_ban::dsl::*; + diesel::update(mod_ban.find(from_id)) + .set(form) + .get_result::(conn) + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] +#[table_name="mod_add_community"] +pub struct ModAddCommunity { + pub id: i32, + pub mod_user_id: i32, + pub other_user_id: i32, + pub community_id: i32, + pub removed: Option, + pub when_: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] +#[table_name="mod_add_community"] +pub struct ModAddCommunityForm { + pub mod_user_id: i32, + pub other_user_id: i32, + pub community_id: i32, + pub removed: Option, +} + +impl Crud for ModAddCommunity { + fn read(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_add_community::dsl::*; + mod_add_community.find(from_id) + .first::(conn) + } + + fn delete(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_add_community::dsl::*; + diesel::delete(mod_add_community.find(from_id)) + .execute(conn) + } + + fn create(conn: &PgConnection, form: &ModAddCommunityForm) -> Result { + use schema::mod_add_community::dsl::*; + insert_into(mod_add_community) + .values(form) + .get_result::(conn) + } + + fn update(conn: &PgConnection, from_id: i32, form: &ModAddCommunityForm) -> Result { + use schema::mod_add_community::dsl::*; + diesel::update(mod_add_community.find(from_id)) + .set(form) + .get_result::(conn) + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)] +#[table_name="mod_add"] +pub struct ModAdd { + pub id: i32, + pub mod_user_id: i32, + pub other_user_id: i32, + pub removed: Option, + pub when_: chrono::NaiveDateTime, +} + +#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] +#[table_name="mod_add"] +pub struct ModAddForm { + pub mod_user_id: i32, + pub other_user_id: i32, + pub removed: Option, +} + +impl Crud for ModAdd { + fn read(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_add::dsl::*; + mod_add.find(from_id) + .first::(conn) + } + + fn delete(conn: &PgConnection, from_id: i32) -> Result { + use schema::mod_add::dsl::*; + diesel::delete(mod_add.find(from_id)) + .execute(conn) + } + + fn create(conn: &PgConnection, form: &ModAddForm) -> Result { + use schema::mod_add::dsl::*; + insert_into(mod_add) + .values(form) + .get_result::(conn) + } + + fn update(conn: &PgConnection, from_id: i32, form: &ModAddForm) -> Result { + use schema::mod_add::dsl::*; + diesel::update(mod_add.find(from_id)) + .set(form) + .get_result::(conn) + } +} + +#[cfg(test)] +mod tests { + use establish_connection; + use super::*; + use actions::user::*; + use actions::post::*; + use actions::community::*; + use actions::comment::*; + // use Crud; + #[test] + fn test_crud() { + let conn = establish_connection(); + + let new_mod = UserForm { + name: "the mod".into(), + fedi_name: "rrf".into(), + preferred_username: None, + password_encrypted: "nope".into(), + email: None, + admin: false, + banned: false, + updated: None + }; + + let inserted_mod = User_::create(&conn, &new_mod).unwrap(); + + let new_user = UserForm { + name: "jim2".into(), + fedi_name: "rrf".into(), + preferred_username: None, + password_encrypted: "nope".into(), + email: None, + admin: false, + banned: false, + updated: None + }; + + let inserted_user = User_::create(&conn, &new_user).unwrap(); + + let new_community = CommunityForm { + name: "mod_community".to_string(), + title: "nada".to_owned(), + description: None, + category_id: 1, + creator_id: inserted_user.id, + removed: None, + updated: None + }; + + let inserted_community = Community::create(&conn, &new_community).unwrap(); + + let new_post = PostForm { + name: "A test post thweep".into(), + url: None, + body: None, + creator_id: inserted_user.id, + community_id: inserted_community.id, + removed: None, + locked: None, + updated: None + }; + + let inserted_post = Post::create(&conn, &new_post).unwrap(); + + let comment_form = CommentForm { + content: "A test comment".into(), + creator_id: inserted_user.id, + post_id: inserted_post.id, + removed: None, + parent_id: None, + updated: None + }; + + let inserted_comment = Comment::create(&conn, &comment_form).unwrap(); + + // Now the actual tests + + // remove post + let mod_remove_post_form = ModRemovePostForm { + mod_user_id: inserted_mod.id, + post_id: inserted_post.id, + reason: None, + removed: None, + }; + let inserted_mod_remove_post = ModRemovePost::create(&conn, &mod_remove_post_form).unwrap(); + let read_moderator_remove_post = ModRemovePost::read(&conn, inserted_mod_remove_post.id).unwrap(); + let expected_moderator_remove_post = ModRemovePost { + id: inserted_mod_remove_post.id, + post_id: inserted_post.id, + mod_user_id: inserted_mod.id, + reason: None, + removed: Some(true), + when_: inserted_mod_remove_post.when_, + }; + + // lock post + + let mod_lock_post_form = ModLockPostForm { + mod_user_id: inserted_mod.id, + post_id: inserted_post.id, + locked: None, + }; + let inserted_mod_lock_post = ModLockPost::create(&conn, &mod_lock_post_form).unwrap(); + let read_moderator_lock_post = ModLockPost::read(&conn, inserted_mod_lock_post.id).unwrap(); + let expected_moderator_lock_post = ModLockPost { + id: inserted_mod_lock_post.id, + post_id: inserted_post.id, + mod_user_id: inserted_mod.id, + locked: Some(true), + when_: inserted_mod_lock_post.when_, + }; + + // comment + + let mod_remove_comment_form = ModRemoveCommentForm { + mod_user_id: inserted_mod.id, + comment_id: inserted_comment.id, + reason: None, + removed: None, + }; + let inserted_mod_remove_comment = ModRemoveComment::create(&conn, &mod_remove_comment_form).unwrap(); + let read_moderator_remove_comment = ModRemoveComment::read(&conn, inserted_mod_remove_comment.id).unwrap(); + let expected_moderator_remove_comment = ModRemoveComment { + id: inserted_mod_remove_comment.id, + comment_id: inserted_comment.id, + mod_user_id: inserted_mod.id, + reason: None, + removed: Some(true), + when_: inserted_mod_remove_comment.when_, + }; + + // community + + let mod_remove_community_form = ModRemoveCommunityForm { + mod_user_id: inserted_mod.id, + community_id: inserted_community.id, + reason: None, + removed: None, + expires: None, + }; + let inserted_mod_remove_community = ModRemoveCommunity::create(&conn, &mod_remove_community_form).unwrap(); + let read_moderator_remove_community = ModRemoveCommunity::read(&conn, inserted_mod_remove_community.id).unwrap(); + let expected_moderator_remove_community = ModRemoveCommunity { + id: inserted_mod_remove_community.id, + community_id: inserted_community.id, + mod_user_id: inserted_mod.id, + reason: None, + removed: Some(true), + expires: None, + when_: inserted_mod_remove_community.when_, + }; + + // ban from community + + let mod_ban_from_community_form = ModBanFromCommunityForm { + mod_user_id: inserted_mod.id, + other_user_id: inserted_user.id, + community_id: inserted_community.id, + reason: None, + banned: None, + expires: None, + }; + let inserted_mod_ban_from_community = ModBanFromCommunity::create(&conn, &mod_ban_from_community_form).unwrap(); + let read_moderator_ban_from_community = ModBanFromCommunity::read(&conn, inserted_mod_ban_from_community.id).unwrap(); + let expected_moderator_ban_from_community = ModBanFromCommunity { + id: inserted_mod_ban_from_community.id, + community_id: inserted_community.id, + mod_user_id: inserted_mod.id, + other_user_id: inserted_user.id, + reason: None, + banned: Some(true), + expires: None, + when_: inserted_mod_ban_from_community.when_, + }; + + // ban + + let mod_ban_form = ModBanForm { + mod_user_id: inserted_mod.id, + other_user_id: inserted_user.id, + reason: None, + banned: None, + expires: None, + }; + let inserted_mod_ban = ModBan::create(&conn, &mod_ban_form).unwrap(); + let read_moderator_ban = ModBan::read(&conn, inserted_mod_ban.id).unwrap(); + let expected_moderator_ban = ModBan { + id: inserted_mod_ban.id, + mod_user_id: inserted_mod.id, + other_user_id: inserted_user.id, + reason: None, + banned: Some(true), + expires: None, + when_: inserted_mod_ban.when_, + }; + + // mod add community + + let mod_add_community_form = ModAddCommunityForm { + mod_user_id: inserted_mod.id, + other_user_id: inserted_user.id, + community_id: inserted_community.id, + removed: None, + }; + let inserted_mod_add_community = ModAddCommunity::create(&conn, &mod_add_community_form).unwrap(); + let read_moderator_add_community = ModAddCommunity::read(&conn, inserted_mod_add_community.id).unwrap(); + let expected_moderator_add_community = ModAddCommunity { + id: inserted_mod_add_community.id, + community_id: inserted_community.id, + mod_user_id: inserted_mod.id, + other_user_id: inserted_user.id, + removed: Some(false), + when_: inserted_mod_add_community.when_, + }; + + // mod add + + let mod_add_form = ModAddForm { + mod_user_id: inserted_mod.id, + other_user_id: inserted_user.id, + removed: None, + }; + let inserted_mod_add = ModAdd::create(&conn, &mod_add_form).unwrap(); + let read_moderator_add = ModAdd::read(&conn, inserted_mod_add.id).unwrap(); + let expected_moderator_add = ModAdd { + id: inserted_mod_add.id, + mod_user_id: inserted_mod.id, + other_user_id: inserted_user.id, + removed: Some(false), + when_: inserted_mod_add.when_, + }; + + ModRemovePost::delete(&conn, inserted_mod_remove_post.id).unwrap(); + ModLockPost::delete(&conn, inserted_mod_lock_post.id).unwrap(); + ModRemoveComment::delete(&conn, inserted_mod_remove_comment.id).unwrap(); + ModRemoveCommunity::delete(&conn, inserted_mod_remove_community.id).unwrap(); + ModBanFromCommunity::delete(&conn, inserted_mod_ban_from_community.id).unwrap(); + ModBan::delete(&conn, inserted_mod_ban.id).unwrap(); + ModAddCommunity::delete(&conn, inserted_mod_add_community.id).unwrap(); + ModAdd::delete(&conn, inserted_mod_add.id).unwrap(); + + Comment::delete(&conn, inserted_comment.id).unwrap(); + Post::delete(&conn, inserted_post.id).unwrap(); + Community::delete(&conn, inserted_community.id).unwrap(); + User_::delete(&conn, inserted_user.id).unwrap(); + User_::delete(&conn, inserted_mod.id).unwrap(); + + assert_eq!(expected_moderator_remove_post, read_moderator_remove_post); + assert_eq!(expected_moderator_lock_post, read_moderator_lock_post); + assert_eq!(expected_moderator_remove_comment, read_moderator_remove_comment); + assert_eq!(expected_moderator_remove_community, read_moderator_remove_community); + assert_eq!(expected_moderator_ban_from_community, read_moderator_ban_from_community); + assert_eq!(expected_moderator_ban, read_moderator_ban); + assert_eq!(expected_moderator_add_community, read_moderator_add_community); + assert_eq!(expected_moderator_add, read_moderator_add); + } +} diff --git a/server/src/actions/moderator_views.rs b/server/src/actions/moderator_views.rs new file mode 100644 index 0000000..2e24356 --- /dev/null +++ b/server/src/actions/moderator_views.rs @@ -0,0 +1,427 @@ +extern crate diesel; +use diesel::*; +use diesel::result::Error; +use serde::{Deserialize, Serialize}; + +table! { + mod_remove_post_view (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + reason -> Nullable, + removed -> Nullable, + when_ -> Timestamp, + mod_user_name -> Varchar, + post_name -> Varchar, + community_id -> Int4, + community_name -> Varchar, + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="mod_remove_post_view"] +pub struct ModRemovePostView { + pub id: i32, + pub mod_user_id: i32, + pub post_id: i32, + pub reason: Option, + pub removed: Option, + pub when_: chrono::NaiveDateTime, + pub mod_user_name: String, + pub post_name: String, + pub community_id: i32, + pub community_name: String, +} + +impl ModRemovePostView { + pub fn list(conn: &PgConnection, + from_community_id: Option, + from_mod_user_id: Option, + limit: Option, + page: Option) -> Result, Error> { + use actions::moderator_views::mod_remove_post_view::dsl::*; + let mut query = mod_remove_post_view.into_boxed(); + + let page = page.unwrap_or(1); + let limit = limit.unwrap_or(10); + let offset = limit * (page - 1); + + if let Some(from_community_id) = from_community_id { + query = query.filter(community_id.eq(from_community_id)); + }; + + if let Some(from_mod_user_id) = from_mod_user_id { + query = query.filter(mod_user_id.eq(from_mod_user_id)); + }; + + query.limit(limit).offset(offset).order_by(when_.desc()).load::(conn) + } +} + +table! { + mod_lock_post_view (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + locked -> Nullable, + when_ -> Timestamp, + mod_user_name -> Varchar, + post_name -> Varchar, + community_id -> Int4, + community_name -> Varchar, + } +} + + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="mod_lock_post_view"] +pub struct ModLockPostView { + pub id: i32, + pub mod_user_id: i32, + pub post_id: i32, + pub locked: Option, + pub when_: chrono::NaiveDateTime, + pub mod_user_name: String, + pub post_name: String, + pub community_id: i32, + pub community_name: String, +} + +impl ModLockPostView { + pub fn list(conn: &PgConnection, + from_community_id: Option, + from_mod_user_id: Option, + limit: Option, + page: Option) -> Result, Error> { + use actions::moderator_views::mod_lock_post_view::dsl::*; + let mut query = mod_lock_post_view.into_boxed(); + + let page = page.unwrap_or(1); + let limit = limit.unwrap_or(10); + let offset = limit * (page - 1); + + if let Some(from_community_id) = from_community_id { + query = query.filter(community_id.eq(from_community_id)); + }; + + if let Some(from_mod_user_id) = from_mod_user_id { + query = query.filter(mod_user_id.eq(from_mod_user_id)); + }; + + query.limit(limit).offset(offset).order_by(when_.desc()).load::(conn) + } +} + +table! { + mod_remove_comment_view (id) { + id -> Int4, + mod_user_id -> Int4, + comment_id -> Int4, + reason -> Nullable, + removed -> Nullable, + when_ -> Timestamp, + mod_user_name -> Varchar, + comment_user_id -> Int4, + comment_user_name -> Varchar, + comment_content -> Text, + post_id -> Int4, + post_name -> Varchar, + community_id -> Int4, + community_name -> Varchar, + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="mod_remove_comment_view"] +pub struct ModRemoveCommentView { + pub id: i32, + pub mod_user_id: i32, + pub comment_id: i32, + pub reason: Option, + pub removed: Option, + pub when_: chrono::NaiveDateTime, + pub mod_user_name: String, + pub comment_user_id: i32, + pub comment_user_name: String, + pub comment_content: String, + pub post_id: i32, + pub post_name: String, + pub community_id: i32, + pub community_name: String, +} + +impl ModRemoveCommentView { + pub fn list(conn: &PgConnection, + from_community_id: Option, + from_mod_user_id: Option, + limit: Option, + page: Option) -> Result, Error> { + use actions::moderator_views::mod_remove_comment_view::dsl::*; + let mut query = mod_remove_comment_view.into_boxed(); + + let page = page.unwrap_or(1); + let limit = limit.unwrap_or(10); + let offset = limit * (page - 1); + + if let Some(from_community_id) = from_community_id { + query = query.filter(community_id.eq(from_community_id)); + }; + + if let Some(from_mod_user_id) = from_mod_user_id { + query = query.filter(mod_user_id.eq(from_mod_user_id)); + }; + + query.limit(limit).offset(offset).order_by(when_.desc()).load::(conn) + } +} + +table! { + mod_remove_community_view (id) { + id -> Int4, + mod_user_id -> Int4, + community_id -> Int4, + reason -> Nullable, + removed -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + mod_user_name -> Varchar, + community_name -> Varchar, + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="mod_remove_community_view"] +pub struct ModRemoveCommunityView { + pub id: i32, + pub mod_user_id: i32, + pub community_id: i32, + pub reason: Option, + pub removed: Option, + pub expires: Option, + pub when_: chrono::NaiveDateTime, + pub mod_user_name: String, + pub community_name: String, +} + +impl ModRemoveCommunityView { + pub fn list(conn: &PgConnection, + from_mod_user_id: Option, + limit: Option, + page: Option) -> Result, Error> { + use actions::moderator_views::mod_remove_community_view::dsl::*; + let mut query = mod_remove_community_view.into_boxed(); + + let page = page.unwrap_or(1); + let limit = limit.unwrap_or(10); + let offset = limit * (page - 1); + + if let Some(from_mod_user_id) = from_mod_user_id { + query = query.filter(mod_user_id.eq(from_mod_user_id)); + }; + + query.limit(limit).offset(offset).order_by(when_.desc()).load::(conn) + } +} + + +table! { + mod_ban_from_community_view (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + community_id -> Int4, + reason -> Nullable, + banned -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + mod_user_name -> Varchar, + other_user_name -> Varchar, + community_name -> Varchar, + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="mod_ban_from_community_view"] +pub struct ModBanFromCommunityView { + pub id: i32, + pub mod_user_id: i32, + pub other_user_id: i32, + pub community_id: i32, + pub reason: Option, + pub banned: Option, + pub expires: Option, + pub when_: chrono::NaiveDateTime, + pub mod_user_name: String, + pub other_user_name: String, + pub community_name: String, +} + +impl ModBanFromCommunityView { + pub fn list(conn: &PgConnection, + from_community_id: Option, + from_mod_user_id: Option, + limit: Option, + page: Option) -> Result, Error> { + use actions::moderator_views::mod_ban_from_community_view::dsl::*; + let mut query = mod_ban_from_community_view.into_boxed(); + + let page = page.unwrap_or(1); + let limit = limit.unwrap_or(10); + let offset = limit * (page - 1); + + if let Some(from_community_id) = from_community_id { + query = query.filter(community_id.eq(from_community_id)); + }; + + if let Some(from_mod_user_id) = from_mod_user_id { + query = query.filter(mod_user_id.eq(from_mod_user_id)); + }; + + query.limit(limit).offset(offset).order_by(when_.desc()).load::(conn) + } +} + +table! { + mod_ban_view (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + reason -> Nullable, + banned -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + mod_user_name -> Varchar, + other_user_name -> Varchar, + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="mod_ban_view"] +pub struct ModBanView { + pub id: i32, + pub mod_user_id: i32, + pub other_user_id: i32, + pub reason: Option, + pub banned: Option, + pub expires: Option, + pub when_: chrono::NaiveDateTime, + pub mod_user_name: String, + pub other_user_name: String, +} + +impl ModBanView { + pub fn list(conn: &PgConnection, + from_mod_user_id: Option, + limit: Option, + page: Option) -> Result, Error> { + use actions::moderator_views::mod_ban_view::dsl::*; + let mut query = mod_ban_view.into_boxed(); + + let page = page.unwrap_or(1); + let limit = limit.unwrap_or(10); + let offset = limit * (page - 1); + + if let Some(from_mod_user_id) = from_mod_user_id { + query = query.filter(mod_user_id.eq(from_mod_user_id)); + }; + + query.limit(limit).offset(offset).order_by(when_.desc()).load::(conn) + } +} + +table! { + mod_add_community_view (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + community_id -> Int4, + removed -> Nullable, + when_ -> Timestamp, + mod_user_name -> Varchar, + other_user_name -> Varchar, + community_name -> Varchar, + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="mod_add_community_view"] +pub struct ModAddCommunityView { + pub id: i32, + pub mod_user_id: i32, + pub other_user_id: i32, + pub community_id: i32, + pub removed: Option, + pub when_: chrono::NaiveDateTime, + pub mod_user_name: String, + pub other_user_name: String, + pub community_name: String, +} + +impl ModAddCommunityView { + pub fn list(conn: &PgConnection, + from_community_id: Option, + from_mod_user_id: Option, + limit: Option, + page: Option) -> Result, Error> { + use actions::moderator_views::mod_add_community_view::dsl::*; + let mut query = mod_add_community_view.into_boxed(); + + let page = page.unwrap_or(1); + let limit = limit.unwrap_or(10); + let offset = limit * (page - 1); + + if let Some(from_community_id) = from_community_id { + query = query.filter(community_id.eq(from_community_id)); + }; + + if let Some(from_mod_user_id) = from_mod_user_id { + query = query.filter(mod_user_id.eq(from_mod_user_id)); + }; + + query.limit(limit).offset(offset).order_by(when_.desc()).load::(conn) + } +} + +table! { + mod_add_view (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + removed -> Nullable, + when_ -> Timestamp, + mod_user_name -> Varchar, + other_user_name -> Varchar, + } +} + +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="mod_add_view"] +pub struct ModAddView { + pub id: i32, + pub mod_user_id: i32, + pub other_user_id: i32, + pub removed: Option, + pub when_: chrono::NaiveDateTime, + pub mod_user_name: String, + pub other_user_name: String, +} + +impl ModAddView { + pub fn list(conn: &PgConnection, + from_mod_user_id: Option, + limit: Option, + page: Option) -> Result, Error> { + use actions::moderator_views::mod_add_view::dsl::*; + let mut query = mod_add_view.into_boxed(); + + let page = page.unwrap_or(1); + let limit = limit.unwrap_or(10); + let offset = limit * (page - 1); + + if let Some(from_mod_user_id) = from_mod_user_id { + query = query.filter(mod_user_id.eq(from_mod_user_id)); + }; + + query.limit(limit).offset(offset).order_by(when_.desc()).load::(conn) + } +} diff --git a/server/src/actions/post.rs b/server/src/actions/post.rs index b53aae4..468b3a9 100644 --- a/server/src/actions/post.rs +++ b/server/src/actions/post.rs @@ -14,6 +14,8 @@ pub struct Post { pub body: Option, pub creator_id: i32, pub community_id: i32, + pub removed: Option, + pub locked: Option, pub published: chrono::NaiveDateTime, pub updated: Option } @@ -26,6 +28,8 @@ pub struct PostForm { pub body: Option, pub creator_id: i32, pub community_id: i32, + pub removed: Option, + pub locked: Option, pub updated: Option } @@ -115,17 +119,20 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + admin: false, + banned: false, updated: None }; let inserted_user = User_::create(&conn, &new_user).unwrap(); let new_community = CommunityForm { - name: "test community_2".to_string(), + name: "test community_3".to_string(), title: "nada".to_owned(), description: None, category_id: 1, creator_id: inserted_user.id, + removed: None, updated: None }; @@ -137,6 +144,8 @@ mod tests { body: None, creator_id: inserted_user.id, community_id: inserted_community.id, + removed: None, + locked: None, updated: None }; @@ -150,6 +159,8 @@ mod tests { creator_id: inserted_user.id, community_id: inserted_community.id, published: inserted_post.published, + removed: Some(false), + locked: Some(false), updated: None }; diff --git a/server/src/actions/post_view.rs b/server/src/actions/post_view.rs index 6ca85c3..0ebcf40 100644 --- a/server/src/actions/post_view.rs +++ b/server/src/actions/post_view.rs @@ -19,6 +19,8 @@ table! { body -> Nullable, creator_id -> Int4, community_id -> Int4, + removed -> Nullable, + locked -> Nullable, published -> Timestamp, updated -> Nullable, creator_name -> Varchar, @@ -31,6 +33,7 @@ table! { user_id -> Nullable, my_vote -> Nullable, subscribed -> Nullable, + am_mod -> Nullable, } } @@ -44,6 +47,8 @@ pub struct PostView { pub body: Option, pub creator_id: i32, pub community_id: i32, + pub removed: Option, + pub locked: Option, pub published: chrono::NaiveDateTime, pub updated: Option, pub creator_name: String, @@ -56,6 +61,7 @@ pub struct PostView { pub user_id: Option, pub my_vote: Option, pub subscribed: Option, + pub am_mod: Option, } impl PostView { @@ -110,6 +116,8 @@ impl PostView { .order_by(score.desc()) }; + query = query.filter(removed.eq(false)); + query.load::(conn) } @@ -156,7 +164,9 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, - updated: None + updated: None, + admin: false, + banned: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -167,6 +177,7 @@ mod tests { description: None, creator_id: inserted_user.id, category_id: 1, + removed: None, updated: None }; @@ -178,6 +189,8 @@ mod tests { body: None, creator_id: inserted_user.id, community_id: inserted_community.id, + removed: None, + locked: None, updated: None }; @@ -216,6 +229,8 @@ mod tests { creator_id: inserted_user.id, creator_name: user_name.to_owned(), community_id: inserted_community.id, + removed: Some(false), + locked: Some(false), community_name: community_name.to_owned(), number_of_comments: 0, score: 1, @@ -224,7 +239,8 @@ mod tests { hot_rank: 864, published: inserted_post.published, updated: None, - subscribed: None + subscribed: None, + am_mod: None, }; let expected_post_listing_with_user = PostView { @@ -234,6 +250,8 @@ mod tests { name: post_name.to_owned(), url: None, body: None, + removed: Some(false), + locked: Some(false), creator_id: inserted_user.id, creator_name: user_name.to_owned(), community_id: inserted_community.id, @@ -245,7 +263,8 @@ mod tests { hot_rank: 864, published: inserted_post.published, updated: None, - subscribed: None + subscribed: None, + am_mod: None, }; @@ -274,6 +293,5 @@ mod tests { assert_eq!(expected_post_like, inserted_post_like); assert_eq!(1, like_removed); assert_eq!(1, num_deleted); - } } diff --git a/server/src/actions/user.rs b/server/src/actions/user.rs index d646adc..ea6f36e 100644 --- a/server/src/actions/user.rs +++ b/server/src/actions/user.rs @@ -17,6 +17,8 @@ pub struct User_ { pub password_encrypted: String, pub email: Option, pub icon: Option>, + pub admin: bool, + pub banned: bool, pub published: chrono::NaiveDateTime, pub updated: Option } @@ -28,6 +30,8 @@ pub struct UserForm { pub fedi_name: String, pub preferred_username: Option, pub password_encrypted: String, + pub admin: bool, + pub banned: bool, pub email: Option, pub updated: Option } @@ -42,22 +46,26 @@ impl Crud for User_ { .execute(conn) } fn create(conn: &PgConnection, form: &UserForm) -> Result { - let mut edited_user = form.clone(); - let password_hash = hash(&form.password_encrypted, DEFAULT_COST) - .expect("Couldn't hash password"); - edited_user.password_encrypted = password_hash; insert_into(user_) - .values(edited_user) + .values(form) .get_result::(conn) } fn update(conn: &PgConnection, user_id: i32, form: &UserForm) -> Result { + diesel::update(user_.find(user_id)) + .set(form) + .get_result::(conn) + } +} + +impl User_ { + pub fn register(conn: &PgConnection, form: &UserForm) -> Result { let mut edited_user = form.clone(); let password_hash = hash(&form.password_encrypted, DEFAULT_COST) .expect("Couldn't hash password"); edited_user.password_encrypted = password_hash; - diesel::update(user_.find(user_id)) - .set(edited_user) - .get_result::(conn) + + Self::create(&conn, &edited_user) + } } @@ -122,6 +130,8 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + admin: false, + banned: false, updated: None }; @@ -132,9 +142,11 @@ mod tests { name: "thommy".into(), fedi_name: "rrf".into(), preferred_username: None, - password_encrypted: "$2y$12$YXpNpYsdfjmed.QlYLvw4OfTCgyKUnKHc/V8Dgcf9YcVKHPaYXYYy".into(), + password_encrypted: "nope".into(), email: None, icon: None, + admin: false, + banned: false, published: inserted_user.published, updated: None }; @@ -143,9 +155,9 @@ mod tests { let updated_user = User_::update(&conn, inserted_user.id, &new_user).unwrap(); let num_deleted = User_::delete(&conn, inserted_user.id).unwrap(); - assert_eq!(expected_user.id, read_user.id); - assert_eq!(expected_user.id, inserted_user.id); - assert_eq!(expected_user.id, updated_user.id); + assert_eq!(expected_user, read_user); + assert_eq!(expected_user, inserted_user); + assert_eq!(expected_user, updated_user); assert_eq!(1, num_deleted); } } diff --git a/server/src/actions/user_view.rs b/server/src/actions/user_view.rs index 5873a5c..a5187ae 100644 --- a/server/src/actions/user_view.rs +++ b/server/src/actions/user_view.rs @@ -8,6 +8,8 @@ table! { id -> Int4, name -> Varchar, fedi_name -> Varchar, + admin -> Bool, + banned -> Bool, published -> Timestamp, number_of_posts -> BigInt, post_score -> BigInt, @@ -22,6 +24,8 @@ pub struct UserView { pub id: i32, pub name: String, pub fedi_name: String, + pub admin: bool, + pub banned: bool, pub published: chrono::NaiveDateTime, pub number_of_posts: i64, pub post_score: i64, @@ -36,5 +40,17 @@ impl UserView { user_view.find(from_user_id) .first::(conn) } + + pub fn admins(conn: &PgConnection) -> Result, Error> { + use actions::user_view::user_view::dsl::*; + user_view.filter(admin.eq(true)) + .load::(conn) + } + + pub fn banned(conn: &PgConnection) -> Result, Error> { + use actions::user_view::user_view::dsl::*; + user_view.filter(banned.eq(true)) + .load::(conn) + } } diff --git a/server/src/apub.rs b/server/src/apub.rs index b245626..a9a417e 100644 --- a/server/src/apub.rs +++ b/server/src/apub.rs @@ -44,6 +44,8 @@ mod tests { email: None, icon: None, published: naive_now(), + admin: false, + banned: false, updated: None }; diff --git a/server/src/lib.rs b/server/src/lib.rs index 9cdbd33..ab971ed 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -12,7 +12,7 @@ pub extern crate jsonwebtoken; pub extern crate bcrypt; pub extern crate regex; #[macro_use] pub extern crate strum_macros; - +#[macro_use] pub extern crate lazy_static; pub mod schema; pub mod apub; pub mod actions; @@ -50,6 +50,11 @@ pub trait Likeable { fn remove(conn: &PgConnection, form: &T) -> Result where Self: Sized; } +pub trait Bannable { + fn ban(conn: &PgConnection, form: &T) -> Result where Self: Sized; + fn unban(conn: &PgConnection, form: &T) -> Result where Self: Sized; +} + pub fn establish_connection() -> PgConnection { let db_url = Settings::get().db_url; PgConnection::establish(&db_url) @@ -88,22 +93,47 @@ pub fn naive_now() -> NaiveDateTime { chrono::prelude::Utc::now().naive_utc() } +pub fn naive_from_unix(time: i64) -> NaiveDateTime { + NaiveDateTime::from_timestamp(time, 0) +} + pub fn is_email_regex(test: &str) -> bool { - let re = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap(); - re.is_match(test) + EMAIL_REGEX.is_match(test) +} + +pub fn remove_slurs(test: &str) -> String { + SLUR_REGEX.replace_all(test, "*removed*").to_string() +} + +pub fn has_slurs(test: &str) -> bool { + SLUR_REGEX.is_match(test) } #[cfg(test)] mod tests { - use {Settings, is_email_regex}; + use {Settings, is_email_regex, remove_slurs, has_slurs}; #[test] fn test_api() { assert_eq!(Settings::get().api_endpoint(), "http://0.0.0.0/api/v1"); } - #[test] - fn test_email() { + #[test] fn test_email() { assert!(is_email_regex("gush@gmail.com")); assert!(!is_email_regex("nada_neutho")); } + + #[test] fn test_slur_filter() { + let test = "coons test dindu ladyboy tranny. This is a bunch of other safe text.".to_string(); + let slur_free = "No slurs here"; + assert_eq!(remove_slurs(&test), "*removed* test *removed* *removed* *removed*. This is a bunch of other safe text.".to_string()); + assert!(has_slurs(&test)); + assert!(!has_slurs(slur_free)); + } } + + +lazy_static! { + static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap(); + static ref SLUR_REGEX: Regex = Regex::new(r"(fag(g|got|tard)?|maricos?|cock\s?sucker(s|ing)?|\bnig(\b|g?(a|er)?s?)\b|dindu(s?)|mudslime?s?|kikes?|mongoloids?|towel\s*heads?|\bspi(c|k)s?\b|\bchinks?|niglets?|beaners?|\bnips?\b|\bcoons?\b|jungle\s*bunn(y|ies?)|jigg?aboo?s?|\bpakis?\b|rag\s*heads?|gooks?|cunts?|bitch(es|ing|y)?|puss(y|ies?)|twats?|feminazis?|whor(es?|ing)|\bslut(s|t?y)?|\btrann?(y|ies?)|ladyboy(s?))").unwrap(); +} + diff --git a/server/src/schema.rs b/server/src/schema.rs index fe11a46..f431610 100644 --- a/server/src/schema.rs +++ b/server/src/schema.rs @@ -12,6 +12,7 @@ table! { post_id -> Int4, parent_id -> Nullable, content -> Text, + removed -> Nullable, published -> Timestamp, updated -> Nullable, } @@ -36,6 +37,7 @@ table! { description -> Nullable, category_id -> Int4, creator_id -> Int4, + removed -> Nullable, published -> Timestamp, updated -> Nullable, } @@ -59,6 +61,105 @@ table! { } } +table! { + community_user_ban (id) { + id -> Int4, + community_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } +} + +table! { + mod_add (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + removed -> Nullable, + when_ -> Timestamp, + } +} + +table! { + mod_add_community (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + community_id -> Int4, + removed -> Nullable, + when_ -> Timestamp, + } +} + +table! { + mod_ban (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + reason -> Nullable, + banned -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + } +} + +table! { + mod_ban_from_community (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + community_id -> Int4, + reason -> Nullable, + banned -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + } +} + +table! { + mod_lock_post (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + locked -> Nullable, + when_ -> Timestamp, + } +} + +table! { + mod_remove_comment (id) { + id -> Int4, + mod_user_id -> Int4, + comment_id -> Int4, + reason -> Nullable, + removed -> Nullable, + when_ -> Timestamp, + } +} + +table! { + mod_remove_community (id) { + id -> Int4, + mod_user_id -> Int4, + community_id -> Int4, + reason -> Nullable, + removed -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + } +} + +table! { + mod_remove_post (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + reason -> Nullable, + removed -> Nullable, + when_ -> Timestamp, + } +} + table! { post (id) { id -> Int4, @@ -67,6 +168,8 @@ table! { body -> Nullable, creator_id -> Int4, community_id -> Int4, + removed -> Nullable, + locked -> Nullable, published -> Timestamp, updated -> Nullable, } @@ -82,6 +185,17 @@ table! { } } +table! { + site (id) { + id -> Int4, + name -> Varchar, + description -> Nullable, + creator_id -> Int4, + published -> Timestamp, + updated -> Nullable, + } +} + table! { user_ (id) { id -> Int4, @@ -91,11 +205,21 @@ table! { password_encrypted -> Text, email -> Nullable, icon -> Nullable, + admin -> Bool, + banned -> Bool, published -> Timestamp, updated -> Nullable, } } +table! { + user_ban (id) { + id -> Int4, + user_id -> Int4, + published -> Timestamp, + } +} + joinable!(comment -> post (post_id)); joinable!(comment -> user_ (creator_id)); joinable!(comment_like -> comment (comment_id)); @@ -107,10 +231,24 @@ joinable!(community_follower -> community (community_id)); joinable!(community_follower -> user_ (user_id)); joinable!(community_moderator -> community (community_id)); joinable!(community_moderator -> user_ (user_id)); +joinable!(community_user_ban -> community (community_id)); +joinable!(community_user_ban -> user_ (user_id)); +joinable!(mod_add_community -> community (community_id)); +joinable!(mod_ban_from_community -> community (community_id)); +joinable!(mod_lock_post -> post (post_id)); +joinable!(mod_lock_post -> user_ (mod_user_id)); +joinable!(mod_remove_comment -> comment (comment_id)); +joinable!(mod_remove_comment -> user_ (mod_user_id)); +joinable!(mod_remove_community -> community (community_id)); +joinable!(mod_remove_community -> user_ (mod_user_id)); +joinable!(mod_remove_post -> post (post_id)); +joinable!(mod_remove_post -> user_ (mod_user_id)); joinable!(post -> community (community_id)); joinable!(post -> user_ (creator_id)); joinable!(post_like -> post (post_id)); joinable!(post_like -> user_ (user_id)); +joinable!(site -> user_ (creator_id)); +joinable!(user_ban -> user_ (user_id)); allow_tables_to_appear_in_same_query!( category, @@ -119,7 +257,18 @@ allow_tables_to_appear_in_same_query!( community, community_follower, community_moderator, + community_user_ban, + mod_add, + mod_add_community, + mod_ban, + mod_ban_from_community, + mod_lock_post, + mod_remove_comment, + mod_remove_community, + mod_remove_post, post, post_like, + site, user_, + user_ban, ); diff --git a/server/src/websocket_server/server.rs b/server/src/websocket_server/server.rs index 92542d0..ef05f80 100644 --- a/server/src/websocket_server/server.rs +++ b/server/src/websocket_server/server.rs @@ -9,8 +9,9 @@ use serde::{Deserialize, Serialize}; use serde_json::{Value}; use bcrypt::{verify}; use std::str::FromStr; +use diesel::PgConnection; -use {Crud, Joinable, Likeable, Followable, establish_connection, naive_now, SortType}; +use {Crud, Joinable, Likeable, Followable, Bannable, establish_connection, naive_now, naive_from_unix, SortType, has_slurs, remove_slurs}; use actions::community::*; use actions::user::*; use actions::post::*; @@ -20,10 +21,12 @@ use actions::comment_view::*; use actions::category::*; use actions::community_view::*; use actions::user_view::*; +use actions::moderator_views::*; +use actions::moderator::*; #[derive(EnumString,ToString,Debug)] pub enum UserOperation { - Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails + Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser } #[derive(Serialize, Deserialize)] @@ -85,7 +88,8 @@ pub struct Register { username: String, email: Option, password: String, - password_verify: String + password_verify: String, + admin: bool, } #[derive(Serialize, Deserialize)] @@ -111,6 +115,8 @@ pub struct CommunityResponse { #[derive(Serialize, Deserialize)] pub struct ListCommunities { + sort: String, + limit: Option, auth: Option } @@ -202,7 +208,10 @@ pub struct EditComment { content: String, parent_id: Option, edit_id: i32, + creator_id: i32, post_id: i32, + removed: Option, + reason: Option, auth: String } @@ -220,7 +229,6 @@ pub struct CreateCommentLike { auth: String } - #[derive(Serialize, Deserialize)] pub struct CreatePostLike { post_id: i32, @@ -238,10 +246,14 @@ pub struct CreatePostLikeResponse { #[derive(Serialize, Deserialize)] pub struct EditPost { edit_id: i32, + creator_id: i32, community_id: i32, name: String, url: Option, body: Option, + removed: Option, + reason: Option, + locked: Option, auth: String } @@ -252,6 +264,9 @@ pub struct EditCommunity { title: String, description: Option, category_id: i32, + removed: Option, + reason: Option, + expires: Option, auth: String } @@ -294,6 +309,120 @@ pub struct GetUserDetailsResponse { saved_comments: Vec, } +#[derive(Serialize, Deserialize)] +pub struct GetModlog { + mod_user_id: Option, + community_id: Option, + limit: Option, + page: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct GetModlogResponse { + op: String, + removed_posts: Vec, + locked_posts: Vec, + removed_comments: Vec, + removed_communities: Vec, + banned_from_community: Vec, + banned: Vec, + added_to_community: Vec, + added: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct BanFromCommunity { + community_id: i32, + user_id: i32, + ban: bool, + reason: Option, + expires: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct BanFromCommunityResponse { + op: String, + user: UserView, + banned: bool, +} + + +#[derive(Serialize, Deserialize)] +pub struct AddModToCommunity { + community_id: i32, + user_id: i32, + added: bool, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct AddModToCommunityResponse { + op: String, + moderators: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct CreateSite { + name: String, + description: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct EditSite { + name: String, + description: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct GetSite { +} + +#[derive(Serialize, Deserialize)] +pub struct SiteResponse { + op: String, + site: SiteView, +} + +#[derive(Serialize, Deserialize)] +pub struct GetSiteResponse { + op: String, + site: Option, + admins: Vec, + banned: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct AddAdmin { + user_id: i32, + added: bool, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct AddAdminResponse { + op: String, + admins: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct BanUser { + user_id: i32, + ban: bool, + reason: Option, + expires: Option, + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct BanUserResponse { + op: String, + user: UserView, + banned: bool, +} + /// `ChatServer` manages chat rooms and responsible for coordinating chat /// session. implementation is super primitive pub struct ChatServer { @@ -328,6 +457,19 @@ impl ChatServer { } } } + + fn send_community_message(&self, conn: &PgConnection, community_id: i32, message: &str, skip_id: usize) { + let posts = PostView::list(conn, + PostListingType::Community, + &SortType::New, + Some(community_id), + None, + None, + 999).unwrap(); + for post in posts { + self.send_room_message(post.id, message, skip_id); + } + } } /// Make actor from `ChatServer` @@ -391,25 +533,28 @@ impl Handler for ChatServer { let json: Value = serde_json::from_str(&msg.msg) .expect("Couldn't parse message"); - let data: &Value = &json["data"]; + let data = &json["data"].to_string(); let op = &json["op"].as_str().unwrap(); let user_operation: UserOperation = UserOperation::from_str(&op).unwrap(); + + // TODO figure out how to do proper error handling here, instead of just returning + // error strings let res: String = match user_operation { UserOperation::Login => { - let login: Login = serde_json::from_str(&data.to_string()).unwrap(); + let login: Login = serde_json::from_str(data).unwrap(); login.perform(self, msg.id) }, UserOperation::Register => { - let register: Register = serde_json::from_str(&data.to_string()).unwrap(); + let register: Register = serde_json::from_str(data).unwrap(); register.perform(self, msg.id) }, UserOperation::CreateCommunity => { - let create_community: CreateCommunity = serde_json::from_str(&data.to_string()).unwrap(); + let create_community: CreateCommunity = serde_json::from_str(data).unwrap(); create_community.perform(self, msg.id) }, UserOperation::ListCommunities => { - let list_communities: ListCommunities = serde_json::from_str(&data.to_string()).unwrap(); + let list_communities: ListCommunities = serde_json::from_str(data).unwrap(); list_communities.perform(self, msg.id) }, UserOperation::ListCategories => { @@ -417,64 +562,89 @@ impl Handler for ChatServer { list_categories.perform(self, msg.id) }, UserOperation::CreatePost => { - let create_post: CreatePost = serde_json::from_str(&data.to_string()).unwrap(); + let create_post: CreatePost = serde_json::from_str(data).unwrap(); create_post.perform(self, msg.id) }, UserOperation::GetPost => { - let get_post: GetPost = serde_json::from_str(&data.to_string()).unwrap(); + let get_post: GetPost = serde_json::from_str(data).unwrap(); get_post.perform(self, msg.id) }, UserOperation::GetCommunity => { - let get_community: GetCommunity = serde_json::from_str(&data.to_string()).unwrap(); + let get_community: GetCommunity = serde_json::from_str(data).unwrap(); get_community.perform(self, msg.id) }, UserOperation::CreateComment => { - let create_comment: CreateComment = serde_json::from_str(&data.to_string()).unwrap(); + let create_comment: CreateComment = serde_json::from_str(data).unwrap(); create_comment.perform(self, msg.id) }, UserOperation::EditComment => { - let edit_comment: EditComment = serde_json::from_str(&data.to_string()).unwrap(); + let edit_comment: EditComment = serde_json::from_str(data).unwrap(); edit_comment.perform(self, msg.id) }, UserOperation::CreateCommentLike => { - let create_comment_like: CreateCommentLike = serde_json::from_str(&data.to_string()).unwrap(); + let create_comment_like: CreateCommentLike = serde_json::from_str(data).unwrap(); create_comment_like.perform(self, msg.id) }, UserOperation::GetPosts => { - let get_posts: GetPosts = serde_json::from_str(&data.to_string()).unwrap(); + let get_posts: GetPosts = serde_json::from_str(data).unwrap(); get_posts.perform(self, msg.id) }, UserOperation::CreatePostLike => { - let create_post_like: CreatePostLike = serde_json::from_str(&data.to_string()).unwrap(); + let create_post_like: CreatePostLike = serde_json::from_str(data).unwrap(); create_post_like.perform(self, msg.id) }, UserOperation::EditPost => { - let edit_post: EditPost = serde_json::from_str(&data.to_string()).unwrap(); + let edit_post: EditPost = serde_json::from_str(data).unwrap(); edit_post.perform(self, msg.id) }, UserOperation::EditCommunity => { - let edit_community: EditCommunity = serde_json::from_str(&data.to_string()).unwrap(); + let edit_community: EditCommunity = serde_json::from_str(data).unwrap(); edit_community.perform(self, msg.id) }, UserOperation::FollowCommunity => { - let follow_community: FollowCommunity = serde_json::from_str(&data.to_string()).unwrap(); + let follow_community: FollowCommunity = serde_json::from_str(data).unwrap(); follow_community.perform(self, msg.id) }, UserOperation::GetFollowedCommunities => { - let followed_communities: GetFollowedCommunities = serde_json::from_str(&data.to_string()).unwrap(); + let followed_communities: GetFollowedCommunities = serde_json::from_str(data).unwrap(); followed_communities.perform(self, msg.id) }, UserOperation::GetUserDetails => { - let get_user_details: GetUserDetails = serde_json::from_str(&data.to_string()).unwrap(); + let get_user_details: GetUserDetails = serde_json::from_str(data).unwrap(); get_user_details.perform(self, msg.id) }, - // _ => { - // let e = ErrorMessage { - // op: "Unknown".to_string(), - // error: "Unknown User Operation".to_string() - // }; - // serde_json::to_string(&e).unwrap() - // } + UserOperation::GetModlog => { + let get_modlog: GetModlog = serde_json::from_str(data).unwrap(); + get_modlog.perform(self, msg.id) + }, + UserOperation::BanFromCommunity => { + let ban_from_community: BanFromCommunity = serde_json::from_str(data).unwrap(); + ban_from_community.perform(self, msg.id) + }, + UserOperation::AddModToCommunity => { + let mod_add_to_community: AddModToCommunity = serde_json::from_str(data).unwrap(); + mod_add_to_community.perform(self, msg.id) + }, + UserOperation::CreateSite => { + let create_site: CreateSite = serde_json::from_str(data).unwrap(); + create_site.perform(self, msg.id) + }, + UserOperation::EditSite => { + let edit_site: EditSite = serde_json::from_str(data).unwrap(); + edit_site.perform(self, msg.id) + }, + UserOperation::GetSite => { + let get_site: GetSite = serde_json::from_str(data).unwrap(); + get_site.perform(self, msg.id) + }, + UserOperation::AddAdmin => { + let add_admin: AddAdmin = serde_json::from_str(data).unwrap(); + add_admin.perform(self, msg.id) + }, + UserOperation::BanUser => { + let ban_user: BanUser = serde_json::from_str(data).unwrap(); + ban_user.perform(self, msg.id) + }, }; MessageResult(res) @@ -541,6 +711,15 @@ impl Perform for Register { return self.error("Passwords do not match."); } + if has_slurs(&self.username) { + return self.error("No slurs"); + } + + // Make sure there are no admins + if self.admin && UserView::admins(&conn).unwrap().len() > 0 { + return self.error("Sorry, there's already an admin."); + } + // Register the new user let user_form = UserForm { name: self.username.to_owned(), @@ -548,17 +727,35 @@ impl Perform for Register { email: self.email.to_owned(), password_encrypted: self.password.to_owned(), preferred_username: None, - updated: None + updated: None, + admin: self.admin, + banned: false, }; // Create the user - let inserted_user = match User_::create(&conn, &user_form) { + let inserted_user = match User_::register(&conn, &user_form) { Ok(user) => user, Err(_e) => { return self.error("User already exists."); } }; + // If its an admin, add them as a mod to main + if self.admin { + let community_moderator_form = CommunityModeratorForm { + community_id: 1, + user_id: inserted_user.id + }; + + let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) { + Ok(user) => user, + Err(_e) => { + return self.error("Community moderator already exists."); + } + }; + } + + // Return the jwt serde_json::to_string( &LoginResponse { @@ -587,6 +784,12 @@ impl Perform for CreateCommunity { } }; + if has_slurs(&self.name) || + has_slurs(&self.title) || + (self.description.is_some() && has_slurs(&self.description.to_owned().unwrap())) { + return self.error("No slurs"); + } + let user_id = claims.id; // When you create a community, make sure the user becomes a moderator and a follower @@ -597,7 +800,8 @@ impl Perform for CreateCommunity { description: self.description.to_owned(), category_id: self.category_id, creator_id: user_id, - updated: None + removed: None, + updated: None, }; let inserted_community = match Community::create(&conn, &community_form) { @@ -665,7 +869,9 @@ impl Perform for ListCommunities { None => None }; - let communities: Vec = CommunityView::list_all(&conn, user_id).unwrap(); + let sort = SortType::from_str(&self.sort).expect("listing sort"); + + let communities: Vec = CommunityView::list(&conn, user_id, sort, self.limit).unwrap(); // Return the jwt serde_json::to_string( @@ -716,14 +922,26 @@ impl Perform for CreatePost { } }; + if has_slurs(&self.name) || + (self.body.is_some() && has_slurs(&self.body.to_owned().unwrap())) { + return self.error("No slurs"); + } + let user_id = claims.id; + // Check for a ban + if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() { + return self.error("You have been banned from this community"); + } + let post_form = PostForm { name: self.name.to_owned(), url: self.url.to_owned(), body: self.body.to_owned(), community_id: self.community_id, creator_id: user_id, + removed: None, + locked: None, updated: None }; @@ -894,11 +1112,20 @@ impl Perform for CreateComment { let user_id = claims.id; + // Check for a ban + let post = Post::read(&conn, self.post_id).unwrap(); + if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { + return self.error("You have been banned from this community"); + } + + let content_slurs_removed = remove_slurs(&self.content.to_owned()); + let comment_form = CommentForm { - content: self.content.to_owned(), + content: content_slurs_removed, parent_id: self.parent_id.to_owned(), post_id: self.post_id, creator_id: user_id, + removed: None, updated: None }; @@ -970,17 +1197,32 @@ impl Perform for EditComment { let user_id = claims.id; - // Verify its the creator - let orig_comment = Comment::read(&conn, self.edit_id).unwrap(); - if user_id != orig_comment.creator_id { - return self.error("Incorrect creator."); + + // Verify its the creator or a mod + let orig_comment = CommentView::read(&conn, self.edit_id, None).unwrap(); + let mut editors: Vec = CommunityModeratorView::for_community(&conn, orig_comment.community_id) + .unwrap() + .into_iter() + .map(|m| m.user_id) + .collect(); + editors.push(self.creator_id); + if !editors.contains(&user_id) { + return self.error("Not allowed to edit comment."); } + // Check for a ban + if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() { + return self.error("You have been banned from this community"); + } + + let content_slurs_removed = remove_slurs(&self.content.to_owned()); + let comment_form = CommentForm { - content: self.content.to_owned(), + content: content_slurs_removed, parent_id: self.parent_id, post_id: self.post_id, - creator_id: user_id, + creator_id: self.creator_id, + removed: self.removed.to_owned(), updated: Some(naive_now()) }; @@ -991,6 +1233,17 @@ impl Perform for EditComment { } }; + // Mod tables + if let Some(removed) = self.removed.to_owned() { + let form = ModRemoveCommentForm { + mod_user_id: user_id, + comment_id: self.edit_id, + removed: Some(removed), + reason: self.reason.to_owned(), + }; + ModRemoveComment::create(&conn, &form).unwrap(); + } + let comment_view = CommentView::read(&conn, self.edit_id, Some(user_id)).unwrap(); @@ -1038,6 +1291,12 @@ impl Perform for CreateCommentLike { let user_id = claims.id; + // Check for a ban + let post = Post::read(&conn, self.post_id).unwrap(); + if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { + return self.error("You have been banned from this community"); + } + let like_form = CommentLikeForm { comment_id: self.comment_id, post_id: self.post_id, @@ -1150,6 +1409,12 @@ impl Perform for CreatePostLike { let user_id = claims.id; + // Check for a ban + let post = Post::read(&conn, self.post_id).unwrap(); + if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { + return self.error("You have been banned from this community"); + } + let like_form = PostLikeForm { post_id: self.post_id, user_id: user_id, @@ -1197,6 +1462,11 @@ impl Perform for EditPost { fn perform(&self, chat: &mut ChatServer, addr: usize) -> String { + if has_slurs(&self.name) || + (self.body.is_some() && has_slurs(&self.body.to_owned().unwrap())) { + return self.error("No slurs"); + } + let conn = establish_connection(); let claims = match Claims::decode(&self.auth) { @@ -1208,18 +1478,30 @@ impl Perform for EditPost { let user_id = claims.id; - // Verify its the creator - let orig_post = Post::read(&conn, self.edit_id).unwrap(); - if user_id != orig_post.creator_id { - return self.error("Incorrect creator."); + // Verify its the creator or a mod + let mut editors: Vec = CommunityModeratorView::for_community(&conn, self.community_id) + .unwrap() + .into_iter() + .map(|m| m.user_id) + .collect(); + editors.push(self.creator_id); + if !editors.contains(&user_id) { + return self.error("Not allowed to edit comment."); + } + + // Check for a ban + if CommunityUserBanView::get(&conn, user_id, self.community_id).is_ok() { + return self.error("You have been banned from this community"); } let post_form = PostForm { name: self.name.to_owned(), url: self.url.to_owned(), body: self.body.to_owned(), - creator_id: user_id, + creator_id: self.creator_id.to_owned(), community_id: self.community_id, + removed: self.removed.to_owned(), + locked: self.locked.to_owned(), updated: Some(naive_now()) }; @@ -1230,6 +1512,26 @@ impl Perform for EditPost { } }; + // Mod tables + if let Some(removed) = self.removed.to_owned() { + let form = ModRemovePostForm { + mod_user_id: user_id, + post_id: self.edit_id, + removed: Some(removed), + reason: self.reason.to_owned(), + }; + ModRemovePost::create(&conn, &form).unwrap(); + } + + if let Some(locked) = self.locked.to_owned() { + let form = ModLockPostForm { + mod_user_id: user_id, + post_id: self.edit_id, + locked: Some(locked), + }; + ModLockPost::create(&conn, &form).unwrap(); + } + let post_view = PostView::read(&conn, self.edit_id, Some(user_id)).unwrap(); let mut post_sent = post_view.clone(); @@ -1264,6 +1566,10 @@ impl Perform for EditCommunity { fn perform(&self, chat: &mut ChatServer, addr: usize) -> String { + if has_slurs(&self.name) || has_slurs(&self.title) { + return self.error("No slurs"); + } + let conn = establish_connection(); let claims = match Claims::decode(&self.auth) { @@ -1275,7 +1581,6 @@ impl Perform for EditCommunity { let user_id = claims.id; - // Verify its a mod let moderator_view = CommunityModeratorView::for_community(&conn, self.edit_id).unwrap(); let mod_ids: Vec = moderator_view.into_iter().map(|m| m.user_id).collect(); @@ -1289,6 +1594,7 @@ impl Perform for EditCommunity { description: self.description.to_owned(), category_id: self.category_id.to_owned(), creator_id: user_id, + removed: self.removed.to_owned(), updated: Some(naive_now()) }; @@ -1299,11 +1605,23 @@ impl Perform for EditCommunity { } }; - let community_view = CommunityView::read(&conn, self.edit_id, Some(user_id)).unwrap(); + // Mod tables + if let Some(removed) = self.removed.to_owned() { + let expires = match self.expires { + Some(time) => Some(naive_from_unix(time)), + None => None + }; + let form = ModRemoveCommunityForm { + mod_user_id: user_id, + community_id: self.edit_id, + removed: Some(removed), + reason: self.reason.to_owned(), + expires: expires + }; + ModRemoveCommunity::create(&conn, &form).unwrap(); + } - // Do the subscriber stuff here - // let mut community_sent = post_view.clone(); - // community_sent.my_vote = None; + let community_view = CommunityView::read(&conn, self.edit_id, Some(user_id)).unwrap(); let community_out = serde_json::to_string( &CommunityResponse { @@ -1313,15 +1631,17 @@ impl Perform for EditCommunity { ) .unwrap(); - // let post_sent_out = serde_json::to_string( - // &PostResponse { - // op: self.op_type().to_string(), - // post: post_sent - // } - // ) - // .unwrap(); + let community_view_sent = CommunityView::read(&conn, self.edit_id, None).unwrap(); - chat.send_room_message(self.edit_id, &community_out, addr); + let community_sent = serde_json::to_string( + &CommunityResponse { + op: self.op_type().to_string(), + community: community_view_sent + } + ) + .unwrap(); + + chat.send_community_message(&conn, self.edit_id, &community_sent, addr); community_out } @@ -1460,3 +1780,459 @@ impl Perform for GetUserDetails { } } +impl Perform for GetModlog { + fn op_type(&self) -> UserOperation { + UserOperation::GetModlog + } + + fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String { + + let conn = establish_connection(); + + let removed_posts = ModRemovePostView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap(); + let locked_posts = ModLockPostView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap(); + let removed_comments = ModRemoveCommentView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap(); + let removed_communities = ModRemoveCommunityView::list(&conn, self.mod_user_id, self.limit, self.page).unwrap(); + let banned_from_community = ModBanFromCommunityView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap(); + let banned = ModBanView::list(&conn, self.mod_user_id, self.limit, self.page).unwrap(); + let added_to_community = ModAddCommunityView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap(); + let added = ModAddView::list(&conn, self.mod_user_id, self.limit, self.page).unwrap(); + + // Return the jwt + serde_json::to_string( + &GetModlogResponse { + op: self.op_type().to_string(), + removed_posts: removed_posts, + locked_posts: locked_posts, + removed_comments: removed_comments, + removed_communities: removed_communities, + banned_from_community: banned_from_community, + banned: banned, + added_to_community: added_to_community, + added: added, + } + ) + .unwrap() + } +} + +impl Perform for BanFromCommunity { + fn op_type(&self) -> UserOperation { + UserOperation::BanFromCommunity + } + + fn perform(&self, chat: &mut ChatServer, addr: usize) -> String { + + let conn = establish_connection(); + + let claims = match Claims::decode(&self.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return self.error("Not logged in."); + } + }; + + let user_id = claims.id; + + let community_user_ban_form = CommunityUserBanForm { + community_id: self.community_id, + user_id: self.user_id, + }; + + if self.ban { + match CommunityUserBan::ban(&conn, &community_user_ban_form) { + Ok(user) => user, + Err(_e) => { + return self.error("Community user ban already exists"); + } + }; + } else { + match CommunityUserBan::unban(&conn, &community_user_ban_form) { + Ok(user) => user, + Err(_e) => { + return self.error("Community user ban already exists"); + } + }; + } + + // Mod tables + let expires = match self.expires { + Some(time) => Some(naive_from_unix(time)), + None => None + }; + + let form = ModBanFromCommunityForm { + mod_user_id: user_id, + other_user_id: self.user_id, + community_id: self.community_id, + reason: self.reason.to_owned(), + banned: Some(self.ban), + expires: expires, + }; + ModBanFromCommunity::create(&conn, &form).unwrap(); + + let user_view = UserView::read(&conn, self.user_id).unwrap(); + + let res = serde_json::to_string( + &BanFromCommunityResponse { + op: self.op_type().to_string(), + user: user_view, + banned: self.ban + } + ) + .unwrap(); + + + chat.send_community_message(&conn, self.community_id, &res, addr); + + res + } +} + +impl Perform for AddModToCommunity { + fn op_type(&self) -> UserOperation { + UserOperation::AddModToCommunity + } + + fn perform(&self, chat: &mut ChatServer, addr: usize) -> String { + + let conn = establish_connection(); + + let claims = match Claims::decode(&self.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return self.error("Not logged in."); + } + }; + + let user_id = claims.id; + + let community_moderator_form = CommunityModeratorForm { + community_id: self.community_id, + user_id: self.user_id + }; + + if self.added { + match CommunityModerator::join(&conn, &community_moderator_form) { + Ok(user) => user, + Err(_e) => { + return self.error("Community moderator already exists."); + } + }; + } else { + match CommunityModerator::leave(&conn, &community_moderator_form) { + Ok(user) => user, + Err(_e) => { + return self.error("Community moderator already exists."); + } + }; + } + + // Mod tables + let form = ModAddCommunityForm { + mod_user_id: user_id, + other_user_id: self.user_id, + community_id: self.community_id, + removed: Some(!self.added), + }; + ModAddCommunity::create(&conn, &form).unwrap(); + + let moderators = CommunityModeratorView::for_community(&conn, self.community_id).unwrap(); + + let res = serde_json::to_string( + &AddModToCommunityResponse { + op: self.op_type().to_string(), + moderators: moderators, + } + ) + .unwrap(); + + + chat.send_community_message(&conn, self.community_id, &res, addr); + + res + + } +} + +impl Perform for CreateSite { + fn op_type(&self) -> UserOperation { + UserOperation::CreateSite + } + + fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String { + + let conn = establish_connection(); + + let claims = match Claims::decode(&self.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return self.error("Not logged in."); + } + }; + + if has_slurs(&self.name) || + (self.description.is_some() && has_slurs(&self.description.to_owned().unwrap())) { + return self.error("No slurs"); + } + + let user_id = claims.id; + + // Make sure user is an admin + if !UserView::read(&conn, user_id).unwrap().admin { + return self.error("Not an admin."); + } + + let site_form = SiteForm { + name: self.name.to_owned(), + description: self.description.to_owned(), + creator_id: user_id, + updated: None, + }; + + match Site::create(&conn, &site_form) { + Ok(site) => site, + Err(_e) => { + return self.error("Site exists already"); + } + }; + + let site_view = SiteView::read(&conn).unwrap(); + + serde_json::to_string( + &SiteResponse { + op: self.op_type().to_string(), + site: site_view, + } + ) + .unwrap() + } +} + +impl Perform for EditSite { + fn op_type(&self) -> UserOperation { + UserOperation::EditSite + } + + fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String { + + let conn = establish_connection(); + + let claims = match Claims::decode(&self.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return self.error("Not logged in."); + } + }; + + if has_slurs(&self.name) || + (self.description.is_some() && has_slurs(&self.description.to_owned().unwrap())) { + return self.error("No slurs"); + } + + let user_id = claims.id; + + // Make sure user is an admin + if UserView::read(&conn, user_id).unwrap().admin == false { + return self.error("Not an admin."); + } + + let found_site = Site::read(&conn, 1).unwrap(); + + let site_form = SiteForm { + name: self.name.to_owned(), + description: self.description.to_owned(), + creator_id: found_site.creator_id, + updated: Some(naive_now()), + }; + + match Site::update(&conn, 1, &site_form) { + Ok(site) => site, + Err(_e) => { + return self.error("Couldn't update site."); + } + }; + + let site_view = SiteView::read(&conn).unwrap(); + + serde_json::to_string( + &SiteResponse { + op: self.op_type().to_string(), + site: site_view, + } + ) + .unwrap() + } +} + +impl Perform for GetSite { + fn op_type(&self) -> UserOperation { + UserOperation::GetSite + } + + fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String { + + let conn = establish_connection(); + + // It can return a null site in order to redirect + let site_view = match Site::read(&conn, 1) { + Ok(_site) => Some(SiteView::read(&conn).unwrap()), + Err(_e) => None + }; + + let admins = UserView::admins(&conn).unwrap(); + let banned = UserView::banned(&conn).unwrap(); + + serde_json::to_string( + &GetSiteResponse { + op: self.op_type().to_string(), + site: site_view, + admins: admins, + banned: banned, + } + ) + .unwrap() + } +} + +impl Perform for AddAdmin { + fn op_type(&self) -> UserOperation { + UserOperation::AddAdmin + } + + fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String { + + let conn = establish_connection(); + + let claims = match Claims::decode(&self.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return self.error("Not logged in."); + } + }; + + let user_id = claims.id; + + // Make sure user is an admin + if UserView::read(&conn, user_id).unwrap().admin == false { + return self.error("Not an admin."); + } + + let read_user = User_::read(&conn, self.user_id).unwrap(); + + let user_form = UserForm { + name: read_user.name, + fedi_name: read_user.fedi_name, + email: read_user.email, + password_encrypted: read_user.password_encrypted, + preferred_username: read_user.preferred_username, + updated: Some(naive_now()), + admin: self.added, + banned: read_user.banned, + }; + + match User_::update(&conn, self.user_id, &user_form) { + Ok(user) => user, + Err(_e) => { + return self.error("Couldn't update user"); + } + }; + + // Mod tables + let form = ModAddForm { + mod_user_id: user_id, + other_user_id: self.user_id, + removed: Some(!self.added), + }; + + ModAdd::create(&conn, &form).unwrap(); + + let admins = UserView::admins(&conn).unwrap(); + + let res = serde_json::to_string( + &AddAdminResponse { + op: self.op_type().to_string(), + admins: admins, + } + ) + .unwrap(); + + res + + } +} + +impl Perform for BanUser { + fn op_type(&self) -> UserOperation { + UserOperation::BanUser + } + + fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String { + + let conn = establish_connection(); + + let claims = match Claims::decode(&self.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return self.error("Not logged in."); + } + }; + + let user_id = claims.id; + + // Make sure user is an admin + if UserView::read(&conn, user_id).unwrap().admin == false { + return self.error("Not an admin."); + } + + let read_user = User_::read(&conn, self.user_id).unwrap(); + + let user_form = UserForm { + name: read_user.name, + fedi_name: read_user.fedi_name, + email: read_user.email, + password_encrypted: read_user.password_encrypted, + preferred_username: read_user.preferred_username, + updated: Some(naive_now()), + admin: read_user.admin, + banned: self.ban, + }; + + match User_::update(&conn, self.user_id, &user_form) { + Ok(user) => user, + Err(_e) => { + return self.error("Couldn't update user"); + } + }; + + // Mod tables + let expires = match self.expires { + Some(time) => Some(naive_from_unix(time)), + None => None + }; + + let form = ModBanForm { + mod_user_id: user_id, + other_user_id: self.user_id, + reason: self.reason.to_owned(), + banned: Some(self.ban), + expires: expires, + }; + + ModBan::create(&conn, &form).unwrap(); + + let user_view = UserView::read(&conn, self.user_id).unwrap(); + + let res = serde_json::to_string( + &BanUserResponse { + op: self.op_type().to_string(), + user: user_view, + banned: self.ban + } + ) + .unwrap(); + + res + + } +} diff --git a/ui/.gitignore b/ui/.gitignore index 1e420dc..cc0ab54 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -1,4 +1,3 @@ -src/version.ts dist .fusebox _site diff --git a/ui/fuse.js b/ui/fuse.js index c70edfb..0fdf9a4 100644 --- a/ui/fuse.js +++ b/ui/fuse.js @@ -11,7 +11,7 @@ const transformInferno = require('ts-transform-inferno').default; const transformClasscat = require('ts-transform-classcat').default; let fuse, app; let isProduction = false; -var setVersion = require('./set_version.js').setVersion; +// var setVersion = require('./set_version.js').setVersion; Sparky.task('config', _ => { fuse = new FuseBox({ @@ -42,16 +42,16 @@ Sparky.task('config', _ => { }); app = fuse.bundle('app').instructions('>index.tsx'); }); -Sparky.task('version', _ => setVersion()); +// Sparky.task('version', _ => setVersion()); Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/')); Sparky.task('env', _ => (isProduction = true)); Sparky.task('copy-assets', () => Sparky.src('assets/*.svg').dest('dist/')); -Sparky.task('dev', ['clean', 'config', 'copy-assets', 'version'], _ => { +Sparky.task('dev', ['clean', 'config', 'copy-assets'], _ => { fuse.dev(); app.hmr().watch(); return fuse.run(); }); -Sparky.task('prod', ['clean', 'env', 'config', 'copy-assets', 'version'], _ => { +Sparky.task('prod', ['clean', 'env', 'config', 'copy-assets'], _ => { // fuse.dev({ reload: true }); // remove after demo return fuse.run(); }); diff --git a/ui/set_version.js b/ui/set_version.js old mode 100644 new mode 100755 index bfd640c..2189308 --- a/ui/set_version.js +++ b/ui/set_version.js @@ -7,3 +7,5 @@ exports.setVersion = function() { let line = `export let version: string = "${revision}";`; fs.writeFileSync("./src/version.ts", line); } + +this.setVersion() diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index a87dd35..df079ba 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -1,6 +1,6 @@ import { Component, linkEvent } from 'inferno'; import { CommentNode as CommentNodeI, CommentForm as CommentFormI } from '../interfaces'; -import { WebSocketService } from '../services'; +import { WebSocketService, UserService } from '../services'; import * as autosize from 'autosize'; interface CommentFormProps { @@ -8,6 +8,7 @@ interface CommentFormProps { node?: CommentNodeI; onReplyCancel?(): any; edit?: boolean; + disabled?: boolean; } interface CommentFormState { @@ -21,9 +22,10 @@ export class CommentForm extends Component { commentForm: { auth: null, content: null, - post_id: this.props.node ? this.props.node.comment.post_id : this.props.postId + post_id: this.props.node ? this.props.node.comment.post_id : this.props.postId, + creator_id: UserService.Instance.user ? UserService.Instance.user.id : null, }, - buttonTitle: !this.props.node ? "Post" : this.props.edit ? "Edit" : "Reply" + buttonTitle: !this.props.node ? "Post" : this.props.edit ? "Edit" : "Reply", } constructor(props: any, context: any) { @@ -36,6 +38,7 @@ export class CommentForm extends Component { this.state.commentForm.edit_id = this.props.node.comment.id; this.state.commentForm.parent_id = this.props.node.comment.parent_id; this.state.commentForm.content = this.props.node.comment.content; + this.state.commentForm.creator_id = this.props.node.comment.creator_id; } else { // A reply gets a new parent id this.state.commentForm.parent_id = this.props.node.comment.id; @@ -53,12 +56,12 @@ export class CommentForm extends Component {
-