diff --git a/CRUSH.md b/CRUSH.md new file mode 100644 index 0000000..9414bdc --- /dev/null +++ b/CRUSH.md @@ -0,0 +1,51 @@ +# Aperonight - CRUSH Development Guidelines + +## Build Commands +- `bin/rails server` - Start development server +- `bin/rails assets:precompile` - Compile assets +- `npm run build` - Build JavaScript bundle (production) +- `npm run build:dev` - Build JavaScript bundle (development) +- `npm run build:css` - Compile CSS with PostCSS/Tailwind + +## Test Commands +- `bin/rails test` - Run all tests +- `bin/rails test test/models/user_test.rb` - Run specific test file +- `bin/rails test test/models/user_test.rb:15` - Run specific test method +- `bin/rails test:system` - Run system tests + +## Lint Commands +- `bin/rubocop` - Run Ruby linter +- `bin/rubocop -a` - Run Ruby linter with auto-fix +- Check JS/JSX files manually (no configured linter) + +## Development Workflow +1. Branch naming: `type/descriptive-name` (e.g., `feature/user-profile`) +2. Follow Git Flow with `main` and `develop` branches +3. Run tests and linters before committing +4. Keep PRs focused on single features/fixes + +## Code Style Guidelines + +### Ruby +- Follow Rubocop Rails Omakase defaults +- Standard Rails MVC conventions +- Use descriptive method and variable names +- Prefer single quotes for strings without interpolation + +### JavaScript/React +- Use Stimulus controllers for DOM interactions +- React components in PascalCase (`UserProfile.jsx`) +- Shadcn components in kebab-case (`button.jsx`) but exported as PascalCase +- Functional components with hooks over class components + +### CSS/Tailwind +- Mobile-first responsive design +- Use Tailwind utility classes over custom CSS +- Primary color palette: indigo → purple → pink gradients +- Consistent spacing with Tailwind's spacing scale + +### General +- Keep functions small and focused +- Comment complex logic +- Use descriptive commit messages +- Maintain consistency with existing code patterns \ No newline at end of file diff --git a/app/controllers/api/v1/parties_controller.rb b/app/controllers/api/v1/parties_controller.rb new file mode 100644 index 0000000..9cd4d88 --- /dev/null +++ b/app/controllers/api/v1/parties_controller.rb @@ -0,0 +1,82 @@ +# API controller for managing party resources +# Provides RESTful endpoints for CRUD operations on Party model +module Api + module V1 + class PartiesController < ApiController + # Load party before specific actions to reduce duplication + before_action :set_party, only: [:show, :update, :destroy] + + # GET /api/v1/parties + # Returns all parties sorted by creation date (newest first) + def index + @parties = Party.all.order(created_at: :desc) + render json: @parties, status: :ok + end + + # GET /api/v1/parties/:id + # Returns a single party by ID + # Returns 404 if party is not found + def show + render json: @party, status: :ok + end + + # POST /api/v1/parties + # Creates a new party with provided attributes + # Returns 201 Created on success with party data + # Returns 422 Unprocessable Entity with validation errors on failure + def create + @party = Party.new(party_params) + if @party.save + render json: @party, status: :created + else + render json: { errors: @party.errors.full_messages }, status: :unprocessable_entity + end + end + + # PATCH/PUT /api/v1/parties/:id + # Updates an existing party with provided attributes + # Returns 200 OK with updated party data on success + # Returns 422 Unprocessable Entity with validation errors on failure + def update + if @party.update(party_params) + render json: @party, status: :ok + else + render json: { errors: @party.errors.full_messages }, status: :unprocessable_entity + end + end + + # DELETE /api/v1/parties/:id + # Permanently deletes a party + # Returns 204 No Content on success + def destroy + @party.destroy + head :no_content + end + + private + + # Finds a party by ID or returns 404 Not Found + # Used as before_action for show, update, and destroy actions + def set_party + @party = Party.find(params[:id]) + rescue ActiveRecord::RecordNotFound + render json: { error: "Party not found" }, status: :not_found + end + + # Strong parameters for party creation and updates + # Whitelists permitted attributes to prevent mass assignment vulnerabilities + def party_params + params.require(:party).permit( + :name, + :description, + :state, + :venue_name, + :venue_address, + :latitude, + :longitude, + :featured + ) + end + end + end +end diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb new file mode 100644 index 0000000..e5feebe --- /dev/null +++ b/app/controllers/api_controller.rb @@ -0,0 +1,24 @@ +# Base controller for API endpoints +# Provides authentication and common functionality for API controllers +class ApiController < ApplicationController + # Disable CSRF protection for API requests (token-based authentication instead) + protect_from_forgery with: :null_session + + # Authenticate all API requests using API key + # Must be called before any API action + before_action :authenticate_api_key + + private + + # Authenticates API requests using X-API-Key header or api_key parameter + # Returns 401 Unauthorized if key is invalid or missing + def authenticate_api_key + # Extract API key from header or query parameter + api_key = request.headers["X-API-Key"] || params[:api_key] + + # Validate against hardcoded key (in production, use environment variable) + unless api_key == "aperonight-api-key-2025" + render json: { error: "Unauthorized" }, status: :unauthorized + end + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0d95db2..bae1930 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,16 @@ +# Base controller for the application +# Provides common functionality and security configurations for all controllers class ApplicationController < ActionController::Base - # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. + # Protect against Cross-Site Request Forgery (CSRF) attacks + # Ensures that all non-GET requests include a valid authenticity token + protect_from_forgery with: :exception + + # Restrict access to modern browsers only + # Requires browsers to support modern web standards: + # - WebP images for better compression + # - Web Push notifications + # - Badge API for notifications + # - Import maps for JavaScript modules + # - CSS nesting and :has() pseudo-class allow_browser versions: :modern end diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index db9cd1f..69eebd1 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -1,9 +1,12 @@ +# Controller for static pages and user dashboard +# Handles basic page rendering and user-specific content class PagesController < ApplicationController - # Display homepage - def home - end + # Require user authentication for dashboard access + # Redirects to login page if user is not signed in + before_action :authenticate_user!, only: [:dashboard] - # Display legal page - def legals + # User dashboard showing personalized content + # Accessible only to authenticated users + def dashboard end end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index b63caeb..adb3919 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,3 +1,6 @@ +# Base class for all ActiveRecord models in the application +# Provides common functionality and configuration for all models class ApplicationRecord < ActiveRecord::Base + # Mark this as the primary abstract class to establish inheritance hierarchy primary_abstract_class end diff --git a/app/models/party.rb b/app/models/party.rb new file mode 100644 index 0000000..3504d7d --- /dev/null +++ b/app/models/party.rb @@ -0,0 +1,40 @@ +# Party model representing nightlife events and parties +# Manages event details, location data, and publication state +class Party < ApplicationRecord + # Define states for party lifecycle management + # draft: Initial state when party is being created + # published: Party is visible to public and can be discovered + # canceled: Party has been canceled by organizer + # sold_out: Party has reached capacity and tickets are no longer available + enum state: { + draft: 0, + published: 1, + canceled: 2, + sold_out: 3 + }, default: :draft + + # Validations for party attributes + # Basic information + validates :name, presence: true, length: { minimum: 3, maximum: 100 } + validates :description, presence: true, length: { minimum: 10, maximum: 1000 } + validates :state, presence: true, inclusion: { in: states.keys } + + # Venue information + validates :venue_name, presence: true, length: { maximum: 100 } + validates :venue_address, presence: true, length: { maximum: 200 } + + # Geographic coordinates for map display + validates :latitude, presence: true, numericality: { + greater_than_or_equal_to: -90, + less_than_or_equal_to: 90 + } + validates :longitude, presence: true, numericality: { + greater_than_or_equal_to: -180, + less_than_or_equal_to: 180 + } + + # Scopes for querying parties with common filters + scope :featured, -> { where(featured: true) } # Get featured parties for homepage + scope :published, -> { where(state: :published) } # Get publicly visible parties + scope :search_by_name, ->(query) { where("name ILIKE ?", "%#{query}%") } # Search by name (case-insensitive) +end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index 4756799..7e3564f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,21 @@ +# User model for authentication and user management +# Handles user accounts, authentication, and authorization using Devise class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable + # + # Include default devise modules for authentication + # :database_authenticatable - encrypts and stores password in database + # :registerable - allows users to sign up and edit their accounts + # :recoverable - handles password reset functionality + # :rememberable - manages token-based user remembering + # :validatable - provides email and password validation + # Other available modules are: + # :confirmable - requires email confirmation + # :lockable - locks account after failed login attempts + # :timeoutable - expires sessions after inactivity + # :trackable - tracks sign-in count, timestamps, and IP + # :omniauthable - allows authentication via OAuth providers devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable end diff --git a/config/routes.rb b/config/routes.rb index 4e9e5ef..c65ec18 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,4 +31,15 @@ Rails.application.routes.draw do confirmation: "authentications/confirmations" # Custom controller for confirmations } + # API routes versioning + namespace :api do + namespace :v1 do + # RESTful routes for party management + resources :parties, only: [ :index, :show, :create, :update, :destroy ] + + # Additional API endpoints can be added here as needed + # Example: search, filtering, user-specific endpoints + end + end + end diff --git a/db/migrate/20250823145902_create_parties.rb b/db/migrate/20250823145902_create_parties.rb new file mode 100644 index 0000000..5d2de99 --- /dev/null +++ b/db/migrate/20250823145902_create_parties.rb @@ -0,0 +1,19 @@ +class CreateParties < ActiveRecord::Migration[8.0] + def change + create_table :parties do |t| + t.string :name, null: false + t.text :description, null: false + t.integer :state, default: 0, null: false + t.string :venue_name, null: false + t.string :venue_address, null: false + t.decimal :latitude, precision: 10, scale: 6, null: false + t.decimal :longitude, precision: 10, scale: 6, null: false + t.boolean :featured, default: false, null: false + t.timestamps + end + + add_index :parties, :state + add_index :parties, :featured + add_index :parties, [:latitude, :longitude] + end +end \ No newline at end of file