mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-18 13:34:08 +01:00
chore: rewrite compose and add manifest file, markdown
This commit is contained in:
parent
1b819e4ed0
commit
00700af27f
93
README.md
93
README.md
@ -1,10 +1,91 @@
|
|||||||
# ForFarm
|
# ForFarm
|
||||||
A smart farming software uses AI, weather data, and analytics to help farmers make better decisions and improve productivity. It empowers farmers and agribusinesses to make data-driven decisions and enhance productivity through integrated technological tools.
|
|
||||||
|
|
||||||
## Installation
|
A farming software designed to empower farmers and agribusinesses by AI, weather data, and analytics. ForFarm helps users make data-driven decisions, and optimize resource management through a tools.
|
||||||
|
|
||||||
### For development
|
## Features
|
||||||
|
|
||||||
```zsh
|
- **Farm & Crop Management:** Define farms, map croplands (points, polygons), track crop lifecycles, status, and growth stages.
|
||||||
docker compose up
|
- **Inventory Tracking:** Manage farm resources like seeds, fertilizers, equipment, etc.
|
||||||
```
|
- **Data Analytics:** Visualize farm and crop performance metrics. (Integration with weather, soil data planned).
|
||||||
|
- **Knowledge Hub:** Access articles and guides on farming best practices.
|
||||||
|
- **AI Chatbot:** Get contextual assistance based on your farm/crop data or ask general farming questions.
|
||||||
|
- **Weather Integration:** (Implemented via Worker) Fetches current weather data for farms.
|
||||||
|
- **User Authentication:** Secure login/registration using email/password and Google OAuth.
|
||||||
|
- **Marketplace Insights:** (Mock Data) Provides simulated market price trends and analysis.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
The project is organized into two main components:
|
||||||
|
|
||||||
|
```
|
||||||
|
├── backend/ # Go backend application (API, business logic, data access)
|
||||||
|
│ ├── cmd/ # Main entry points (API server, CLI commands)
|
||||||
|
│ ├── internal/ # Private application code
|
||||||
|
│ │ ├── api/ # API handlers and routing (Huma framework)
|
||||||
|
│ │ ├── cache/ # Caching interface and implementations
|
||||||
|
│ │ ├── config/ # Configuration loading (Viper)
|
||||||
|
│ │ ├── domain/ # Core business entities and repository interfaces
|
||||||
|
│ │ ├── event/ # Event bus and projection logic
|
||||||
|
│ │ ├── middlewares/ # HTTP middlewares (Auth, Rate Limiting)
|
||||||
|
│ │ ├── repository/ # Data access layer implementations (Postgres)
|
||||||
|
│ │ ├── services/ # Business logic services (Chat, Weather, Analytics)
|
||||||
|
│ │ └── workers/ # Background worker processes (Weather Updater)
|
||||||
|
│ ├── migrations/ # Database schema migrations (Goose)
|
||||||
|
│ ├── scripts/ # Utility scripts (Seeding)
|
||||||
|
│ ├── .air.toml # Live reload config for backend dev
|
||||||
|
│ ├── go.mod # Go module dependencies
|
||||||
|
│ └── go.dockerfile # Dockerfile for backend
|
||||||
|
├── frontend/ # Next.js frontend application (UI)
|
||||||
|
│ ├── app/ # Next.js App Router structure (pages, layouts)
|
||||||
|
│ ├── api/ # Frontend API client functions for backend interaction
|
||||||
|
│ ├── components/ # Reusable UI components (shadcn/ui)
|
||||||
|
│ ├── context/ # React context providers (Session)
|
||||||
|
│ ├── hooks/ # Custom React hooks
|
||||||
|
│ ├── lib/ # Utility functions, providers
|
||||||
|
│ ├── schemas/ # Zod validation schemas for forms
|
||||||
|
│ ├── types.ts # TypeScript type definitions
|
||||||
|
│ ├── next.config.ts # Next.js configuration
|
||||||
|
│ └── next.dockerfile # Dockerfile for frontend
|
||||||
|
├── docker-compose.yml # Docker Compose configuration for services
|
||||||
|
├── .env.example # Example environment variables file
|
||||||
|
├── README.md # This file
|
||||||
|
└── SETUP.md # Detailed development setup guide
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Backend:** Built with Go, using Chi for routing, Huma for API definition, pgx for PostgreSQL interaction, and Cobra for CLI commands. It handles business logic, data persistence, and external service integrations.
|
||||||
|
- **Frontend:** Built with Next.js (App Router) and TypeScript, using Tailwind CSS and shadcn/ui for styling and components. It provides the user interface and interacts with the backend API.
|
||||||
|
|
||||||
|
## Installation & Setup
|
||||||
|
|
||||||
|
For detailed setup instructions, please refer to the **[SETUP.md](SETUP.md)** guide.
|
||||||
|
|
||||||
|
The basic steps are:
|
||||||
|
|
||||||
|
1. **Prerequisites:** Ensure Docker, Docker Compose, Go, Node.js, and pnpm are installed.
|
||||||
|
2. **Clone:** Clone this repository.
|
||||||
|
3. **Configure:** Create a `.env` file from `.env.example` and fill in necessary secrets and keys (Database, JWT, Google OAuth, Maps API, Weather API, Gemini API).
|
||||||
|
4. **Run:** Start all services using `docker compose up --build -d`.
|
||||||
|
5. **Migrate:** Run database migrations: `cd backend && make migrate && cd ..`.
|
||||||
|
6. **Seed (Optional):** Populate static data: `cd backend && make seed && cd ..`.
|
||||||
|
7. **Access:** Open [http://localhost:3000](http://localhost:3000) in your browser.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Once set up and running:
|
||||||
|
|
||||||
|
1. Navigate to [http://localhost:3000](http://localhost:3000).
|
||||||
|
2. Register a new account or log in.
|
||||||
|
3. Explore the dashboard:
|
||||||
|
- Add and manage your farms.
|
||||||
|
- Add croplands within your farms, drawing boundaries or placing markers.
|
||||||
|
- Manage your inventory items.
|
||||||
|
- Consult the AI Chatbot for farming advice.
|
||||||
|
- Browse the Knowledge Hub articles.
|
||||||
|
- View (simulated) Marketplace data.
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
- [Natthapol Sermsaran](https://github.com/xNatthapol)
|
||||||
|
- [Sirin Puenggun](https://github.com/Sosokker/)
|
||||||
|
- [Pattadon Loyprasert](https://github.com/GGWPXXXX)
|
||||||
|
- [Buravit Yenjit](https://github.com/KikyoBRV)
|
||||||
|
|||||||
283
SETUP.md
Normal file
283
SETUP.md
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
# ForFarm Project Setup Guide
|
||||||
|
|
||||||
|
This guide provides instructions for setting up and running the ForFarm project using different methods.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Ensure you have the following tools installed:
|
||||||
|
|
||||||
|
- **Go:** Version 1.23 or later (see `backend/go.mod`).
|
||||||
|
- **Node.js:** Version 20 or later (see `frontend/next.dockerfile`).
|
||||||
|
- **pnpm:** Node package manager (`npm install -g pnpm`).
|
||||||
|
- **Docker:** Latest version.
|
||||||
|
- **Docker Compose:** Latest version (often included with Docker Desktop).
|
||||||
|
- **kubectl:** Kubernetes command-line tool.
|
||||||
|
- **gcloud:** Google Cloud SDK (if deploying to GKE).
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Environment variables are used for configuration.
|
||||||
|
|
||||||
|
1. **Backend:**
|
||||||
|
|
||||||
|
- Copy `backend/sample.env` to `backend/.env`.
|
||||||
|
- Fill in the required values in `backend/.env`:
|
||||||
|
- `DATABASE_URL`: Connection string for your PostgreSQL database. (e.g., `postgres://postgres:@Password123@localhost:5433/postgres?sslmode=disable` for local Docker Compose setup).
|
||||||
|
- `RABBITMQ_URL`: Connection string for RabbitMQ (e.g., `amqp://user:password@localhost:5672/` for local Docker Compose).
|
||||||
|
- `JWT_SECRET_KEY`: A strong, random secret key (at least 32 characters).
|
||||||
|
- `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`: For Google OAuth.
|
||||||
|
- `OPENWEATHER_API_KEY`: Your OpenWeatherMap API key.
|
||||||
|
- `GEMINI_API_KEY`: Your Google AI Gemini API key.
|
||||||
|
- `GCS_BUCKET_NAME`: Your Google Cloud Storage bucket name.
|
||||||
|
- `GCS_SERVICE_ACCOUNT_KEY_PATH`: (Optional) Path to your GCS service account key JSON file if _not_ using Application Default Credentials (ADC). Leave empty if using ADC (recommended for GKE with Workload Identity).
|
||||||
|
|
||||||
|
2. **Frontend:**
|
||||||
|
- Copy `frontend/sample.env` to `frontend/.env`.
|
||||||
|
- Fill in the required values in `frontend/.env`:
|
||||||
|
- `NEXT_PUBLIC_BACKEND_URL`: URL of the running backend API (e.g., `http://localhost:8000` for local/Compose).
|
||||||
|
- `NEXT_PUBLIC_GOOGLE_CLIENT_ID`: Your Google Client ID for OAuth on the frontend.
|
||||||
|
- `NEXT_PUBLIC_GOOGLE_MAPS_API_KEY`: Your Google Maps API Key.
|
||||||
|
- (Other `NEXTAUTH_*` variables might be needed if you integrate `next-auth` fully).
|
||||||
|
|
||||||
|
## Running Locally (Manual Setup)
|
||||||
|
|
||||||
|
This method requires running services like Postgres and RabbitMQ separately.
|
||||||
|
|
||||||
|
1. **Start Database:** Run PostgreSQL (e.g., using Docker: `docker run --name some-postgres -e POSTGRES_PASSWORD=yourpassword -p 5432:5432 -d postgres:16-alpine`). Ensure the `DATABASE_URL` in `backend/.env` points to it.
|
||||||
|
2. **Start RabbitMQ:** Run RabbitMQ (e.g., using Docker: `docker run --name some-rabbit -p 5672:5672 -p 15672:15672 -d rabbitmq:3-management-alpine`). Ensure `RABBITMQ_URL` in `backend/.env` points to it.
|
||||||
|
3. **Backend Migrations:**
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
go run cmd/forfarm/main.go migrate
|
||||||
|
```
|
||||||
|
4. **Run Backend API:**
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
# For live reloading (requires air - go install github.com/cosmtrek/air@latest)
|
||||||
|
# air
|
||||||
|
# Or run directly
|
||||||
|
go run cmd/forfarm/main.go api
|
||||||
|
```
|
||||||
|
The backend should be running on `http://localhost:8000`.
|
||||||
|
5. **Run Frontend:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
pnpm install
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
The frontend should be running on `http://localhost:3000`.
|
||||||
|
|
||||||
|
6. (Optional) Add dummy data in /backend/dummy directory to database
|
||||||
|
|
||||||
|
- Do it manually or `make seed`
|
||||||
|
|
||||||
|
## Installation Steps (In detailed)
|
||||||
|
|
||||||
|
1. **Clone the Repository:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/your-username/ForFarm.git # Replace with your repo URL
|
||||||
|
cd ForFarm
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Environment Variables:**
|
||||||
|
|
||||||
|
- Copy the example environment file:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
- **Edit the `.env` file:** Fill in the required values, especially for:
|
||||||
|
- `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` (you can keep defaults for local setup)
|
||||||
|
- `JWT_SECRET_KEY` (generate a strong, random secret)
|
||||||
|
- `GOOGLE_CLIENT_ID` (if using Google OAuth)
|
||||||
|
- `NEXT_PUBLIC_GOOGLE_CLIENT_ID` (Frontend Google Client ID)
|
||||||
|
- `NEXT_PUBLIC_GOOGLE_MAPS_API_KEY` (Required for maps)
|
||||||
|
- `OPENWEATHER_API_KEY` (Required for weather features)
|
||||||
|
- `GEMINI_API_KEY` (Required for AI chatbot features)
|
||||||
|
- `RABBITMQ_URL` (Keep default if using the docker-compose setup)
|
||||||
|
- (Optionally adjust `RATE_LIMIT_*` variables)
|
||||||
|
|
||||||
|
3. **Build and Run Services:**
|
||||||
|
Use Docker Compose to build the images and start the backend, frontend, and database containers.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up --build -d
|
||||||
|
```
|
||||||
|
|
||||||
|
- `--build`: Forces Docker to rebuild images if Dockerfiles have changed.
|
||||||
|
- `-d`: Runs containers in detached mode (in the background).
|
||||||
|
|
||||||
|
4. **Run Backend Database Migrations:**
|
||||||
|
Apply the necessary database schema changes. Open a **new terminal** in the project root and navigate to the backend directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
make migrate
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
- This command uses Go to connect to the database (running in Docker) and applies migrations located in `backend/migrations`.
|
||||||
|
|
||||||
|
5. **Install Frontend Dependencies:**
|
||||||
|
Navigate to the frontend directory and install its dependencies using pnpm.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
pnpm install
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
_(Docker Compose might handle this during build if configured in `next.dockerfile`, but running it explicitly ensures dependencies are up-to-date)_
|
||||||
|
|
||||||
|
6. **Access the Application:**
|
||||||
|
|
||||||
|
- **Frontend:** Open your browser and navigate to [http://localhost:3000](http://localhost:3000) (or the port specified by `FRONTEND_PORT` in your `.env`).
|
||||||
|
- **Backend API:** The API is accessible at [http://localhost:8000](http://localhost:8000) (or the port specified by `BACKEND_PORT`). You can use tools like Postman or `curl` to interact with it.
|
||||||
|
|
||||||
|
7. (Optional) Add dummy data in /backend/dummy directory to database
|
||||||
|
|
||||||
|
- Do it manually or `make seed`
|
||||||
|
|
||||||
|
## Running with Docker Compose
|
||||||
|
|
||||||
|
This is the recommended way for local development and testing the containerized setup.
|
||||||
|
|
||||||
|
1. **Ensure `.env` files are configured** as described in the Configuration section. Use `localhost` for hostnames in URLs (e.g., `DATABASE_URL='postgres://postgres:@Password123@db:5432/postgres?sslmode=disable'`, `RABBITMQ_URL=amqp://user:password@rabbitmq:5672/` - note the service names `db` and `rabbitmq`).
|
||||||
|
2. **Build and Start:**
|
||||||
|
```bash
|
||||||
|
docker compose up --build -d # -d runs in detached mode
|
||||||
|
```
|
||||||
|
3. **Run Migrations (First time or after changes):**
|
||||||
|
```bash
|
||||||
|
docker compose exec backend /app/api migrate
|
||||||
|
# Or if using source mount and go is available:
|
||||||
|
# docker compose exec backend go run cmd/forfarm/main.go migrate
|
||||||
|
```
|
||||||
|
4. **Access Services:**
|
||||||
|
- Frontend: `http://localhost:3000`
|
||||||
|
- Backend API: `http://localhost:8000`
|
||||||
|
- RabbitMQ Management: `http://localhost:15672` (user/password from `.env`)
|
||||||
|
- Database: Connect via `localhost:5433` using credentials from `.env`.
|
||||||
|
5. **View Logs:** `docker compose logs -f [service_name]` (e.g., `docker compose logs -f backend`)
|
||||||
|
6. **Stop:** `docker compose down`
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
- **Live Reload (Backend):** While `docker compose up -d` keeps the backend running, for active Go development with live reload, stop the backend service (`docker compose stop backend`) and run:
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
make live
|
||||||
|
```
|
||||||
|
This uses `air` (configured in `.air.toml`) to automatically rebuild and restart the Go application when code changes.
|
||||||
|
- **Live Reload (Frontend):** The `pnpm dev` command used in the frontend Dockerfile typically includes hot module replacement (HMR). Changes made to frontend code should reflect in the browser automatically when running `docker compose up`. If not, check the Next.js configuration.
|
||||||
|
|
||||||
|
## Deploying to Google Kubernetes Engine (GKE)
|
||||||
|
|
||||||
|
This requires a configured GKE cluster and `gcloud` CLI authenticated.
|
||||||
|
|
||||||
|
1. **Prerequisites:**
|
||||||
|
|
||||||
|
- Create a GKE cluster.
|
||||||
|
- Configure `kubectl` to connect to your cluster (`gcloud container clusters get-credentials YOUR_CLUSTER_NAME --zone YOUR_ZONE --project YOUR_PROJECT_ID`).
|
||||||
|
- Enable Google Container Registry (GCR) or Artifact Registry API.
|
||||||
|
|
||||||
|
2. **Configure GCS:**
|
||||||
|
|
||||||
|
- Create a GCS bucket (`YOUR_GCS_BUCKET_NAME`).
|
||||||
|
- **Authentication:**
|
||||||
|
- **(Recommended) Workload Identity:** Set up Workload Identity to grant your Kubernetes Service Account permissions to access the GCS bucket without key files. This involves creating a GCP Service Account, granting it `roles/storage.objectAdmin` on the bucket, creating a K8s Service Account (e.g., `backend-sa`), and binding them. Update `backend-deployment.yaml` to use `serviceAccountName: backend-sa`.
|
||||||
|
- **(Alternative) Service Account Key:** Create a GCP Service Account, grant it permissions, download its JSON key file.
|
||||||
|
|
||||||
|
3. **Build and Push Docker Images:**
|
||||||
|
|
||||||
|
- Authenticate Docker with GCR/Artifact Registry (`gcloud auth configure-docker YOUR_REGION-docker.pkg.dev`).
|
||||||
|
- Build the images:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend
|
||||||
|
docker build -t YOUR_REGION-docker.pkg.dev/YOUR_GCR_PROJECT_ID/forfarm/backend:latest -f backend/go.dockerfile ./backend
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
docker build -t YOUR_REGION-docker.pkg.dev/YOUR_GCR_PROJECT_ID/forfarm/frontend:latest -f frontend/next.dockerfile ./frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
- Push the images:
|
||||||
|
```bash
|
||||||
|
docker push YOUR_REGION-docker.pkg.dev/YOUR_GCR_PROJECT_ID/forfarm/backend:latest
|
||||||
|
docker push YOUR_REGION-docker.pkg.dev/YOUR_GCR_PROJECT_ID/forfarm/frontend:latest
|
||||||
|
```
|
||||||
|
- **Update `k8s/*.yaml` files:** Replace `YOUR_GCR_PROJECT_ID/forfarm-backend:latest` and `YOUR_GCR_PROJECT_ID/forfarm-frontend:latest` with your actual image paths.
|
||||||
|
|
||||||
|
4. **Create Kubernetes Secrets:**
|
||||||
|
|
||||||
|
- **Encode Secrets:** Base64 encode all values needed in `k8s/secrets.yaml`.
|
||||||
|
```bash
|
||||||
|
echo -n "your_password" | base64
|
||||||
|
# For GCS key file (if using):
|
||||||
|
cat path/to/your-gcs-key.json | base64 | tr -d '\n' # Ensure no newlines in output
|
||||||
|
```
|
||||||
|
- **Update `k8s/secrets.yaml`:** Paste the base64 encoded values into the `data` section.
|
||||||
|
- **Apply Secrets:**
|
||||||
|
```bash
|
||||||
|
kubectl apply -f k8s/namespace.yaml
|
||||||
|
kubectl apply -f k8s/secrets.yaml -n forfarm
|
||||||
|
```
|
||||||
|
Alternatively, create secrets imperatively (safer as values aren't stored in YAML):
|
||||||
|
```bash
|
||||||
|
kubectl create secret generic forfarm-secrets -n forfarm \
|
||||||
|
--from-literal=POSTGRES_PASSWORD='your_db_password' \
|
||||||
|
--from-literal=RABBITMQ_PASSWORD='your_rabbit_password' \
|
||||||
|
# ... add other secrets ...
|
||||||
|
# If using key file:
|
||||||
|
# --from-file=GCS_SERVICE_ACCOUNT_KEY_JSON=/path/to/your-gcs-key.json
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Create ConfigMap:**
|
||||||
|
|
||||||
|
- **Update `k8s/configmap.yaml`:** Replace placeholders like `YOUR_GOOGLE_CLIENT_ID`, `YOUR_GOOGLE_MAPS_API_KEY`, `YOUR_GCS_BUCKET_NAME`. Adjust service URLs if needed.
|
||||||
|
- **Apply ConfigMap:**
|
||||||
|
```bash
|
||||||
|
kubectl apply -f k8s/configmap.yaml -n forfarm
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Apply Deployments, Services, PVCs:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Apply database and message queue first
|
||||||
|
kubectl apply -f k8s/postgres-pvc.yaml -n forfarm # Only if using self-hosted postgres
|
||||||
|
kubectl apply -f k8s/postgres-deployment.yaml -n forfarm
|
||||||
|
kubectl apply -f k8s/postgres-service.yaml -n forfarm
|
||||||
|
kubectl apply -f k8s/rabbitmq-pvc.yaml -n forfarm
|
||||||
|
kubectl apply -f k8s/rabbitmq-deployment.yaml -n forfarm
|
||||||
|
kubectl apply -f k8s/rabbitmq-service.yaml -n forfarm
|
||||||
|
|
||||||
|
# Wait for DB and RabbitMQ to be ready (check pods: kubectl get pods -n forfarm -w)
|
||||||
|
|
||||||
|
# Apply backend and frontend
|
||||||
|
kubectl apply -f k8s/backend-deployment.yaml -n forfarm
|
||||||
|
kubectl apply -f k8s/backend-service.yaml -n forfarm
|
||||||
|
kubectl apply -f k8s/frontend-deployment.yaml -n forfarm
|
||||||
|
kubectl apply -f k8s/frontend-service.yaml -n forfarm
|
||||||
|
```
|
||||||
|
|
||||||
|
_Note: The `initContainer` in `backend-deployment.yaml` should handle migrations._
|
||||||
|
|
||||||
|
7. **Setup Ingress:**
|
||||||
|
|
||||||
|
- **Update `k8s/ingress.yaml`:** Replace `your-domain.com` with your domain. Configure TLS and managed certificates if needed (requires creating a `ManagedCertificate` resource in GKE).
|
||||||
|
- **Apply Ingress:**
|
||||||
|
```bash
|
||||||
|
kubectl apply -f k8s/ingress.yaml -n forfarm
|
||||||
|
```
|
||||||
|
- **Get Ingress IP:** Wait a few minutes, then run `kubectl get ingress forfarm-ingress -n forfarm`. Note the `ADDRESS`.
|
||||||
|
- **Configure DNS:** Point your domain's A record(s) to the Ingress IP address.
|
||||||
|
|
||||||
|
8. **Alternative: Cloud SQL:** Instead of running Postgres in K8s, consider using Cloud SQL. Create a Cloud SQL instance, configure its user/database, and update the `DATABASE_URL` in your `k8s/configmap.yaml` to point to the Cloud SQL proxy or private IP. You won't need the `postgres-*.yaml` files.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **Docker Compose:** Use `docker compose logs -f <service_name>` to check logs. Use `docker compose exec <service_name> sh` to get a shell inside a container.
|
||||||
|
- **Kubernetes:** Use `kubectl get pods -n forfarm`, `kubectl logs <pod_name> -n forfarm [-c <container_name>]`, `kubectl describe pod <pod_name> -n forfarm`.
|
||||||
|
- **Migrations:** Check the `goose_db_version` table in your database to see applied migrations.
|
||||||
@ -1,16 +1,48 @@
|
|||||||
FROM golang:1.23.6-alpine3.21
|
FROM golang:1.23-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN apk update && apk add --no-cache curl
|
# Install build dependencies (if any, e.g., for CGO)
|
||||||
|
# RUN apk add --no-cache gcc musl-dev
|
||||||
RUN go install github.com/air-verse/air@latest
|
|
||||||
|
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
|
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application binary
|
||||||
|
# Statically link if possible (reduces dependencies in final image)
|
||||||
|
# RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o ./tmp/api ./cmd/forfarm
|
||||||
|
# Or standard build if CGO is needed or static linking causes issues
|
||||||
|
RUN go build -o ./tmp/api ./cmd/forfarm
|
||||||
|
|
||||||
|
# --- Runner Stage ---
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install runtime dependencies (ca-certificates for HTTPS, tzdata for timezones)
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|
||||||
|
# Copy the compiled binary from the builder stage
|
||||||
|
COPY --from=builder /app/tmp/api /app/api
|
||||||
|
|
||||||
|
# Copy migrations directory (needed if running migrations from container)
|
||||||
|
COPY migrations ./migrations
|
||||||
|
|
||||||
|
# Copy .env file (for local/compose - NOT for production K8s)
|
||||||
|
# If you intend to run migrations inside the container on start,
|
||||||
|
# the DATABASE_URL needs to be available. Secrets/ConfigMaps are preferred in K8s.
|
||||||
|
# COPY .env .env
|
||||||
|
|
||||||
|
# Expose the port the application listens on
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
CMD ["air", "-c", ".air.toml"]
|
# Define the entrypoint - runs the API server by default
|
||||||
|
# Migrations should ideally be run as a separate step or init container
|
||||||
|
ENTRYPOINT ["/app/api"]
|
||||||
|
|
||||||
|
# Default command (in case ENTRYPOINT is just the binary)
|
||||||
|
CMD ["api"]
|
||||||
@ -1,38 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/danielgtaylor/huma/v2"
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HelloWorldInput struct {
|
|
||||||
MyHeader string `header:"Authorization" required:"true" example:"Bearer token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelloWorldOutput struct {
|
|
||||||
Body struct {
|
|
||||||
Message string `json:"message" example:"hello world"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *api) registerHelloRoutes(_ chi.Router, api huma.API) {
|
|
||||||
tags := []string{"hello"}
|
|
||||||
|
|
||||||
huma.Register(api, huma.Operation{
|
|
||||||
OperationID: "helloWorld",
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Path: "/hello",
|
|
||||||
Tags: tags,
|
|
||||||
Summary: "Get hello world message",
|
|
||||||
Description: "Returns a simple hello world message",
|
|
||||||
}, a.helloWorldHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *api) helloWorldHandler(ctx context.Context, input *HelloWorldInput) (*HelloWorldOutput, error) {
|
|
||||||
resp := &HelloWorldOutput{}
|
|
||||||
resp.Body.Message = "hello world from forfarm"
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
12
backend/sample.env
Normal file
12
backend/sample.env
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
POSTGRES_USER=postgres
|
||||||
|
POSTGRES_PASSWORD=@Password123
|
||||||
|
POSTGRES_DB=postgres
|
||||||
|
DATABASE_URL='postgres://postgres:@Password123@localhost:5433/postgres'
|
||||||
|
GOOGLE_CLIENT_ID=GOOGLE_CLIENT_ID
|
||||||
|
GOOGLE_CLIENT_SECRET=GOOGLE_CLIENT_SECRET
|
||||||
|
RABBITMQ_URL=amqp://user:password@localhost:5672/
|
||||||
|
WEATHER_FETCH_INTERVAL=60m
|
||||||
|
OPENWEATHER_API_KEY=OPENWEATHER_API_KEY
|
||||||
|
GEMINI_API_KEY=GEMINI_API_KEY
|
||||||
|
RATE_LIMIT_ENABLED=true
|
||||||
|
RATE_LIMIT_RPS=100
|
||||||
@ -1,47 +1,90 @@
|
|||||||
services:
|
services:
|
||||||
go-api:
|
db:
|
||||||
container_name: go-api
|
image: postgres:16-alpine
|
||||||
# image: goapi
|
container_name: forfarm_db
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-@Password123}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB:-postgres}
|
||||||
|
ports:
|
||||||
|
- "5433:5432"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-postgres} -d $${POSTGRES_DB:-postgres}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq:3-management-alpine
|
||||||
|
container_name: forfarm_rabbitmq
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- rabbitmq_data:/var/lib/rabbitmq/
|
||||||
|
environment:
|
||||||
|
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-user}
|
||||||
|
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:-password}
|
||||||
|
ports:
|
||||||
|
- "5672:5672"
|
||||||
|
- "15672:15672"
|
||||||
|
healthcheck:
|
||||||
|
test: rabbitmq-diagnostics -q ping
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
backend:
|
||||||
|
container_name: forfarm_backend
|
||||||
build:
|
build:
|
||||||
context: ./backend
|
context: ./backend
|
||||||
dockerfile: go.dockerfile
|
dockerfile: go.dockerfile
|
||||||
environment:
|
restart: unless-stopped
|
||||||
DATABASE_URL: 'postgres://postgres:@Password123@psqldb:5432/postgres?sslmode=disable'
|
|
||||||
ports:
|
ports:
|
||||||
- '8000:8000'
|
- "8000:8000"
|
||||||
volumes:
|
|
||||||
- ./backend:/app
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- psqldb
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
env_file:
|
||||||
|
- ./backend/.env
|
||||||
|
# If running migrations inside the container:
|
||||||
|
# command: sh -c "/app/api migrate && /app/api api"
|
||||||
|
# If running migrations separately (preferred):
|
||||||
|
command: ["api"] # Runs the default command in the Dockerfile
|
||||||
|
volumes: # Optional: Mount source for live reload during dev (remove for prod builds)
|
||||||
|
- ./backend:/app
|
||||||
|
# networks:
|
||||||
|
# - forfarm-net
|
||||||
|
|
||||||
psqldb:
|
frontend:
|
||||||
container_name: psqldb
|
container_name: forfarm_frontend
|
||||||
image: postgres:17
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: postgres
|
|
||||||
POSTGRES_PASSWORD: '@Password123'
|
|
||||||
POSTGRES_DB: postgres
|
|
||||||
ports:
|
|
||||||
- '5433:5432'
|
|
||||||
volumes:
|
|
||||||
- pgsqldata:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
next-frontend:
|
|
||||||
container_name: next-frontend
|
|
||||||
# image: nextfront
|
|
||||||
build:
|
build:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
dockerfile: next.dockerfile
|
dockerfile: next.dockerfile
|
||||||
environment:
|
# args:
|
||||||
NEXT_PUBLIC_API_URL: 'localhost:8000/api'
|
# NEXT_PUBLIC_BACKEND_URL: ${NEXT_PUBLIC_BACKEND_URL}
|
||||||
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- '3000:3000'
|
- "3000:3000"
|
||||||
volumes:
|
|
||||||
- ./frontend:/app
|
|
||||||
- /app/node_modules
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- go-api
|
- backend
|
||||||
|
env_file:
|
||||||
|
- ./frontend/.env
|
||||||
|
# volumes: # Mount source for live reload during dev (remove for prod builds)
|
||||||
|
# - ./frontend:/app
|
||||||
|
# - /app/node_modules # Avoid overwriting node_modules
|
||||||
|
# - /app/.next # Avoid overwriting build artifacts
|
||||||
|
# networks:
|
||||||
|
# - forfarm-net
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgsqldata:
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
rabbitmq_data:
|
||||||
|
driver: local
|
||||||
|
# networks:
|
||||||
|
# forfarm-net:
|
||||||
|
# driver: bridge
|
||||||
|
|||||||
@ -58,7 +58,7 @@ export function EditFarmForm({ initialData, onSubmit, onCancel, isSubmitting }:
|
|||||||
type: initialData.farmType || "",
|
type: initialData.farmType || "",
|
||||||
area: initialData.totalSize || "",
|
area: initialData.totalSize || "",
|
||||||
});
|
});
|
||||||
}, [initialData, form.reset]);
|
}, [initialData, form]);
|
||||||
|
|
||||||
const handleSubmit = async (values: z.infer<typeof farmFormSchema>) => {
|
const handleSubmit = async (values: z.infer<typeof farmFormSchema>) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -49,11 +49,11 @@ export function FarmCard({ variant, farm, onClick, onEditClick, onDeleteClick }:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (variant === "farm" && farm) {
|
if (variant === "farm" && farm) {
|
||||||
const formattedDate = new Intl.DateTimeFormat("en-US", {
|
// const formattedDate = new Intl.DateTimeFormat("en-US", {
|
||||||
year: "numeric",
|
// year: "numeric",
|
||||||
month: "short",
|
// month: "short",
|
||||||
day: "numeric",
|
// day: "numeric",
|
||||||
}).format(new Date(farm.createdAt));
|
// }).format(new Date(farm.createdAt));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={cardClasses}>
|
<Card className={cardClasses}>
|
||||||
|
|||||||
@ -10,7 +10,15 @@ const compat = new FlatCompat({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const eslintConfig = [
|
const eslintConfig = [
|
||||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
...compat.config({
|
||||||
|
extends: ["next", "next/core-web-vitals", "next/typescript"],
|
||||||
|
rules: {
|
||||||
|
"react/no-unescaped-entities": "off",
|
||||||
|
"@next/next/no-page-custom-font": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
},
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
export default eslintConfig;
|
export default eslintConfig;
|
||||||
|
|||||||
@ -13,6 +13,9 @@ const nextConfig: NextConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
typescript: {
|
||||||
|
ignoreBuildErrors: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|||||||
@ -1,19 +1,47 @@
|
|||||||
FROM node:20 AS base
|
# --- Base Stage (Dependencies) ---
|
||||||
|
FROM node:20-alpine AS base
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN npm i -g pnpm
|
RUN npm install -g pnpm
|
||||||
COPY package.json pnpm-lock.yaml ./
|
|
||||||
RUN pnpm install
|
|
||||||
|
|
||||||
|
# Copy package manager files
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
# Install dependencies using pnpm
|
||||||
|
# Use --frozen-lockfile for reproducible installs
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# --- Builder Stage ---
|
||||||
|
FROM base AS builder
|
||||||
|
|
||||||
|
# Copy the rest of the source code
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
FROM node:20-alpine3.20 AS release
|
# Copy environment variables needed for build time (if any)
|
||||||
WORKDIR /app
|
# Ensure this file exists or handle its absence
|
||||||
|
# COPY .env.production .env.production
|
||||||
|
|
||||||
RUN npm i -g pnpm
|
# Build the Next.js application
|
||||||
|
# Pass build-time env vars if needed via ARG and --build-arg
|
||||||
|
# Example: ARG NEXT_PUBLIC_SOME_VAR
|
||||||
|
# ENV NEXT_PUBLIC_SOME_VAR=$NEXT_PUBLIC_SOME_VAR
|
||||||
|
RUN pnpm build
|
||||||
|
|
||||||
COPY --from=base /app .
|
# --- Runner Stage ---
|
||||||
|
FROM base AS runner
|
||||||
|
|
||||||
|
# Copy built artifacts from the builder stage
|
||||||
|
COPY --from=builder /app/.next ./.next
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
# Copy only necessary node_modules for production runtime
|
||||||
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
|
||||||
|
# Expose the port the Next.js app runs on
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["pnpm", "dev"]
|
# Set NODE_ENV to production
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
CMD ["pnpm", "start"]
|
||||||
8
frontend/sample.env
Normal file
8
frontend/sample.env
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
NEXT_PUBLIC_BACKEND_URL=http://localhost:8000
|
||||||
|
NEXTAUTH_URL=http://localhost:3000
|
||||||
|
NEXTAUTH_URL_INTERNAL=http://localhost:3000
|
||||||
|
NEXT_PUBLIC_BACKEND_URL=http://localhost:8000
|
||||||
|
NEXT_PUBLIC_GOOGLE_CLIENT_ID=NEXT_PUBLIC_GOOGLE_CLIENT_ID
|
||||||
|
GOOGLE_CLIENT_SECRET=GOOGLE_CLIENT_SECRET
|
||||||
|
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=NEXT_PUBLIC_GOOGLE_MAPS_API_KEY
|
||||||
|
OPENWEATHER_API_KEY=OPENWEATHER_API_KEY
|
||||||
43
k8s/backend-deployment.yaml
Normal file
43
k8s/backend-deployment.yaml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: forfarm-backend
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: forfarm-backend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: forfarm-backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: backend
|
||||||
|
image: sosokker/forfarm-backend:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8000
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: forfarm-config
|
||||||
|
- secretRef:
|
||||||
|
name: forfarm-secrets
|
||||||
|
resources:
|
||||||
|
requests: # Minimum guaranteed resources
|
||||||
|
memory: "128Mi" # Mebibytes
|
||||||
|
cpu: "100m" # 100 millicores (0.1 CPU)
|
||||||
|
limits: # Maximum allowed resources
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "500m" # 500 millicores (0.5 CPU)
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8000
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8000
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 20
|
||||||
13
k8s/backend-service.yaml
Normal file
13
k8s/backend-service.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: backend-service
|
||||||
|
namespace: forfarm
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: backend
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 8000
|
||||||
|
targetPort: 8000
|
||||||
|
type: ClusterIP
|
||||||
29
k8s/configmap.yaml
Normal file
29
k8s/configmap.yaml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: forfarm-config
|
||||||
|
namespace: forfarm
|
||||||
|
data:
|
||||||
|
POSTGRES_USER: "postgres"
|
||||||
|
POSTGRES_DB: "forfarmdb"
|
||||||
|
POSTGRES_HOST: "postgres-service.forfarm.svc.cluster.local"
|
||||||
|
DATABASE_URL: "postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@$(POSTGRES_HOST):5432/$(POSTGRES_DB)?sslmode=disable"
|
||||||
|
|
||||||
|
RABBITMQ_USER: "user"
|
||||||
|
RABBITMQ_HOST: "rabbitmq-service.forfarm.svc.cluster.local"
|
||||||
|
RABBITMQ_URL: "amqp://$(RABBITMQ_USER):$(RABBITMQ_PASSWORD)@$(RABBITMQ_HOST):5672/"
|
||||||
|
|
||||||
|
# Backend Config
|
||||||
|
PORT: "8000"
|
||||||
|
WEATHER_FETCH_INTERVAL: "60m"
|
||||||
|
OPENWEATHER_CACHE_TTL: "15m"
|
||||||
|
GOOGLE_CLIENT_ID: "GOOGLE_CLIENT_ID"
|
||||||
|
GOOGLE_REDIRECT_URL: "https://your-domain.com/auth/login/google"
|
||||||
|
|
||||||
|
NEXT_PUBLIC_BACKEND_URL: "http://backend-service.forfarm.svc.cluster.local:8000"
|
||||||
|
NEXT_PUBLIC_GOOGLE_CLIENT_ID: "NEXT_PUBLIC_GOOGLE_CLIENT_ID"
|
||||||
|
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: "NEXT_PUBLIC_GOOGLE_MAPS_API_KEY"
|
||||||
|
|
||||||
|
# GCS Config
|
||||||
|
GCS_BUCKET_NAME: "YOUR_GCS_BUCKET_NAME"
|
||||||
|
# GCS_SERVICE_ACCOUNT_KEY_PATH: "/etc/gcs-secrets/key.json"
|
||||||
45
k8s/frontend-deployment.yaml
Normal file
45
k8s/frontend-deployment.yaml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: forfarm-frontend
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: forfarm-frontend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: forfarm-frontend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: frontend
|
||||||
|
image: sosokker/forfarm-frontend:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
env:
|
||||||
|
- name: NEXT_PUBLIC_BACKEND_URL
|
||||||
|
value: "http://forfarm-backend-service:8000"
|
||||||
|
- name: NEXT_PUBLIC_GOOGLE_CLIENT_ID
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: forfarm-secrets
|
||||||
|
key: NEXT_PUBLIC_GOOGLE_CLIENT_ID
|
||||||
|
- name: NEXT_PUBLIC_GOOGLE_MAPS_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: forfarm-secrets
|
||||||
|
key: NEXT_PUBLIC_GOOGLE_MAPS_API_KEY
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "150m"
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "750m"
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 3000
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
13
k8s/frontend-service.yaml
Normal file
13
k8s/frontend-service.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: frontend-service
|
||||||
|
namespace: forfarm
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: frontend
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 80
|
||||||
|
targetPort: 3000
|
||||||
|
type: ClusterIP
|
||||||
30
k8s/ingress.yaml
Normal file
30
k8s/ingress.yaml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: forfarm-ingress
|
||||||
|
namespace: forfarm
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: "gce"
|
||||||
|
networking.gke.io/managed-certificates: "forfarm-certificate"
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: sirin.dev
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: frontend-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- host: api.sirin.dev
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: backend-service
|
||||||
|
port:
|
||||||
|
number: 8000
|
||||||
4
k8s/namespace.yaml
Normal file
4
k8s/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: forfarm
|
||||||
72
k8s/postgres-deployment.yaml
Normal file
72
k8s/postgres-deployment.yaml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: postgres-deployment
|
||||||
|
namespace: forfarm
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: postgres
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: postgres
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: postgres
|
||||||
|
image: postgres:16-alpine
|
||||||
|
ports:
|
||||||
|
- containerPort: 5432
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "1Gi"
|
||||||
|
cpu: "500m"
|
||||||
|
env:
|
||||||
|
- name: POSTGRES_DB
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: forfarm-config
|
||||||
|
key: POSTGRES_DB
|
||||||
|
- name: POSTGRES_USER
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: forfarm-config
|
||||||
|
key: POSTGRES_USER
|
||||||
|
- name: POSTGRES_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: forfarm-secrets
|
||||||
|
key: POSTGRES_PASSWORD
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /var/lib/postgresql/data
|
||||||
|
name: postgres-storage
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["pg_isready", "-U", "$(POSTGRES_USER)", "-d", "$(POSTGRES_DB)"]
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["pg_isready", "-U", "$(POSTGRES_USER)", "-d", "$(POSTGRES_DB)"]
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
volumes:
|
||||||
|
- name: postgres-storage
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: postgres-pvc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: postgres-pvc
|
||||||
|
namespace: forfarm
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Gi
|
||||||
13
k8s/postgres-service.yaml
Normal file
13
k8s/postgres-service.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: postgres-service
|
||||||
|
namespace: forfarm
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: postgres
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5432
|
||||||
|
targetPort: 5432
|
||||||
|
type: ClusterIP
|
||||||
68
k8s/rabbitmq-deployment.yaml
Normal file
68
k8s/rabbitmq-deployment.yaml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: rabbitmq-deployment
|
||||||
|
namespace: forfarm
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: rabbitmq
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: rabbitmq
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: rabbitmq
|
||||||
|
image: rabbitmq:3-management-alpine
|
||||||
|
ports:
|
||||||
|
- containerPort: 5672 # AMQP
|
||||||
|
- containerPort: 15672 # Management UI
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "300m"
|
||||||
|
env:
|
||||||
|
- name: RABBITMQ_DEFAULT_USER
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: forfarm-config
|
||||||
|
key: RABBITMQ_USER
|
||||||
|
- name: RABBITMQ_DEFAULT_PASS
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: forfarm-secrets
|
||||||
|
key: RABBITMQ_PASSWORD
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /var/lib/rabbitmq/
|
||||||
|
name: rabbitmq-storage
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["rabbitmq-diagnostics", "status"]
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["rabbitmq-diagnostics", "ping"]
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
volumes:
|
||||||
|
- name: rabbitmq-storage
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: rabbitmq-pvc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: rabbitmq-pvc
|
||||||
|
namespace: forfarm
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 2Gi
|
||||||
18
k8s/rabbitmq-service.yaml
Normal file
18
k8s/rabbitmq-service.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: rabbitmq-service
|
||||||
|
namespace: forfarm
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: rabbitmq
|
||||||
|
ports:
|
||||||
|
- name: amqp
|
||||||
|
protocol: TCP
|
||||||
|
port: 5672
|
||||||
|
targetPort: 5672
|
||||||
|
- name: management
|
||||||
|
protocol: TCP
|
||||||
|
port: 15672
|
||||||
|
targetPort: 15672
|
||||||
|
type: ClusterIP
|
||||||
15
k8s/secrets.yaml
Normal file
15
k8s/secrets.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: forfarm-secrets
|
||||||
|
namespace: forfarm
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
# Use: echo -n "your_password" | base64
|
||||||
|
POSTGRES_PASSWORD: <base64-encoded-db-password>
|
||||||
|
RABBITMQ_PASSWORD: <base64-encoded-rabbitmq-password>
|
||||||
|
JWT_SECRET_KEY: <base64-encoded-jwt-secret>
|
||||||
|
GOOGLE_CLIENT_SECRET: <base64-encoded-google-client-secret>
|
||||||
|
OPENWEATHER_API_KEY: <base64-encoded-openweather-key>
|
||||||
|
GEMINI_API_KEY: <base64-encoded-gemini-key>
|
||||||
|
GCS_SERVICE_ACCOUNT_KEY_JSON: <base64-encoded-gcs-key-json>
|
||||||
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"dependencies": {
|
|
||||||
"@types/js-cookie": "^3.0.6"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
lockfileVersion: '9.0'
|
|
||||||
|
|
||||||
settings:
|
|
||||||
autoInstallPeers: true
|
|
||||||
excludeLinksFromLockfile: false
|
|
||||||
|
|
||||||
importers:
|
|
||||||
|
|
||||||
.:
|
|
||||||
dependencies:
|
|
||||||
'@types/js-cookie':
|
|
||||||
specifier: ^3.0.6
|
|
||||||
version: 3.0.6
|
|
||||||
|
|
||||||
packages:
|
|
||||||
|
|
||||||
'@types/js-cookie@3.0.6':
|
|
||||||
resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
|
|
||||||
|
|
||||||
snapshots:
|
|
||||||
|
|
||||||
'@types/js-cookie@3.0.6': {}
|
|
||||||
Loading…
Reference in New Issue
Block a user