mirror of https://github.com/LemmyNet/lemmy.git
Adding SiteAggregates.
parent
ca7224c086
commit
7731479607
|
@ -19,6 +19,7 @@ use lemmy_db::{
|
||||||
naive_now,
|
naive_now,
|
||||||
post_view::*,
|
post_view::*,
|
||||||
site::*,
|
site::*,
|
||||||
|
site_aggregates::SiteAggregates,
|
||||||
user_view::*,
|
user_view::*,
|
||||||
views::site_view::SiteView,
|
views::site_view::SiteView,
|
||||||
Crud,
|
Crud,
|
||||||
|
@ -310,6 +311,8 @@ impl Perform for GetSite {
|
||||||
u
|
u
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let counts = blocking(context.pool(), move |conn| SiteAggregates::read(conn)).await??;
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
site: site_view,
|
site: site_view,
|
||||||
admins,
|
admins,
|
||||||
|
@ -318,11 +321,7 @@ impl Perform for GetSite {
|
||||||
version: version::VERSION.to_string(),
|
version: version::VERSION.to_string(),
|
||||||
my_user,
|
my_user,
|
||||||
federated_instances: linked_instances(context.pool()).await?,
|
federated_instances: linked_instances(context.pool()).await?,
|
||||||
// TODO
|
counts,
|
||||||
number_of_users: 0,
|
|
||||||
number_of_posts: 0,
|
|
||||||
number_of_comments: 0,
|
|
||||||
number_of_communities: 0,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,6 +545,8 @@ impl Perform for TransferSite {
|
||||||
|
|
||||||
let banned = blocking(context.pool(), move |conn| UserView::banned(conn)).await??;
|
let banned = blocking(context.pool(), move |conn| UserView::banned(conn)).await??;
|
||||||
|
|
||||||
|
let counts = blocking(context.pool(), move |conn| SiteAggregates::read(conn)).await??;
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
site: Some(site_view),
|
site: Some(site_view),
|
||||||
admins,
|
admins,
|
||||||
|
@ -554,11 +555,7 @@ impl Perform for TransferSite {
|
||||||
version: version::VERSION.to_string(),
|
version: version::VERSION.to_string(),
|
||||||
my_user: Some(user),
|
my_user: Some(user),
|
||||||
federated_instances: linked_instances(context.pool()).await?,
|
federated_instances: linked_instances(context.pool()).await?,
|
||||||
// TODO
|
counts,
|
||||||
number_of_users: 0,
|
|
||||||
number_of_posts: 0,
|
|
||||||
number_of_comments: 0,
|
|
||||||
number_of_communities: 0,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,7 @@ async fn check_private_message_activity_valid<T, Kind>(
|
||||||
where
|
where
|
||||||
T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
|
T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
|
||||||
{
|
{
|
||||||
let to_and_cc = get_activity_to_and_cc(activity)?;
|
let to_and_cc = get_activity_to_and_cc(activity);
|
||||||
if to_and_cc.len() != 1 {
|
if to_and_cc.len() != 1 {
|
||||||
return Err(anyhow!("Private message can only be addressed to one user").into());
|
return Err(anyhow!("Private message can only be addressed to one user").into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ pub async fn community_inbox(
|
||||||
Community::read_from_name(&conn, &path)
|
Community::read_from_name(&conn, &path)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
let to_and_cc = get_activity_to_and_cc(&activity)?;
|
let to_and_cc = get_activity_to_and_cc(&activity);
|
||||||
if !to_and_cc.contains(&&community.actor_id()?) {
|
if !to_and_cc.contains(&&community.actor_id()?) {
|
||||||
return Err(anyhow!("Activity delivered to wrong community").into());
|
return Err(anyhow!("Activity delivered to wrong community").into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ pub(crate) async fn is_activity_already_known(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_activity_to_and_cc<T, Kind>(activity: &T) -> Result<Vec<Url>, LemmyError>
|
pub(crate) fn get_activity_to_and_cc<T, Kind>(activity: &T) -> Vec<Url>
|
||||||
where
|
where
|
||||||
T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
|
T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
|
||||||
{
|
{
|
||||||
|
@ -75,14 +75,14 @@ where
|
||||||
.collect();
|
.collect();
|
||||||
to_and_cc.append(&mut cc);
|
to_and_cc.append(&mut cc);
|
||||||
}
|
}
|
||||||
Ok(to_and_cc)
|
to_and_cc
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_addressed_to_public<T, Kind>(activity: &T) -> Result<(), LemmyError>
|
pub(crate) fn is_addressed_to_public<T, Kind>(activity: &T) -> Result<(), LemmyError>
|
||||||
where
|
where
|
||||||
T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
|
T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
|
||||||
{
|
{
|
||||||
let to_and_cc = get_activity_to_and_cc(activity)?;
|
let to_and_cc = get_activity_to_and_cc(activity);
|
||||||
if to_and_cc.contains(&public()) {
|
if to_and_cc.contains(&public()) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -66,7 +66,7 @@ pub async fn shared_inbox(
|
||||||
|
|
||||||
let activity_any_base = activity.clone().into_any_base()?;
|
let activity_any_base = activity.clone().into_any_base()?;
|
||||||
let mut res: Option<HttpResponse> = None;
|
let mut res: Option<HttpResponse> = None;
|
||||||
let to_and_cc = get_activity_to_and_cc(&activity)?;
|
let to_and_cc = get_activity_to_and_cc(&activity);
|
||||||
// Handle community first, so in case the sender is banned by the community, it will error out.
|
// Handle community first, so in case the sender is banned by the community, it will error out.
|
||||||
// If we handled the user receive first, the activity would be inserted to the database before the
|
// If we handled the user receive first, the activity would be inserted to the database before the
|
||||||
// community could check for bans.
|
// community could check for bans.
|
||||||
|
|
|
@ -101,7 +101,7 @@ pub async fn user_inbox(
|
||||||
User_::read_from_name(&conn, &username)
|
User_::read_from_name(&conn, &username)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
let to_and_cc = get_activity_to_and_cc(&activity)?;
|
let to_and_cc = get_activity_to_and_cc(&activity);
|
||||||
// TODO: we should also accept activities that are sent to community followers
|
// TODO: we should also accept activities that are sent to community followers
|
||||||
if !to_and_cc.contains(&&user.actor_id()?) {
|
if !to_and_cc.contains(&&user.actor_id()?) {
|
||||||
return Err(anyhow!("Activity delivered to wrong user").into());
|
return Err(anyhow!("Activity delivered to wrong user").into());
|
||||||
|
@ -172,7 +172,7 @@ async fn is_for_user_inbox(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
activity: &UserAcceptedActivities,
|
activity: &UserAcceptedActivities,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let to_and_cc = get_activity_to_and_cc(activity)?;
|
let to_and_cc = get_activity_to_and_cc(activity);
|
||||||
// Check if it is addressed directly to any local user
|
// Check if it is addressed directly to any local user
|
||||||
if is_addressed_to_local_user(&to_and_cc, context.pool()).await? {
|
if is_addressed_to_local_user(&to_and_cc, context.pool()).await? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub mod private_message;
|
||||||
pub mod private_message_view;
|
pub mod private_message_view;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
pub mod site;
|
pub mod site;
|
||||||
pub mod site_view;
|
pub mod site_aggregates;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod user_mention;
|
pub mod user_mention;
|
||||||
pub mod user_mention_view;
|
pub mod user_mention_view;
|
||||||
|
|
|
@ -440,6 +440,16 @@ table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
site_aggregates (id) {
|
||||||
|
id -> Int4,
|
||||||
|
users -> Int8,
|
||||||
|
posts -> Int8,
|
||||||
|
comments -> Int8,
|
||||||
|
communities -> Int8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
user_ (id) {
|
user_ (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -587,6 +597,7 @@ allow_tables_to_appear_in_same_query!(
|
||||||
post_saved,
|
post_saved,
|
||||||
private_message,
|
private_message,
|
||||||
site,
|
site,
|
||||||
|
site_aggregates,
|
||||||
user_,
|
user_,
|
||||||
user_ban,
|
user_ban,
|
||||||
user_fast,
|
user_fast,
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::schema::site_aggregates;
|
||||||
|
use diesel::{result::Error, *};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)]
|
||||||
|
#[table_name = "site_aggregates"]
|
||||||
|
pub struct SiteAggregates {
|
||||||
|
pub id: i32,
|
||||||
|
pub users: i64,
|
||||||
|
pub posts: i64,
|
||||||
|
pub comments: i64,
|
||||||
|
pub communities: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SiteAggregates {
|
||||||
|
pub fn read(conn: &PgConnection) -> Result<Self, Error> {
|
||||||
|
site_aggregates::table.first::<Self>(conn)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,55 +0,0 @@
|
||||||
use diesel::{result::Error, *};
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
table! {
|
|
||||||
site_view (id) {
|
|
||||||
id -> Int4,
|
|
||||||
name -> Varchar,
|
|
||||||
description -> Nullable<Text>,
|
|
||||||
creator_id -> Int4,
|
|
||||||
published -> Timestamp,
|
|
||||||
updated -> Nullable<Timestamp>,
|
|
||||||
enable_downvotes -> Bool,
|
|
||||||
open_registration -> Bool,
|
|
||||||
enable_nsfw -> Bool,
|
|
||||||
icon -> Nullable<Text>,
|
|
||||||
banner -> Nullable<Text>,
|
|
||||||
creator_name -> Varchar,
|
|
||||||
creator_preferred_username -> Nullable<Varchar>,
|
|
||||||
creator_avatar -> Nullable<Text>,
|
|
||||||
number_of_users -> BigInt,
|
|
||||||
number_of_posts -> BigInt,
|
|
||||||
number_of_comments -> BigInt,
|
|
||||||
number_of_communities -> BigInt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, QueryableByName, Clone)]
|
|
||||||
#[table_name = "site_view"]
|
|
||||||
pub struct SiteView {
|
|
||||||
pub id: i32,
|
|
||||||
pub name: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub creator_id: i32,
|
|
||||||
pub published: chrono::NaiveDateTime,
|
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
|
||||||
pub enable_downvotes: bool,
|
|
||||||
pub open_registration: bool,
|
|
||||||
pub enable_nsfw: bool,
|
|
||||||
pub icon: Option<String>,
|
|
||||||
pub banner: Option<String>,
|
|
||||||
pub creator_name: String,
|
|
||||||
pub creator_preferred_username: Option<String>,
|
|
||||||
pub creator_avatar: Option<String>,
|
|
||||||
pub number_of_users: i64,
|
|
||||||
pub number_of_posts: i64,
|
|
||||||
pub number_of_comments: i64,
|
|
||||||
pub number_of_communities: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SiteView {
|
|
||||||
pub fn read(conn: &PgConnection) -> Result<Self, Error> {
|
|
||||||
use super::site_view::site_view::dsl::*;
|
|
||||||
site_view.first::<Self>(conn)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +1,2 @@
|
||||||
pub mod site_view;
|
pub mod site_view;
|
||||||
|
pub mod user_view;
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
use crate::{
|
||||||
|
schema::user_,
|
||||||
|
user::{UserSafe, User_},
|
||||||
|
};
|
||||||
|
use diesel::{result::Error, *};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Clone)]
|
||||||
|
pub struct UserViewSafe {
|
||||||
|
pub user: UserSafe,
|
||||||
|
// TODO
|
||||||
|
// pub number_of_posts: i64,
|
||||||
|
// pub post_score: i64,
|
||||||
|
// pub number_of_comments: i64,
|
||||||
|
// pub comment_score: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UserViewDangerous {
|
||||||
|
pub user: User_,
|
||||||
|
// TODO
|
||||||
|
// pub number_of_posts: i64,
|
||||||
|
// pub post_score: i64,
|
||||||
|
// pub number_of_comments: i64,
|
||||||
|
// pub comment_score: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserViewDangerous {
|
||||||
|
pub fn read(conn: &PgConnection, id: i32) -> Result<Self, Error> {
|
||||||
|
let user = user_::table.find(id).first::<User_>(conn)?;
|
||||||
|
Ok(Self { user })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserViewSafe {
|
||||||
|
pub fn read(conn: &PgConnection, id: i32) -> Result<Self, Error> {
|
||||||
|
let user = user_::table.find(id).first::<User_>(conn)?.to_safe();
|
||||||
|
Ok(Self { user })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn admins(conn: &PgConnection) -> Result<Vec<Self>, Error> {
|
||||||
|
let admins = user_::table
|
||||||
|
// TODO do joins here
|
||||||
|
.filter(user_::admin.eq(true))
|
||||||
|
.order_by(user_::published)
|
||||||
|
.load::<User_>(conn)?;
|
||||||
|
|
||||||
|
Ok(vec_to_user_view_safe(admins))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn banned(conn: &PgConnection) -> Result<Vec<Self>, Error> {
|
||||||
|
let banned = user_::table
|
||||||
|
// TODO do joins here
|
||||||
|
.filter(user_::banned.eq(true))
|
||||||
|
.load::<User_>(conn)?;
|
||||||
|
|
||||||
|
Ok(vec_to_user_view_safe(banned))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec_to_user_view_safe(users: Vec<User_>) -> Vec<UserViewSafe> {
|
||||||
|
users
|
||||||
|
.iter()
|
||||||
|
.map(|a| UserViewSafe { user: a.to_safe() })
|
||||||
|
.collect::<Vec<UserViewSafe>>()
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ use lemmy_db::{
|
||||||
community_view::*,
|
community_view::*,
|
||||||
moderator_views::*,
|
moderator_views::*,
|
||||||
post_view::*,
|
post_view::*,
|
||||||
|
site_aggregates::SiteAggregates,
|
||||||
user::*,
|
user::*,
|
||||||
user_view::*,
|
user_view::*,
|
||||||
views::site_view::SiteView,
|
views::site_view::SiteView,
|
||||||
|
@ -98,10 +99,7 @@ pub struct SiteResponse {
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct GetSiteResponse {
|
pub struct GetSiteResponse {
|
||||||
pub site: Option<SiteView>, // Because the site might not be set up yet
|
pub site: Option<SiteView>, // Because the site might not be set up yet
|
||||||
pub number_of_users: i64,
|
pub counts: SiteAggregates,
|
||||||
pub number_of_posts: i64,
|
|
||||||
pub number_of_comments: i64,
|
|
||||||
pub number_of_communities: i64,
|
|
||||||
pub admins: Vec<UserView>,
|
pub admins: Vec<UserView>,
|
||||||
pub banned: Vec<UserView>,
|
pub banned: Vec<UserView>,
|
||||||
pub online: usize,
|
pub online: usize,
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
-- Site aggregates
|
||||||
|
drop table site_aggregates;
|
||||||
|
drop trigger site_aggregates_insert_user on user_;
|
||||||
|
drop trigger site_aggregates_delete_user on user_;
|
||||||
|
drop trigger site_aggregates_insert_post on post;
|
||||||
|
drop trigger site_aggregates_delete_post on post;
|
||||||
|
drop trigger site_aggregates_insert_comment on comment;
|
||||||
|
drop trigger site_aggregates_delete_comment on comment;
|
||||||
|
drop trigger site_aggregates_insert_community on community;
|
||||||
|
drop trigger site_aggregates_delete_community on community;
|
||||||
|
drop function
|
||||||
|
site_aggregates_user_increment,
|
||||||
|
site_aggregates_user_decrement,
|
||||||
|
site_aggregates_post_increment,
|
||||||
|
site_aggregates_post_decrement,
|
||||||
|
site_aggregates_comment_increment,
|
||||||
|
site_aggregates_comment_decrement,
|
||||||
|
site_aggregates_community_increment,
|
||||||
|
site_aggregates_community_decrement;
|
|
@ -0,0 +1,124 @@
|
||||||
|
-- Add site aggregates
|
||||||
|
create table site_aggregates (
|
||||||
|
id serial primary key,
|
||||||
|
users bigint not null,
|
||||||
|
posts bigint not null,
|
||||||
|
comments bigint not null,
|
||||||
|
communities bigint not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into site_aggregates (users, posts, comments, communities)
|
||||||
|
select ( select coalesce(count(*), 0) from user_) as users,
|
||||||
|
( select coalesce(count(*), 0) from post) as posts,
|
||||||
|
( select coalesce(count(*), 0) from comment) as comments,
|
||||||
|
( select coalesce(count(*), 0) from community) as communities;
|
||||||
|
|
||||||
|
-- Add site aggregate triggers
|
||||||
|
-- user
|
||||||
|
create function site_aggregates_user_increment()
|
||||||
|
returns trigger language plpgsql
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
update site_aggregates
|
||||||
|
set users = users + 1;
|
||||||
|
return null;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
create trigger site_aggregates_insert_user
|
||||||
|
after insert on user_
|
||||||
|
execute procedure site_aggregates_user_increment();
|
||||||
|
|
||||||
|
create function site_aggregates_user_decrement()
|
||||||
|
returns trigger language plpgsql
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
update site_aggregates
|
||||||
|
set users = users - 1;
|
||||||
|
return null;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
create trigger site_aggregates_delete_user
|
||||||
|
after delete on user_
|
||||||
|
execute procedure site_aggregates_user_decrement();
|
||||||
|
|
||||||
|
-- post
|
||||||
|
create function site_aggregates_post_increment()
|
||||||
|
returns trigger language plpgsql
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
update site_aggregates
|
||||||
|
set posts = posts + 1;
|
||||||
|
return null;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
create trigger site_aggregates_insert_post
|
||||||
|
after insert on post
|
||||||
|
execute procedure site_aggregates_post_increment();
|
||||||
|
|
||||||
|
create function site_aggregates_post_decrement()
|
||||||
|
returns trigger language plpgsql
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
update site_aggregates
|
||||||
|
set posts = posts - 1;
|
||||||
|
return null;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
create trigger site_aggregates_delete_post
|
||||||
|
after delete on post
|
||||||
|
execute procedure site_aggregates_post_decrement();
|
||||||
|
|
||||||
|
-- comment
|
||||||
|
create function site_aggregates_comment_increment()
|
||||||
|
returns trigger language plpgsql
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
update site_aggregates
|
||||||
|
set comments = comments + 1;
|
||||||
|
return null;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
create trigger site_aggregates_insert_comment
|
||||||
|
after insert on comment
|
||||||
|
execute procedure site_aggregates_comment_increment();
|
||||||
|
|
||||||
|
create function site_aggregates_comment_decrement()
|
||||||
|
returns trigger language plpgsql
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
update site_aggregates
|
||||||
|
set comments = comments - 1;
|
||||||
|
return null;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
create trigger site_aggregates_delete_comment
|
||||||
|
after delete on comment
|
||||||
|
execute procedure site_aggregates_comment_decrement();
|
||||||
|
|
||||||
|
-- community
|
||||||
|
create function site_aggregates_community_increment()
|
||||||
|
returns trigger language plpgsql
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
update site_aggregates
|
||||||
|
set communities = communities + 1;
|
||||||
|
return null;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
create trigger site_aggregates_insert_community
|
||||||
|
after insert on community
|
||||||
|
execute procedure site_aggregates_community_increment();
|
||||||
|
|
||||||
|
create function site_aggregates_community_decrement()
|
||||||
|
returns trigger language plpgsql
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
update site_aggregates
|
||||||
|
set communities = communities - 1;
|
||||||
|
return null;
|
||||||
|
end $$;
|
||||||
|
|
||||||
|
create trigger site_aggregates_delete_community
|
||||||
|
after delete on community
|
||||||
|
execute procedure site_aggregates_community_decrement();
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
-- This file should undo anything in `up.sql`
|
|
|
@ -1 +0,0 @@
|
||||||
-- Your SQL goes here
|
|
Loading…
Reference in New Issue