Add initial project structure with core functionality for book and stationery uploads

- Created main application entry point in `main.go`.
- Added configuration management in `config/config.go` and tests in `config/config_test.go`.
- Implemented handlers for book and stationery uploads in `handlers/book.go` and `handlers/stationery.go`, including validation logic.
- Established database connection and services in `services/database.go` and `services/book_service.go`.
- Defined models for books and stationery in `models/book.go` and `models/stationery.go`.
- Set up Firebase integration for image uploads in `services/firebase.go`.
- Created migration scripts for database schema in `migrations/001_create_tables.sql` and subsequent updates.
- Added CORS and error handling middleware.
- Included comprehensive tests for handlers, services, and utilities.
- Documented API endpoints and usage in `README.md` and migration instructions in `migrations/README.md`.
- Introduced `.gitignore` to exclude unnecessary files and directories.
- Added Go module support with `go.mod` and `go.sum` files.
- Implemented utility functions for slug generation and validation in `utils/slug.go` and `utils/validation.go`.
This commit is contained in:
ianshaloom
2025-11-21 08:50:27 +03:00
commit ebeae34e01
39 changed files with 4044 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
-- Create books table
CREATE TABLE IF NOT EXISTS books (
book_code UUID PRIMARY KEY DEFAULT gen_random_uuid(),
book_name VARCHAR(200) NOT NULL,
cost DECIMAL(10,2) NOT NULL,
price DECIMAL(10,2) NOT NULL,
discount DECIMAL(10,2) DEFAULT 0,
quantity INTEGER NOT NULL,
publisher_author VARCHAR(200) NOT NULL,
category VARCHAR(100) NOT NULL,
description TEXT,
image_url TEXT NOT NULL,
slug VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Create indexes for books table
CREATE INDEX IF NOT EXISTS idx_books_created_at ON books(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_books_category ON books(category);
CREATE INDEX IF NOT EXISTS idx_books_slug ON books(slug);
-- Create stationery table
CREATE TABLE IF NOT EXISTS stationery (
stationery_code UUID PRIMARY KEY DEFAULT gen_random_uuid(),
stationery_name VARCHAR(200) NOT NULL,
cost DECIMAL(10,2) NOT NULL,
price DECIMAL(10,2) NOT NULL,
discount DECIMAL(10,2) DEFAULT 0,
quantity INTEGER NOT NULL,
color VARCHAR(100),
material VARCHAR(100),
dimensions VARCHAR(100),
category VARCHAR(100) NOT NULL,
description TEXT,
image_url TEXT NOT NULL,
slug VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Create indexes for stationery table
CREATE INDEX IF NOT EXISTS idx_stationery_created_at ON stationery(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_stationery_category ON stationery(category);
CREATE INDEX IF NOT EXISTS idx_stationery_slug ON stationery(slug);

View File

@@ -0,0 +1,25 @@
-- Drop the old books table if it exists with wrong schema
DROP TABLE IF EXISTS books CASCADE;
-- Recreate books table with correct schema
CREATE TABLE books (
book_code UUID PRIMARY KEY DEFAULT gen_random_uuid(),
book_name VARCHAR(200) NOT NULL,
cost DECIMAL(10,2) NOT NULL,
price DECIMAL(10,2) NOT NULL,
discount DECIMAL(10,2) DEFAULT 0,
quantity INTEGER NOT NULL,
publisher_author VARCHAR(200) NOT NULL,
category VARCHAR(100) NOT NULL,
description TEXT,
image_url TEXT NOT NULL,
slug VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Create indexes for books table
CREATE INDEX idx_books_created_at ON books(created_at DESC);
CREATE INDEX idx_books_category ON books(category);
CREATE INDEX idx_books_slug ON books(slug);

View File

@@ -0,0 +1,43 @@
-- Add UNIQUE constraint to slug columns in both books and stationery tables
-- This prevents duplicate slugs and enables slug-based image filename uniqueness
-- First, handle any existing duplicate slugs by appending a suffix
-- For books
UPDATE books b1
SET slug = slug || '-' || book_code::text
WHERE EXISTS (
SELECT 1 FROM books b2
WHERE b2.slug = b1.slug
AND b2.book_code < b1.book_code
);
-- For stationery
UPDATE stationery s1
SET slug = slug || '-' || stationery_code::text
WHERE EXISTS (
SELECT 1 FROM stationery s2
WHERE s2.slug = s1.slug
AND s2.stationery_code < s1.stationery_code
);
-- Add unique constraint to books.slug (if not exists)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'books_slug_unique'
) THEN
ALTER TABLE books ADD CONSTRAINT books_slug_unique UNIQUE (slug);
END IF;
END $$;
-- Add unique constraint to stationery.slug (if not exists)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'stationery_slug_unique'
) THEN
ALTER TABLE stationery ADD CONSTRAINT stationery_slug_unique UNIQUE (slug);
END IF;
END $$;

41
migrations/README.md Normal file
View File

@@ -0,0 +1,41 @@
# Database Migrations
## Running Migrations
### Prerequisites
1. Ensure PostgreSQL is running
2. Create the database if it doesn't exist:
```bash
psql -U postgres -c "CREATE DATABASE jd_book_uploader;"
```
3. Ensure your `.env` or `.env.local` file in the backend directory has the correct database credentials
### Run Migration
```bash
cd backend
./scripts/run_migration.sh
```
Or manually:
```bash
cd backend
export $(grep -v '^#' .env.local | grep -v '^$' | xargs)
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f migrations/001_create_tables.sql
```
### Verify Schema
```bash
cd backend
./scripts/verify_schema.sh
```
This will show:
- Table structures for `books` and `stationery`
- All indexes created on both tables
## Migration Files
- `001_create_tables.sql` - Creates books and stationery tables with indexes