Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion memory_bank/schemas/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Created in: `20250307100515_add_reviews.sql`

### user_books
Created in: `20250310143716_add_user_books.sql`
Updated in: `20250310150000_update_user_books_policies.sql`, `20250310160000_add_select_policy.sql`, `20250310160001_add_source_to_user_books.sql`
Updated in: `20250310150000_update_user_books_policies.sql`, `20250310160000_add_select_policy.sql`, `20250310160001_add_source_to_user_books.sql`, `20250403000000_add_book_api_sources.sql`
- Table for tracking books in user collections
- Has security policies for user access
- Includes a source field to track where the book came from
Expand All @@ -19,6 +19,12 @@ Updated in: `20250310150000_update_user_books_policies.sql`, `20250310160000_add
Created in: `20250311143000_add_local_books.sql`
- Table for storing locally added books

### book_api_sources
Created in: `20250403000000_add_book_api_sources.sql`
- Table for storing different book API sources (e.g., Google Books API, Open Library API)
- Contains API URLs, keys, and other configuration for each source
- Referenced by user_books.source field

## Functions

- Created timestamp function (`20250310143715_create_timestamp_function.sql`)
Expand Down
78 changes: 78 additions & 0 deletions src/services/bookApiSourceService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { supabase } from '@/lib/supabase';
import { BookApiSource } from '@/types/book';

export const getBookApiSources = async (): Promise<BookApiSource[]> => {
const { data, error } = await supabase
.from('book_api_sources')
.select('*')
.order('name');

if (error) {
console.error('Error fetching book API sources:', error);
throw new Error('Failed to fetch book API sources');
}

return data || [];
};

export const getBookApiSourceByName = async (name: string): Promise<BookApiSource | null> => {
const { data, error } = await supabase
.from('book_api_sources')
.select('*')
.eq('name', name)
.single();

if (error) {
if (error.code === 'PGRST116') {
// No rows returned
return null;
}
console.error(`Error fetching book API source with name ${name}:`, error);
throw new Error(`Failed to fetch book API source with name ${name}`);
}

return data;
};

export const createBookApiSource = async (source: Omit<BookApiSource, 'id' | 'created_at' | 'updated_at'>): Promise<BookApiSource> => {
const { data, error } = await supabase
.from('book_api_sources')
.insert(source)
.select()
.single();

if (error) {
console.error('Error creating book API source:', error);
throw new Error('Failed to create book API source');
}

return data;
};

export const updateBookApiSource = async (id: string, updates: Partial<Omit<BookApiSource, 'id' | 'created_at' | 'updated_at'>>): Promise<BookApiSource> => {
const { data, error } = await supabase
.from('book_api_sources')
.update(updates)
.eq('id', id)
.select()
.single();

if (error) {
console.error(`Error updating book API source with id ${id}:`, error);
throw new Error(`Failed to update book API source with id ${id}`);
}

return data;
};

export const deleteBookApiSource = async (id: string): Promise<void> => {
const { error } = await supabase
.from('book_api_sources')
.delete()
.eq('id', id);

if (error) {
console.error(`Error deleting book API source with id ${id}:`, error);
throw new Error(`Failed to delete book API source with id ${id}`);
}
};
13 changes: 12 additions & 1 deletion src/types/book.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface Author {
books?: Book[];
}

export type BookSource = 'google' | 'libro';
export type BookSource = 'google' | 'libro' | 'openlibrary' | string;

export interface Book {
source: BookSource;
Expand Down Expand Up @@ -34,6 +34,17 @@ export interface BookReview {
created_at: string;
}

export interface BookApiSource {
id: string;
name: string;
api_url: string;
api_key?: string;
description?: string;
is_active: boolean;
created_at: string;
updated_at: string;
}

export interface GoogleBooksResponse {
items: {
id: string;
Expand Down
40 changes: 40 additions & 0 deletions supabase/migrations/20250403000000_add_book_api_sources.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-- Create book_api_sources table
CREATE TABLE book_api_sources (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL UNIQUE,
api_url TEXT NOT NULL,
api_key TEXT,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Enable RLS
ALTER TABLE book_api_sources ENABLE ROW LEVEL SECURITY;

-- Create policies
CREATE POLICY "Book API sources are viewable by everyone" ON book_api_sources FOR
SELECT USING (true);

CREATE POLICY "Only authenticated users can manage book API sources" ON book_api_sources FOR ALL USING (auth.role() = 'authenticated');

-- Create trigger for updated_at
CREATE TRIGGER set_book_api_sources_timestamp BEFORE
UPDATE ON book_api_sources FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamp();

-- Insert default sources
INSERT INTO book_api_sources (name, api_url, description) VALUES
('google', 'https://www.googleapis.com/books/v1', 'Google Books API'),
('openlibrary', 'https://openlibrary.org/api', 'Open Library API');

-- Drop the check constraint on user_books.source
ALTER TABLE user_books DROP CONSTRAINT valid_source;

-- Add a new constraint that references the book_api_sources table
ALTER TABLE user_books ADD CONSTRAINT valid_source CHECK (
source IN (SELECT name FROM book_api_sources) OR source = 'libro'
);

-- Add comment to explain the constraint
COMMENT ON CONSTRAINT valid_source ON user_books IS 'Source must be "libro" or a name from book_api_sources table';
Loading