mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
update library_item table
This commit is contained in:
parent
da0d1705b5
commit
1da8dc6545
10 changed files with 122 additions and 343 deletions
|
|
@ -1,53 +0,0 @@
|
|||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm'
|
||||
import { Feed } from './feed'
|
||||
|
||||
@Entity()
|
||||
export class FeedItem {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string
|
||||
|
||||
@Column('text')
|
||||
guid!: string
|
||||
|
||||
@Column('text')
|
||||
title!: string
|
||||
|
||||
@Column('array')
|
||||
links!: string[]
|
||||
|
||||
@Column('text')
|
||||
author?: string | null
|
||||
|
||||
@Column('text')
|
||||
summary?: string | null
|
||||
|
||||
@Column('array')
|
||||
categories?: string[] | null
|
||||
|
||||
@Column('text')
|
||||
content?: string | null
|
||||
|
||||
@Column('text')
|
||||
previewContent?: string | null
|
||||
|
||||
@CreateDateColumn({ default: () => 'CURRENT_TIMESTAMP' })
|
||||
createdAt!: Date
|
||||
|
||||
@UpdateDateColumn({ default: () => 'CURRENT_TIMESTAMP' })
|
||||
updatedAt!: Date
|
||||
|
||||
@Column('timestamptz')
|
||||
publishedAt?: Date | null
|
||||
|
||||
@ManyToOne(() => Feed, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'feed_id' })
|
||||
feed!: Feed
|
||||
}
|
||||
|
|
@ -837,50 +837,6 @@ export type FiltersSuccess = {
|
|||
filters: Array<Filter>;
|
||||
};
|
||||
|
||||
export type Following = {
|
||||
__typename?: 'Following';
|
||||
author?: Maybe<Scalars['String']>;
|
||||
categories?: Maybe<Array<Scalars['String']>>;
|
||||
content?: Maybe<Scalars['String']>;
|
||||
createdAt: Scalars['Date'];
|
||||
feedItemId: Scalars['ID'];
|
||||
hiddenAt?: Maybe<Scalars['Date']>;
|
||||
id: Scalars['ID'];
|
||||
isHidden: Scalars['Boolean'];
|
||||
isSaved: Scalars['Boolean'];
|
||||
links: Array<Scalars['String']>;
|
||||
previewContent?: Maybe<Scalars['String']>;
|
||||
publishedAt?: Maybe<Scalars['Date']>;
|
||||
savedAt?: Maybe<Scalars['Date']>;
|
||||
summary?: Maybe<Scalars['String']>;
|
||||
title: Scalars['String'];
|
||||
updatedAt: Scalars['Date'];
|
||||
};
|
||||
|
||||
export type FollowingEdge = {
|
||||
__typename?: 'FollowingEdge';
|
||||
cursor: Scalars['String'];
|
||||
node: Following;
|
||||
};
|
||||
|
||||
export type FollowingError = {
|
||||
__typename?: 'FollowingError';
|
||||
errorCodes: Array<FollowingErrorCode>;
|
||||
};
|
||||
|
||||
export enum FollowingErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type FollowingResult = FollowingError | FollowingSuccess;
|
||||
|
||||
export type FollowingSuccess = {
|
||||
__typename?: 'FollowingSuccess';
|
||||
edges: Array<FollowingEdge>;
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type GenerateApiKeyError = {
|
||||
__typename?: 'GenerateApiKeyError';
|
||||
errorCodes: Array<GenerateApiKeyErrorCode>;
|
||||
|
|
@ -1815,7 +1771,6 @@ export type Query = {
|
|||
deviceTokens: DeviceTokensResult;
|
||||
feeds: FeedsResult;
|
||||
filters: FiltersResult;
|
||||
following: FollowingResult;
|
||||
getUserPersonalization: GetUserPersonalizationResult;
|
||||
groups: GroupsResult;
|
||||
hello?: Maybe<Scalars['String']>;
|
||||
|
|
@ -1857,14 +1812,6 @@ export type QueryFeedsArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type QueryFollowingArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
since?: InputMaybe<Scalars['Date']>;
|
||||
until?: InputMaybe<Scalars['Date']>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryRulesArgs = {
|
||||
enabled?: InputMaybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
|
@ -3581,12 +3528,6 @@ export type ResolversTypes = {
|
|||
FiltersResult: ResolversTypes['FiltersError'] | ResolversTypes['FiltersSuccess'];
|
||||
FiltersSuccess: ResolverTypeWrapper<FiltersSuccess>;
|
||||
Float: ResolverTypeWrapper<Scalars['Float']>;
|
||||
Following: ResolverTypeWrapper<Following>;
|
||||
FollowingEdge: ResolverTypeWrapper<FollowingEdge>;
|
||||
FollowingError: ResolverTypeWrapper<FollowingError>;
|
||||
FollowingErrorCode: FollowingErrorCode;
|
||||
FollowingResult: ResolversTypes['FollowingError'] | ResolversTypes['FollowingSuccess'];
|
||||
FollowingSuccess: ResolverTypeWrapper<FollowingSuccess>;
|
||||
GenerateApiKeyError: ResolverTypeWrapper<GenerateApiKeyError>;
|
||||
GenerateApiKeyErrorCode: GenerateApiKeyErrorCode;
|
||||
GenerateApiKeyInput: GenerateApiKeyInput;
|
||||
|
|
@ -4071,11 +4012,6 @@ export type ResolversParentTypes = {
|
|||
FiltersResult: ResolversParentTypes['FiltersError'] | ResolversParentTypes['FiltersSuccess'];
|
||||
FiltersSuccess: FiltersSuccess;
|
||||
Float: Scalars['Float'];
|
||||
Following: Following;
|
||||
FollowingEdge: FollowingEdge;
|
||||
FollowingError: FollowingError;
|
||||
FollowingResult: ResolversParentTypes['FollowingError'] | ResolversParentTypes['FollowingSuccess'];
|
||||
FollowingSuccess: FollowingSuccess;
|
||||
GenerateApiKeyError: GenerateApiKeyError;
|
||||
GenerateApiKeyInput: GenerateApiKeyInput;
|
||||
GenerateApiKeyResult: ResolversParentTypes['GenerateApiKeyError'] | ResolversParentTypes['GenerateApiKeySuccess'];
|
||||
|
|
@ -4958,47 +4894,6 @@ export type FiltersSuccessResolvers<ContextType = ResolverContext, ParentType ex
|
|||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type FollowingResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['Following'] = ResolversParentTypes['Following']> = {
|
||||
author?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
categories?: Resolver<Maybe<Array<ResolversTypes['String']>>, ParentType, ContextType>;
|
||||
content?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
feedItemId?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
hiddenAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
isHidden?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
isSaved?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
links?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
previewContent?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
publishedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
savedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
summary?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
title?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type FollowingEdgeResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['FollowingEdge'] = ResolversParentTypes['FollowingEdge']> = {
|
||||
cursor?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
node?: Resolver<ResolversTypes['Following'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type FollowingErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['FollowingError'] = ResolversParentTypes['FollowingError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['FollowingErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type FollowingResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['FollowingResult'] = ResolversParentTypes['FollowingResult']> = {
|
||||
__resolveType: TypeResolveFn<'FollowingError' | 'FollowingSuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type FollowingSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['FollowingSuccess'] = ResolversParentTypes['FollowingSuccess']> = {
|
||||
edges?: Resolver<Array<ResolversTypes['FollowingEdge']>, ParentType, ContextType>;
|
||||
pageInfo?: Resolver<ResolversTypes['PageInfo'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type GenerateApiKeyErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['GenerateApiKeyError'] = ResolversParentTypes['GenerateApiKeyError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['GenerateApiKeyErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
|
|
@ -5470,7 +5365,6 @@ export type QueryResolvers<ContextType = ResolverContext, ParentType extends Res
|
|||
deviceTokens?: Resolver<ResolversTypes['DeviceTokensResult'], ParentType, ContextType>;
|
||||
feeds?: Resolver<ResolversTypes['FeedsResult'], ParentType, ContextType, RequireFields<QueryFeedsArgs, 'input'>>;
|
||||
filters?: Resolver<ResolversTypes['FiltersResult'], ParentType, ContextType>;
|
||||
following?: Resolver<ResolversTypes['FollowingResult'], ParentType, ContextType, Partial<QueryFollowingArgs>>;
|
||||
getUserPersonalization?: Resolver<ResolversTypes['GetUserPersonalizationResult'], ParentType, ContextType>;
|
||||
groups?: Resolver<ResolversTypes['GroupsResult'], ParentType, ContextType>;
|
||||
hello?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
|
|
@ -6505,11 +6399,6 @@ export type Resolvers<ContextType = ResolverContext> = {
|
|||
FiltersError?: FiltersErrorResolvers<ContextType>;
|
||||
FiltersResult?: FiltersResultResolvers<ContextType>;
|
||||
FiltersSuccess?: FiltersSuccessResolvers<ContextType>;
|
||||
Following?: FollowingResolvers<ContextType>;
|
||||
FollowingEdge?: FollowingEdgeResolvers<ContextType>;
|
||||
FollowingError?: FollowingErrorResolvers<ContextType>;
|
||||
FollowingResult?: FollowingResultResolvers<ContextType>;
|
||||
FollowingSuccess?: FollowingSuccessResolvers<ContextType>;
|
||||
GenerateApiKeyError?: GenerateApiKeyErrorResolvers<ContextType>;
|
||||
GenerateApiKeyResult?: GenerateApiKeyResultResolvers<ContextType>;
|
||||
GenerateApiKeySuccess?: GenerateApiKeySuccessResolvers<ContextType>;
|
||||
|
|
|
|||
|
|
@ -742,46 +742,6 @@ type FiltersSuccess {
|
|||
filters: [Filter!]!
|
||||
}
|
||||
|
||||
type Following {
|
||||
author: String
|
||||
categories: [String!]
|
||||
content: String
|
||||
createdAt: Date!
|
||||
feedItemId: ID!
|
||||
hiddenAt: Date
|
||||
id: ID!
|
||||
isHidden: Boolean!
|
||||
isSaved: Boolean!
|
||||
links: [String!]!
|
||||
previewContent: String
|
||||
publishedAt: Date
|
||||
savedAt: Date
|
||||
summary: String
|
||||
title: String!
|
||||
updatedAt: Date!
|
||||
}
|
||||
|
||||
type FollowingEdge {
|
||||
cursor: String!
|
||||
node: Following!
|
||||
}
|
||||
|
||||
type FollowingError {
|
||||
errorCodes: [FollowingErrorCode!]!
|
||||
}
|
||||
|
||||
enum FollowingErrorCode {
|
||||
BAD_REQUEST
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
union FollowingResult = FollowingError | FollowingSuccess
|
||||
|
||||
type FollowingSuccess {
|
||||
edges: [FollowingEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type GenerateApiKeyError {
|
||||
errorCodes: [GenerateApiKeyErrorCode!]!
|
||||
}
|
||||
|
|
@ -1376,7 +1336,6 @@ type Query {
|
|||
deviceTokens: DeviceTokensResult!
|
||||
feeds(input: FeedsInput!): FeedsResult!
|
||||
filters: FiltersResult!
|
||||
following(after: String, first: Int, since: Date, until: Date): FollowingResult!
|
||||
getUserPersonalization: GetUserPersonalizationResult!
|
||||
groups: GroupsResult!
|
||||
hello: String
|
||||
|
|
|
|||
61
packages/api/src/resolvers/following/index.ts
Normal file
61
packages/api/src/resolvers/following/index.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import {
|
||||
FeedEdge,
|
||||
FeedsError,
|
||||
FeedsErrorCode,
|
||||
FeedsSuccess,
|
||||
QueryFeedsArgs,
|
||||
} from '../../generated/graphql'
|
||||
import { feedRepository } from '../../repository/feed'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
|
||||
export const feedsResolve = authorized<
|
||||
FeedsSuccess,
|
||||
FeedsError,
|
||||
QueryFeedsArgs
|
||||
>(async (_, { input }, { log }) => {
|
||||
try {
|
||||
const startCursor = input.after || ''
|
||||
const start =
|
||||
startCursor && !isNaN(Number(startCursor)) ? Number(startCursor) : 0
|
||||
const first = Math.min(input.first || 10, 100) // cap at 100
|
||||
|
||||
const { feeds, count } = await feedRepository.searchFeeds(
|
||||
input.query || '',
|
||||
first + 1, // fetch one extra to check if there is a next page
|
||||
start,
|
||||
input.sort?.by,
|
||||
input.sort?.order || undefined
|
||||
)
|
||||
|
||||
const hasNextPage = feeds.length > first
|
||||
const endCursor = String(start + feeds.length - (hasNextPage ? 1 : 0))
|
||||
|
||||
if (hasNextPage) {
|
||||
// remove an extra if exists
|
||||
feeds.pop()
|
||||
}
|
||||
|
||||
const edges: FeedEdge[] = feeds.map((feed) => ({
|
||||
node: feed,
|
||||
cursor: endCursor,
|
||||
}))
|
||||
|
||||
return {
|
||||
__typename: 'FeedsSuccess',
|
||||
edges,
|
||||
pageInfo: {
|
||||
hasPreviousPage: start > 0,
|
||||
hasNextPage,
|
||||
startCursor,
|
||||
endCursor,
|
||||
totalCount: count,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('Error fetching feeds', error)
|
||||
|
||||
return {
|
||||
errorCodes: [FeedsErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import express from "express";
|
||||
|
||||
export function feedRouter() {
|
||||
const router = express.Router()
|
||||
|
||||
router.post('/')
|
||||
}
|
||||
|
|
@ -2645,46 +2645,6 @@ const schema = gql`
|
|||
author: String
|
||||
}
|
||||
|
||||
union FollowingResult = FollowingSuccess | FollowingError
|
||||
|
||||
type FollowingSuccess {
|
||||
edges: [FollowingEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type FollowingEdge {
|
||||
cursor: String!
|
||||
node: Following!
|
||||
}
|
||||
|
||||
type FollowingError {
|
||||
errorCodes: [FollowingErrorCode!]!
|
||||
}
|
||||
|
||||
enum FollowingErrorCode {
|
||||
UNAUTHORIZED
|
||||
BAD_REQUEST
|
||||
}
|
||||
|
||||
type Following {
|
||||
id: ID!
|
||||
feedItemId: ID!
|
||||
title: String!
|
||||
links: [String!]!
|
||||
author: String
|
||||
summary: String
|
||||
categories: [String!]
|
||||
content: String
|
||||
previewContent: String
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
savedAt: Date
|
||||
hiddenAt: Date
|
||||
publishedAt: Date
|
||||
isHidden: Boolean!
|
||||
isSaved: Boolean!
|
||||
}
|
||||
|
||||
# Mutations
|
||||
type Mutation {
|
||||
googleLogin(input: GoogleLoginInput!): LoginResult!
|
||||
|
|
@ -2847,12 +2807,6 @@ const schema = gql`
|
|||
groups: GroupsResult!
|
||||
recentEmails: RecentEmailsResult!
|
||||
feeds(input: FeedsInput!): FeedsResult!
|
||||
following(
|
||||
after: String
|
||||
first: Int
|
||||
since: Date
|
||||
until: Date
|
||||
): FollowingResult!
|
||||
}
|
||||
`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
-- Type: DO
|
||||
-- Name: feed
|
||||
-- Description: Create feed, feed_item and user_feed_item tables
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE omnivore.feed (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
|
||||
title text NOT NULL,
|
||||
url text NOT NULL,
|
||||
author text,
|
||||
description text,
|
||||
image text,
|
||||
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
published_at timestamptz,
|
||||
UNIQUE(url)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS feed_title_idx ON omnivore.feed(title);
|
||||
|
||||
CREATE TRIGGER update_feed_modtime BEFORE UPDATE ON omnivore.feed FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE ON omnivore.feed TO omnivore_user;
|
||||
|
||||
CREATE TABLE omnivore.feed_item (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
|
||||
guid text NOT NULL,
|
||||
title text NOT NULL,
|
||||
links text[] NOT NULL,
|
||||
author text,
|
||||
summary text,
|
||||
categories text[],
|
||||
content text,
|
||||
preview_content text,
|
||||
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
published_at timestamptz,
|
||||
feed_id uuid NOT NULL REFERENCES omnivore.feed(id) ON DELETE CASCADE,
|
||||
UNIQUE(guid)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS feed_item_feed_id_idx ON omnivore.feed_item(feed_id);
|
||||
|
||||
CREATE TRIGGER update_feed_item_modtime BEFORE UPDATE ON omnivore.feed_item FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE ON omnivore.feed_item TO omnivore_user;
|
||||
|
||||
CREATE TABLE omnivore.user_feed_item (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
|
||||
user_id uuid NOT NULL REFERENCES omnivore.user(id) ON DELETE CASCADE,
|
||||
feed_item_id uuid NOT NULL REFERENCES omnivore.feed_item(id) ON DELETE CASCADE,
|
||||
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
hidden_at timestamptz,
|
||||
saved_at timestamptz
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS user_feed_item_user_id_idx ON omnivore.user_feed_item(user_id);
|
||||
CREATE INDEX IF NOT EXISTS user_feed_item_feed_item_id_idx ON omnivore.user_feed_item(feed_item_id);
|
||||
|
||||
CREATE TRIGGER update_user_feed_item_modtime BEFORE UPDATE ON omnivore.user_feed_item FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||
|
||||
ALTER TABLE omnivore.user_feed_item ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY user_feed_item_policy ON omnivore.user_feed_item
|
||||
USING (user_id = omnivore.get_current_user_id())
|
||||
WITH CHECK (user_id = omnivore.get_current_user_id());
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE ON omnivore.user_feed_item TO omnivore_user;
|
||||
|
||||
COMMIT;
|
||||
39
packages/db/migrations/0145.do.following.sql
Executable file
39
packages/db/migrations/0145.do.following.sql
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
-- Type: DO
|
||||
-- Name: following
|
||||
-- Description: Create tables for following feature
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE omnivore.subscriptions
|
||||
ADD COLUMN is_public boolean,
|
||||
ADD COLUMN is_fetching_content boolean;
|
||||
|
||||
ALTER TABLE omnivore.library_item
|
||||
ADD COLUMN hidden_at timestamptz,
|
||||
ADD COLUMN shared_at timestamptz,
|
||||
ADD COLUMN shared_by text,
|
||||
ADD COLUMN links jsonb,
|
||||
ADD COLUMN preview_content text,
|
||||
ADD COLUMN seen_at timestamptz,
|
||||
ALTER COLUMN saved_at DROP NOT NULL;
|
||||
|
||||
CREATE TABLE omnivore.feed (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
|
||||
title text NOT NULL,
|
||||
url text NOT NULL,
|
||||
author text,
|
||||
description text,
|
||||
image text,
|
||||
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||
published_at timestamptz,
|
||||
UNIQUE(url)
|
||||
);
|
||||
|
||||
CREATE INDEX feed_title_idx ON omnivore.feed(title);
|
||||
|
||||
CREATE TRIGGER update_feed_modtime BEFORE UPDATE ON omnivore.feed FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE ON omnivore.feed TO omnivore_user;
|
||||
|
||||
COMMIT;
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
-- Type: UNDO
|
||||
-- Name: feed
|
||||
-- Description: Create feed, feed_item and user_feed_item tables
|
||||
|
||||
BEGIN;
|
||||
|
||||
DROP TABLE IF EXISTS omnivore.user_feed_item;
|
||||
|
||||
DROP TABLE IF EXISTS omnivore.feed_item;
|
||||
|
||||
DROP TABLE IF EXISTS omnivore.feed;
|
||||
|
||||
COMMIT;
|
||||
22
packages/db/migrations/0145.undo.following.sql
Executable file
22
packages/db/migrations/0145.undo.following.sql
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
-- Type: UNDO
|
||||
-- Name: following
|
||||
-- Description: Create tables for following feature
|
||||
|
||||
BEGIN;
|
||||
|
||||
DROP TABLE omnivore.feed;
|
||||
|
||||
ALTER TABLE omnivore.library_item
|
||||
DROP COLUMN hidden_at,
|
||||
DROP COLUMN shared_at,
|
||||
DROP COLUMN shared_by,
|
||||
DROP COLUMN links,
|
||||
DROP COLUMN preview_content,
|
||||
DROP COLUMN seen_at,
|
||||
ALTER COLUMN saved_at SET NOT NULL;
|
||||
|
||||
ALTER TABLE omnivore.subscriptions
|
||||
DROP COLUMN is_public,
|
||||
DROP COLUMN is_fetching_content;
|
||||
|
||||
COMMIT;
|
||||
Loading…
Reference in a new issue