From 2831b8151a7139defe31db26f48c580c9e2892f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 08:24:26 +0000 Subject: [PATCH 1/6] Initial plan From dfc4c9d204a86b9ae200f5f85f9eff5b0980ef2b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 08:38:24 +0000 Subject: [PATCH 2/6] Add development infrastructure and fix code quality issues Co-authored-by: Genaker <9213670+Genaker@users.noreply.github.com> --- .editorconfig | 39 ++++ .github/workflows/ci.yml | 124 ++++++++++++ .github/workflows/security.yml | 53 +++++ .gitignore | 47 ++++- .golangci.yml | 52 +++++ CHANGELOG.md | 58 ++++++ CONTRIBUTING.md | 232 ++++++++++++++++++++++ Dockerfile | 49 +++++ Makefile | 106 ++++++++++ SECURITY.md | 140 +++++++++++++ docker-compose.yml | 65 ++++++ magento.go | 9 + service/sales/sales_order_grid_service.go | 4 +- 13 files changed, 972 insertions(+), 6 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/security.yml create mode 100644 .golangci.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 SECURITY.md create mode 100644 docker-compose.yml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..213a9ad --- /dev/null +++ b/.editorconfig @@ -0,0 +1,39 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +# Go files +[*.go] +indent_style = tab +indent_size = 4 + +# YAML files +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +# JSON files +[*.json] +indent_style = space +indent_size = 2 + +# Makefile +[Makefile] +indent_style = tab + +# Markdown files +[*.md] +trim_trailing_whitespace = false + +# Shell scripts +[*.sh] +indent_style = space +indent_size = 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2888146 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,124 @@ +name: CI + +on: + push: + branches: [ main, master, develop ] + pull_request: + branches: [ main, master, develop ] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: magento_test + MYSQL_USER: magento + MYSQL_PASSWORD: magento + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + redis: + image: redis:7-alpine + ports: + - 6379:6379 + options: >- + --health-cmd="redis-cli ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Verify dependencies + run: | + go mod download + go mod verify + + - name: Run go fmt + run: | + fmt_output=$(gofmt -l .) + if [ -n "$fmt_output" ]; then + echo "The following files are not formatted:" + echo "$fmt_output" + exit 1 + fi + + - name: Run go vet + run: go vet ./... + + - name: Run tests + run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./... + env: + MYSQL_USER: magento + MYSQL_PASS: magento + MYSQL_HOST: localhost + MYSQL_PORT: 3306 + MYSQL_DB: magento_test + REDIS_ADDR: localhost:6379 + API_USER: admin + API_PASS: admin123 + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + file: ./coverage.txt + flags: unittests + name: codecov-umbrella + + build: + name: Build + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Build server binary + run: go build -v -o magento magento.go + + - name: Build CLI binary + run: go build -v -o cli cli.go + + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: golangci-lint + uses: golangci/golangci-lint-action@v4 + with: + version: latest + args: --timeout=5m diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..5b9c79a --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,53 @@ +name: Security Scan + +on: + push: + branches: [ main, master, develop ] + pull_request: + branches: [ main, master, develop ] + schedule: + - cron: '0 0 * * 0' # Run weekly on Sundays + +jobs: + gosec: + name: Security Scan (gosec) + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Run Gosec Security Scanner + uses: securego/gosec@master + with: + args: '-no-fail -fmt sarif -out results.sarif ./...' + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif + + dependency-check: + name: Dependency Vulnerability Scan + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Run govulncheck + run: | + go install golang.org/x/vuln/cmd/govulncheck@latest + govulncheck ./... diff --git a/.gitignore b/.gitignore index e836d08..b7fc16d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,48 @@ # Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool +*.out +coverage.txt +coverage.html + +# Go workspace file +go.work +go.work.sum + +# Dependency directories +vendor/ + +# Environment files .env +.env.local +.env.*.local + +# IDE specific files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Build output +/magento +/cli +dist/ +build/ + +# Application specific var/* *.log *.log.* -*.log.*.* -*.log.*.*.* -*.log.*.*.*.* -*.log.*.*.*.*.* + +# OS specific +.DS_Store +Thumbs.db diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..f202d01 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,52 @@ +run: + timeout: 5m + tests: true + skip-dirs: + - vendor + +linters: + enable: + - errcheck # Check for unchecked errors + - gosimple # Simplify code + - govet # Reports suspicious constructs + - ineffassign # Detects ineffectual assignments + - staticcheck # Advanced Go linter + - unused # Checks for unused constants, variables, functions and types + - gofmt # Checks whether code was gofmt-ed + - goimports # Check import statements are formatted according to goimport + - misspell # Finds commonly misspelled English words + - gosec # Inspects source code for security problems + - bodyclose # Checks whether HTTP response body is closed + - unconvert # Remove unnecessary type conversions + - prealloc # Finds slice declarations that could potentially be preallocated + - exportloopref # Checks for pointers to enclosing loop variables + +linters-settings: + errcheck: + check-type-assertions: true + check-blank: true + + govet: + check-shadowing: true + + gofmt: + simplify: true + + misspell: + locale: US + + gosec: + excludes: + - G104 # Audit errors not checked - already covered by errcheck + +issues: + exclude-use-default: false + max-issues-per-linter: 0 + max-same-issues: 0 + + exclude-rules: + # Exclude some linters from running on tests files + - path: _test\.go + linters: + - gosec + - errcheck diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9dc67d7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,58 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Makefile for common development tasks +- Dockerfile for containerization +- docker-compose.yml for local development with MySQL and Redis +- .editorconfig for consistent code formatting +- .golangci.yml for comprehensive linting configuration +- GitHub Actions CI workflow for automated testing and building +- GitHub Actions security scanning workflow +- CONTRIBUTING.md with development guidelines +- SECURITY.md for vulnerability reporting +- CHANGELOG.md for tracking changes +- Improved .gitignore with comprehensive Go project exclusions + +### Fixed +- Import alias conflict in `service/sales/sales_order_grid_service.go` +- Package import declarations to avoid `go vet` errors + +### Changed +- Enhanced documentation with contribution guidelines +- Improved project structure documentation + +## [1.0.1] - 2025-01-XX + +### Added +- Initial public release +- REST API for Magento products, categories, and orders +- Echo web server with RESTful routing +- Basic authentication for all endpoints +- GORM ORM for MySQL +- Global product cache for performance +- Flexible product API with EAV attributes flattened +- Redis integration for caching +- Cron job scheduler +- CLI interface for management tasks +- HTML templates with Tailwind CSS +- Performance monitoring headers +- Request registry and global cache +- Comprehensive README documentation + +### Features +- Product flat API with cache (~4x performance improvement) +- Category management +- Sales order grid API +- Product image optimization with WebP support +- Scheduled background jobs +- Multi-environment configuration support + +[Unreleased]: https://github.com/Genaker/GoGento/compare/v1.0.1...HEAD +[1.0.1]: https://github.com/Genaker/GoGento/releases/tag/v1.0.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ad77efa --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,232 @@ +# Contributing to GoGento + +Thank you for your interest in contributing to GoGento! This document provides guidelines and instructions for contributing to this project. + +## Code of Conduct + +Please be respectful and constructive in all interactions with other contributors. + +## Getting Started + +### Prerequisites + +- Go 1.23 or higher +- MySQL 8.0 or higher +- Redis (optional) +- Make (optional but recommended) + +### Development Setup + +1. **Clone the repository** + ```bash + git clone https://github.com/Genaker/GoGento.git + cd GoGento + ``` + +2. **Install dependencies** + ```bash + make deps + # or + go mod download + ``` + +3. **Set up environment** + ```bash + cp .env.example .env + # Edit .env with your local database credentials + ``` + +4. **Start local services with Docker** + ```bash + make docker-up + # or + docker-compose up -d + ``` + +5. **Run the application** + ```bash + make run + # or + go run magento.go + ``` + +## Development Workflow + +### Before Making Changes + +1. Create a new branch from `main`: + ```bash + git checkout -b feature/your-feature-name + ``` + +2. Make sure you're up to date: + ```bash + git pull origin main + ``` + +### Making Changes + +1. **Write clean, idiomatic Go code** + - Follow the [Effective Go](https://golang.org/doc/effective_go) guidelines + - Use meaningful variable and function names + - Keep functions small and focused + - Add comments for exported functions and complex logic + +2. **Format your code** + ```bash + make fmt + # or + go fmt ./... + ``` + +3. **Run linters** + ```bash + make lint + # or + golangci-lint run ./... + ``` + +4. **Run tests** + ```bash + make test + # or + go test -v ./... + ``` + +### Code Structure + +Follow the existing project structure: + +``` +magento.GO/ +├── api/ # HTTP handlers +├── cmd/ # CLI commands +├── config/ # Configuration +├── core/ # Core utilities (cache, log, registry) +├── cron/ # Scheduled jobs +├── html/ # HTML templates and handlers +├── model/ +│ ├── entity/ # Data models +│ └── repository/ # Data access layer +└── service/ # Business logic +``` + +### Commit Messages + +Write clear, descriptive commit messages: + +``` +feat: add new product search endpoint +fix: resolve race condition in cache +docs: update API documentation +refactor: simplify order service logic +test: add unit tests for product repository +``` + +Use conventional commit format: +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation changes +- `refactor`: Code refactoring +- `test`: Adding or updating tests +- `chore`: Maintenance tasks + +## Testing + +### Writing Tests + +- Place test files next to the code they test (e.g., `product_service.go` → `product_service_test.go`) +- Use table-driven tests for multiple test cases +- Mock external dependencies +- Aim for high test coverage + +Example test structure: +```go +func TestProductService_GetProduct(t *testing.T) { + tests := []struct { + name string + id uint + want *Product + wantErr bool + }{ + // test cases + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // test implementation + }) + } +} +``` + +### Running Tests + +```bash +# Run all tests +make test + +# Run tests with coverage +make test-coverage + +# Run tests for a specific package +go test -v ./service/product/... +``` + +## Pull Request Process + +1. **Update documentation** if you've changed APIs or added features +2. **Add tests** for new functionality +3. **Ensure all tests pass** locally +4. **Run linters** and fix any issues +5. **Update README.md** if needed +6. **Create a pull request** with a clear description of changes + +### PR Checklist + +- [ ] Code follows project style guidelines +- [ ] Tests pass locally +- [ ] New code is covered by tests +- [ ] Documentation is updated +- [ ] Commit messages are clear and descriptive +- [ ] No unnecessary dependencies added + +## Adding New Features + +### Adding a New API Endpoint + +1. **Create the entity model** in `model/entity/` +2. **Create the repository** in `model/repository/` +3. **Create the service** in `service/` +4. **Create the API handler** in `api/` +5. **Register routes** in `magento.go` +6. **Add tests** for each layer +7. **Update documentation** + +### Adding a New Cron Job + +1. **Create job implementation** in `cron/jobs/` +2. **Register the job** in `config/cron.go` +3. **Add CLI command** in `cmd/cron.go` +4. **Document the job** in README.md + +## Code Review + +All submissions require review. We use GitHub pull requests for this purpose. Reviewers will check for: + +- Code quality and style +- Test coverage +- Documentation +- Performance implications +- Security considerations + +## Questions? + +If you have questions about contributing, please: + +1. Check existing issues and discussions +2. Open a new issue with your question +3. Reach out to maintainers + +## License + +By contributing to GoGento, you agree that your contributions will be licensed under the MIT License. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..15527d6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +# Build stage +FROM golang:1.24-alpine AS builder + +# Install build dependencies +RUN apk add --no-cache git make + +WORKDIR /app + +# Copy go mod files +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source code +COPY . . + +# Build the application +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o magento magento.go +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o cli cli.go + +# Final stage +FROM alpine:latest + +# Install ca-certificates for HTTPS +RUN apk --no-cache add ca-certificates tzdata + +WORKDIR /app + +# Copy binaries from builder +COPY --from=builder /app/magento . +COPY --from=builder /app/cli . + +# Copy static assets and templates +COPY --from=builder /app/assets ./assets +COPY --from=builder /app/html ./html +COPY --from=builder /app/input.css ./input.css +COPY --from=builder /app/tailwind.config.js ./tailwind.config.js + +# Create var directory for logs +RUN mkdir -p /app/var + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + +# Run the application +CMD ["./magento"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b52a214 --- /dev/null +++ b/Makefile @@ -0,0 +1,106 @@ +.PHONY: help build build-cli run test lint fmt vet clean install deps tidy docker-build docker-up docker-down + +# Default target +.DEFAULT_GOAL := help + +# Variables +BINARY_NAME=magento +CLI_BINARY_NAME=cli +GO=go +GOFLAGS=-v +LDFLAGS=-ldflags "-s -w" + +help: ## Display this help message + @echo "GoGento - Magento Go API" + @echo "" + @echo "Usage: make [target]" + @echo "" + @echo "Available targets:" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-20s %s\n", $$1, $$2}' + +build: ## Build the main server binary + @echo "Building $(BINARY_NAME)..." + $(GO) build $(GOFLAGS) $(LDFLAGS) -o $(BINARY_NAME) magento.go + +build-cli: ## Build the CLI binary + @echo "Building $(CLI_BINARY_NAME)..." + $(GO) build $(GOFLAGS) $(LDFLAGS) -o $(CLI_BINARY_NAME) cli.go + +build-all: build build-cli ## Build all binaries + +run: ## Run the main server (development mode) + @echo "Running $(BINARY_NAME)..." + $(GO) run magento.go + +run-cli: ## Run the CLI + @echo "Running $(CLI_BINARY_NAME)..." + $(GO) run cli.go + +test: ## Run tests + @echo "Running tests..." + $(GO) test -v -race -coverprofile=coverage.txt -covermode=atomic ./... + +test-coverage: test ## Run tests with coverage report + @echo "Generating coverage report..." + $(GO) tool cover -html=coverage.txt -o coverage.html + @echo "Coverage report generated: coverage.html" + +lint: ## Run golangci-lint + @echo "Running linters..." + @if command -v golangci-lint > /dev/null; then \ + golangci-lint run ./...; \ + else \ + echo "golangci-lint not installed. Install it from https://golangci-lint.run/usage/install/"; \ + exit 1; \ + fi + +fmt: ## Format code with go fmt + @echo "Formatting code..." + $(GO) fmt ./... + +vet: ## Run go vet + @echo "Running go vet..." + $(GO) vet ./... + +check: fmt vet ## Run fmt and vet + +clean: ## Clean build artifacts + @echo "Cleaning..." + rm -f $(BINARY_NAME) $(CLI_BINARY_NAME) + rm -f coverage.txt coverage.html + rm -rf dist/ build/ + $(GO) clean + +install: ## Install dependencies + @echo "Installing dependencies..." + $(GO) mod download + +deps: install ## Alias for install + +tidy: ## Tidy and verify dependencies + @echo "Tidying dependencies..." + $(GO) mod tidy + $(GO) mod verify + +docker-build: ## Build Docker image + @echo "Building Docker image..." + docker build -t gogento:latest . + +docker-up: ## Start services with docker-compose + @echo "Starting Docker services..." + docker-compose up -d + +docker-down: ## Stop services with docker-compose + @echo "Stopping Docker services..." + docker-compose down + +dev: ## Run development server with auto-reload (requires air) + @if command -v air > /dev/null; then \ + air; \ + else \ + echo "air not installed. Install it with: go install github.com/air-verse/air@latest"; \ + echo "Falling back to regular run..."; \ + $(MAKE) run; \ + fi + +all: clean tidy check test build-all ## Run all checks and build everything diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..9226e01 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,140 @@ +# Security Policy + +## Supported Versions + +We release patches for security vulnerabilities for the following versions: + +| Version | Supported | +| ------- | ------------------ | +| 1.x.x | :white_check_mark: | + +## Reporting a Vulnerability + +The GoGento team takes security bugs seriously. We appreciate your efforts to responsibly disclose your findings. + +### How to Report a Security Vulnerability + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them via one of the following methods: + +1. **GitHub Security Advisories** (Recommended) + - Go to the [Security tab](https://github.com/Genaker/GoGento/security/advisories) of this repository + - Click "Report a vulnerability" + - Fill out the form with details + +2. **Email** + - Send an email to the repository maintainers + - Include as much information as possible (see below) + +### What to Include in Your Report + +Please include the following information to help us better understand the nature and scope of the issue: + +- Type of issue (e.g., buffer overflow, SQL injection, cross-site scripting, etc.) +- Full paths of source file(s) related to the manifestation of the issue +- The location of the affected source code (tag/branch/commit or direct URL) +- Any special configuration required to reproduce the issue +- Step-by-step instructions to reproduce the issue +- Proof-of-concept or exploit code (if possible) +- Impact of the issue, including how an attacker might exploit it + +### What to Expect + +- **Acknowledgment**: We'll acknowledge your report within 48 hours +- **Updates**: We'll keep you informed about our progress +- **Timeline**: We aim to resolve critical security issues within 7-14 days +- **Credit**: We'll credit you in the security advisory (unless you prefer to remain anonymous) + +## Security Best Practices + +When deploying GoGento in production: + +### Environment Variables +- Never commit `.env` files to version control +- Use strong, unique passwords for `API_USER` and `API_PASS` +- Rotate credentials regularly +- Use environment-specific configurations + +### Database Security +- Use dedicated database users with minimal required privileges +- Enable SSL/TLS for database connections in production +- Keep MySQL updated to the latest stable version +- Regularly backup your database + +### Redis Security +- Set a strong Redis password (`REDIS_PASS`) +- Bind Redis to localhost or use firewall rules +- Enable Redis AUTH +- Consider using Redis ACLs for fine-grained access control + +### Authentication +- Use `AUTH_TYPE=key` with a strong API key for production +- Implement rate limiting to prevent brute force attacks +- Consider implementing OAuth2 or JWT for more sophisticated authentication +- Use HTTPS/TLS in production + +### API Security +- Always run behind a reverse proxy (nginx, Caddy, etc.) in production +- Enable HTTPS/TLS +- Implement request rate limiting +- Validate and sanitize all user input +- Use prepared statements (GORM does this by default) + +### Dependency Management +- Regularly update dependencies: `go get -u ./...` +- Monitor for security advisories: `go install golang.org/x/vuln/cmd/govulncheck@latest && govulncheck ./...` +- Review dependency changes before updating + +### Logging and Monitoring +- Monitor application logs for suspicious activity +- Set up alerts for repeated authentication failures +- Log security-relevant events +- Use `GORM_LOG=off` in production to avoid logging sensitive data + +### Docker Security +- Use specific version tags, not `latest` +- Run containers as non-root user +- Keep base images updated +- Scan images for vulnerabilities +- Use secrets management for sensitive data + +### Network Security +- Use firewall rules to restrict access +- Implement network segmentation +- Use VPN for administrative access +- Restrict database and Redis access to application servers only + +## Known Security Considerations + +### CORS +The application currently uses Echo's default CORS middleware. In production: +- Configure specific allowed origins +- Avoid using wildcard (`*`) in production +- Set appropriate `Access-Control-Allow-Credentials` + +### SQL Injection +GORM uses prepared statements by default, providing protection against SQL injection. However: +- Never use raw SQL with user input without parameterization +- Validate and sanitize all user input +- Use GORM's safe query methods + +### Authentication +The current basic authentication is suitable for internal APIs but consider: +- Implementing OAuth2 for public APIs +- Using API keys with proper rotation policies +- Implementing multi-factor authentication for sensitive operations + +## Disclosure Policy + +When we receive a security bug report, we will: + +1. Confirm the problem and determine affected versions +2. Audit code to find any similar problems +3. Prepare fixes for all supported versions +4. Release new security patch versions +5. Publish a security advisory + +## Comments on This Policy + +If you have suggestions on how this process could be improved, please submit a pull request. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7985683 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,65 @@ +version: '3.8' + +services: + mysql: + image: mysql:8.0 + container_name: gogento-mysql + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: magento + MYSQL_USER: magento + MYSQL_PASSWORD: magento + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + container_name: gogento-redis + ports: + - "6379:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + + app: + build: + context: . + dockerfile: Dockerfile + container_name: gogento-app + ports: + - "8080:8080" + environment: + MYSQL_USER: magento + MYSQL_PASS: magento + MYSQL_HOST: mysql + MYSQL_PORT: 3306 + MYSQL_DB: magento + API_USER: admin + API_PASS: admin123 + REDIS_ADDR: redis:6379 + REDIS_PASS: "" + PORT: 8080 + GORM_LOG: "off" + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + restart: unless-stopped + volumes: + - ./var:/app/var + +volumes: + mysql_data: + redis_data: diff --git a/magento.go b/magento.go index 1d9cf48..d637657 100644 --- a/magento.go +++ b/magento.go @@ -187,6 +187,15 @@ func main() { productApi.RegisterProductRoutes(apiGroup, db) categoryApi.RegisterCategoryAPI(apiGroup, db) + // Health check endpoint (no auth required) + e.GET("/health", func(c echo.Context) error { + return c.JSON(http.StatusOK, echo.Map{ + "status": "healthy", + "service": "GoGento", + "version": "1.0.1", + }) + }) + // Not Autorised HTML Routes html.RegisterProductHTMLRoutes(e, db) html.RegisterCategoryHTMLRoutes(e, db) diff --git a/service/sales/sales_order_grid_service.go b/service/sales/sales_order_grid_service.go index 5e9f08d..95fdca6 100644 --- a/service/sales/sales_order_grid_service.go +++ b/service/sales/sales_order_grid_service.go @@ -1,8 +1,8 @@ package sales import ( - "magento.GO/model/entity/sales" - "magento.GO/model/repository/sales" + entity "magento.GO/model/entity/sales" + repository "magento.GO/model/repository/sales" ) type SalesOrderGridService struct { From 3f21b349ec7953bc50f0820cd09fc8c377731424 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 08:41:05 +0000 Subject: [PATCH 3/6] Add tests, Dependabot, and improve README with badges Co-authored-by: Genaker <9213670+Genaker@users.noreply.github.com> --- .github/dependabot.yml | 49 ++++++++++++ README.md | 53 +++++++++++++ core/cache/cache_test.go | 159 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 core/cache/cache_test.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ff3f948 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,49 @@ +version: 2 +updates: + # Enable version updates for Go modules + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + reviewers: + - "Genaker" + labels: + - "dependencies" + - "go" + commit-message: + prefix: "chore(deps)" + include: "scope" + + # Enable version updates for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + reviewers: + - "Genaker" + labels: + - "dependencies" + - "github-actions" + commit-message: + prefix: "chore(deps)" + include: "scope" + + # Enable version updates for Docker + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + reviewers: + - "Genaker" + labels: + - "dependencies" + - "docker" + commit-message: + prefix: "chore(deps)" + include: "scope" diff --git a/README.md b/README.md index d5256c2..80e5f5e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ # Magento Go API and Frontend +[![Go Version](https://img.shields.io/github/go-mod/go-version/Genaker/GoGento)](https://golang.org/dl/) +[![Go Report Card](https://goreportcard.com/badge/github.com/Genaker/GoGento)](https://goreportcard.com/report/github.com/Genaker/GoGento) +[![CI](https://github.com/Genaker/GoGento/actions/workflows/ci.yml/badge.svg)](https://github.com/Genaker/GoGento/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![GitHub issues](https://img.shields.io/github/issues/Genaker/GoGento)](https://github.com/Genaker/GoGento/issues) +[![GitHub stars](https://img.shields.io/github/stars/Genaker/GoGento)](https://github.com/Genaker/GoGento/stargazers) + A fully functional REST API and HTTP server for Magento using Go, Echo, and GORM. ## The world’s fastest framework for building e-Commerce MAGENTO websites! @@ -15,6 +22,52 @@ A fully functional REST API and HTTP server for Magento using Go, Echo, and GORM - **Concurrent-safe global product cache for fast flat product queries** - **Flexible product API: fetch all or specific products, with EAV attributes flattened** +## Quick Start + +### Using Docker (Recommended) + +```bash +# Clone the repository +git clone https://github.com/Genaker/GoGento.git +cd GoGento + +# Start all services (MySQL, Redis, and the application) +make docker-up +# or +docker-compose up -d + +# View logs +docker-compose logs -f app +``` + +The API will be available at `http://localhost:8080` + +### Using Make (Local Development) + +```bash +# Install dependencies +make deps + +# Copy environment file and configure +cp .env.example .env +# Edit .env with your database credentials + +# Run the server +make run +``` + +### Manual Setup + +See the detailed installation instructions below for manual setup without Docker or Make. + +## Contributing + +We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. + +## Security + +For reporting security vulnerabilities, please see [SECURITY.md](SECURITY.md). + ## Directory Structure ``` magento.GO/ diff --git a/core/cache/cache_test.go b/core/cache/cache_test.go new file mode 100644 index 0000000..491c0d1 --- /dev/null +++ b/core/cache/cache_test.go @@ -0,0 +1,159 @@ +package cache + +import ( + "testing" + "time" +) + +func TestCache_SetAndGet(t *testing.T) { + c := NewCache() + + tests := []struct { + name string + key string + value interface{} + ttl int64 + tags []string + checkVal func(interface{}) bool + }{ + { + name: "string value with no expiration", + key: "test_key", + value: "test_value", + ttl: 0, + tags: nil, + checkVal: func(v interface{}) bool { + return v == "test_value" + }, + }, + { + name: "integer value with tags", + key: "test_int", + value: 42, + ttl: 0, + tags: []string{"numbers", "test"}, + checkVal: func(v interface{}) bool { + return v == 42 + }, + }, + { + name: "struct value", + key: "test_struct", + value: map[string]interface{}{"name": "test", "count": 10}, + ttl: 0, + tags: []string{"maps"}, + checkVal: func(v interface{}) bool { + m, ok := v.(map[string]interface{}) + if !ok { + return false + } + return m["name"] == "test" && m["count"] == 10 + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set the value + c.Set(tt.key, tt.value, tt.ttl, tt.tags) + + // Get the value + got, ok := c.Get(tt.key) + if !ok { + t.Errorf("Get() returned ok = false, want true") + return + } + + // Check value using custom checker + if !tt.checkVal(got) { + t.Errorf("Get() value check failed for %v", got) + } + }) + } +} + +func TestCache_GetNonExistent(t *testing.T) { + c := NewCache() + + _, ok := c.Get("non_existent_key") + if ok { + t.Errorf("Get() for non-existent key returned ok = true, want false") + } +} + +func TestCache_Delete(t *testing.T) { + c := NewCache() + + key := "test_delete" + value := "test_value" + + // Set a value + c.Set(key, value, 0, nil) + + // Verify it exists + _, ok := c.Get(key) + if !ok { + t.Fatalf("Get() returned ok = false after Set(), want true") + } + + // Delete the value + c.Delete(key) + + // Verify it's deleted + _, ok = c.Get(key) + if ok { + t.Errorf("Get() after Delete() returned ok = true, want false") + } +} + +func TestCache_Expiration(t *testing.T) { + c := NewCache() + + key := "test_expire" + value := "test_value" + ttl := int64(1) // 1 second + + // Set a value with short TTL + c.Set(key, value, ttl, nil) + + // Immediately get the value - should exist + got, ok := c.Get(key) + if !ok { + t.Fatalf("Get() immediately after Set() returned ok = false, want true") + } + if got != value { + t.Errorf("Get() = %v, want %v", got, value) + } + + // Wait for expiration + time.Sleep(1100 * time.Millisecond) + + // Try to get expired value - should not exist + _, ok = c.Get(key) + if ok { + t.Errorf("Get() after expiration returned ok = true, want false") + } +} + +func TestGetInstance(t *testing.T) { + // Get instance twice + inst1 := GetInstance() + inst2 := GetInstance() + + // Should be the same instance (singleton) + if inst1 != inst2 { + t.Errorf("GetInstance() should return the same instance (singleton pattern)") + } + + // Set a value using inst1 + inst1.Set("singleton_test_unique", "test_value", 0, nil) + + // Get the value using inst2 + val, ok := inst2.Get("singleton_test_unique") + if !ok { + t.Errorf("Value set on inst1 should be accessible from inst2") + } + if val != "test_value" { + t.Errorf("Got value %v, want 'test_value'", val) + } +} From f9099e640a29bc44f109e6e85af1accb0c1813c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 08:44:15 +0000 Subject: [PATCH 4/6] Fix GitHub Actions security permissions Co-authored-by: Genaker <9213670+Genaker@users.noreply.github.com> --- .github/workflows/ci.yml | 9 ++++ .github/workflows/security.yml | 9 ++++ go.mod | 8 +-- go.sum | 90 ++++++++++++++++++++++++++-------- 4 files changed, 91 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2888146..ddb6324 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,10 +6,15 @@ on: pull_request: branches: [ main, master, develop ] +permissions: + contents: read + jobs: test: name: Test runs-on: ubuntu-latest + permissions: + contents: read services: mysql: @@ -86,6 +91,8 @@ jobs: build: name: Build runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Check out code @@ -106,6 +113,8 @@ jobs: lint: name: Lint runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Check out code diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 5b9c79a..d097729 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -8,10 +8,17 @@ on: schedule: - cron: '0 0 * * 0' # Run weekly on Sundays +permissions: + contents: read + security-events: write + jobs: gosec: name: Security Scan (gosec) runs-on: ubuntu-latest + permissions: + contents: read + security-events: write steps: - name: Check out code @@ -36,6 +43,8 @@ jobs: dependency-check: name: Dependency Vulnerability Scan runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Check out code diff --git a/go.mod b/go.mod index 7c67869..63d2822 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,13 @@ toolchain go1.24.3 require ( github.com/chai2010/webp v1.4.0 github.com/disintegration/imaging v1.6.2 + github.com/golang-migrate/migrate/v4 v4.18.3 github.com/joho/godotenv v1.5.1 github.com/labstack/echo/v4 v4.11.4 github.com/redis/go-redis/v9 v9.8.0 + github.com/robfig/cron/v3 v3.0.1 + github.com/spf13/cobra v1.9.1 + gorm.io/datatypes v1.2.5 gorm.io/driver/mysql v1.5.6 gorm.io/gorm v1.25.11 ) @@ -20,7 +24,6 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang-migrate/migrate/v4 v4.18.3 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -30,8 +33,6 @@ require ( github.com/labstack/gommon v0.4.2 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect @@ -42,5 +43,4 @@ require ( golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect golang.org/x/time v0.5.0 // indirect - gorm.io/datatypes v1.2.5 // indirect ) diff --git a/go.sum b/go.sum index 8abbfd0..fadeeac 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -14,16 +18,37 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dhui/dktest v0.4.5 h1:uUfYBIVREmj/Rw6MvgmqNAYzTiKOHJak+enB5Di73MM= +github.com/dhui/dktest v0.4.5/go.mod h1:tmcyeHDKagvlDrz7gDKq4UAJOLIfVZYkfD5OnHDwcCo= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4= +github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs= github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -33,6 +58,14 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -43,11 +76,29 @@ github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zG github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= +github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= @@ -61,42 +112,37 @@ github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -107,12 +153,14 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/datatypes v1.2.5 h1:9UogU3jkydFVW1bIVVeoYsTpLRgwDVW3rHfJG6/Ek9I= gorm.io/datatypes v1.2.5/go.mod h1:I5FUdlKpLb5PMqeMQhm30CQ6jXP8Rj89xkTeCSAaAD4= -gorm.io/driver/mysql v1.5.0 h1:6hSAT5QcyIaty0jfnff0z0CLDjyRgZ8mlMHLqSt7uXM= -gorm.io/driver/mysql v1.5.0/go.mod h1:FFla/fJuCvyTi7rJQd27qlNX2v3L6deTR1GgTjSOLPo= gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= -gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= -gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= +gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= +gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= +gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= +gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= +gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g= +gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= From 89b80e559715d0db9414f9c265479f48b33d2b4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 08:45:52 +0000 Subject: [PATCH 5/6] Update CI to handle two main packages correctly Co-authored-by: Genaker <9213670+Genaker@users.noreply.github.com> --- .github/workflows/ci.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddb6324..fb45bb2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,10 +67,21 @@ jobs: fi - name: Run go vet - run: go vet ./... + run: | + # vet individual packages since we have two main packages + go vet ./api/... + go vet ./cmd/... + go vet ./config/... + go vet ./core/... + go vet ./cron/... + go vet ./html/... + go vet ./model/... + go vet ./service/... - name: Run tests - run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./... + run: | + # Test individual packages since we have two main packages + go test -v -race -coverprofile=coverage.txt -covermode=atomic ./api/... ./cmd/... ./config/... ./core/... ./cron/... ./html/... ./model/... ./service/... env: MYSQL_USER: magento MYSQL_PASS: magento From 71806b9fd993a0fde00ed093dd40bd6062b71cb3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 07:42:25 +0000 Subject: [PATCH 6/6] Fix Go formatting issues across all packages Co-authored-by: Genaker <9213670+Genaker@users.noreply.github.com> --- api/category/category_api.go | 12 ++-- api/product/product_api.go | 16 ++--- api/sales/sales_order_grid_api.go | 4 +- cmd/cron.go | 6 +- cmd/migrate.go | 10 +-- cmd/product_json.go | 38 +++++------ cmd/root.go | 2 +- config/api.go | 2 +- config/app.go | 14 ++-- config/db.go | 28 ++++---- config/env.go | 4 +- config/redis.go | 5 +- core/log/log.go | 8 +-- core/registry/registry.go | 2 +- cron/jobs/product_json.go | 7 +- cron/jobs/test.go | 20 +++--- cron/sheduler.go | 4 +- html/category.go | 34 +++++----- html/hello-world.go | 26 +++---- html/image.go | 24 +++---- html/parts/critical_css.go | 2 +- html/product.go | 23 +++---- html/template.go | 14 ++-- magento.go | 44 ++++++------ model/entity/category/category.go | 32 ++++----- model/entity/category/category_int.go | 2 +- model/entity/category/category_product.go | 12 ++-- model/entity/category/category_text.go | 2 +- model/entity/category/category_varchar.go | 2 +- model/entity/eav_attribute.go | 38 +++++------ model/entity/flag.go | 16 ++--- model/entity/product/product.go | 42 ++++++------ model/entity/product/product_attribute.go | 14 ++-- .../product/product_attribute_decimal.go | 14 ++-- .../entity/product/product_attribute_text.go | 14 ++-- model/entity/product/product_datetime.go | 14 ++-- model/entity/product/product_decimal.go | 14 ++-- model/entity/product/product_gallery.go | 26 +++---- model/entity/product/product_index_price.go | 20 +++--- model/entity/product/product_int.go | 14 ++-- model/entity/product/product_json.go | 8 +-- model/entity/product/product_link.go | 12 ++-- model/entity/product/product_media_gallery.go | 14 ++-- model/entity/product/product_text.go | 14 ++-- model/entity/product/product_varchar.go | 14 ++-- model/entity/product/stock_item.go | 54 +++++++-------- model/entity/sales/sales_order_grid.go | 68 +++++++++---------- .../category/category_repository.go | 39 +++++------ .../repository/product/product_repository.go | 32 ++++----- .../sales/sales_order_grid_repository.go | 4 +- service/product/product_service.go | 2 +- service/sales/sales_order_grid_service.go | 2 +- 52 files changed, 440 insertions(+), 448 deletions(-) diff --git a/api/category/category_api.go b/api/category/category_api.go index c7400ad..ae44fad 100644 --- a/api/category/category_api.go +++ b/api/category/category_api.go @@ -1,12 +1,12 @@ package category import ( - "net/http" "github.com/labstack/echo/v4" - repo "magento.GO/model/repository/category" "gorm.io/gorm" - "strconv" categoryEntity "magento.GO/model/entity/category" + repo "magento.GO/model/repository/category" + "net/http" + "strconv" "strings" ) @@ -31,7 +31,7 @@ func RegisterCategoryAPI(g *echo.Group, db *gorm.DB) { } return c.JSON(http.StatusOK, map[string]interface{}{ "categories": categories, - "total": len(categories), + "total": len(categories), }) } g.GET("/categories", fullHandler) @@ -57,7 +57,6 @@ func RegisterCategoryAPI(g *echo.Group, db *gorm.DB) { return c.JSON(http.StatusOK, cat) }) - g.GET("/category/:ids/flat", func(c echo.Context) error { storeID := uint16(0) if sid := c.QueryParam("store_id"); sid != "" { @@ -152,7 +151,6 @@ func RegisterCategoryAPI(g *echo.Group, db *gorm.DB) { }) } - /* Usage Example (in your main or route setup): import ( @@ -167,4 +165,4 @@ func main() { categoryapi.RegisterCategoryAPI(e, db) e.Start(":8080") } -*/ \ No newline at end of file +*/ diff --git a/api/product/product_api.go b/api/product/product_api.go index c70ac48..9420ab3 100644 --- a/api/product/product_api.go +++ b/api/product/product_api.go @@ -4,8 +4,8 @@ import ( "net/http" //"os" "strconv" - "time" "strings" + "time" "github.com/labstack/echo/v4" //"github.com/labstack/echo/v4/middleware" @@ -26,8 +26,8 @@ func flatProductsHandler(repo *productRepository.ProductRepository) echo.Handler } c.Response().Header().Set("X-Request-Duration-ms", strconv.FormatInt(duration, 10)) return c.JSON(http.StatusOK, echo.Map{ - "products": flatProducts, - "count": len(flatProducts), + "products": flatProducts, + "count": len(flatProducts), "request_duration_ms": duration, }) } @@ -47,8 +47,8 @@ func RegisterProductRoutes(api *echo.Group, db *gorm.DB) { } c.Response().Header().Set("X-Request-Duration-ms", strconv.FormatInt(duration, 10)) return c.JSON(http.StatusOK, echo.Map{ - "products": products, - "count": len(products), + "products": products, + "count": len(products), "request_duration_ms": duration, }) }) @@ -154,10 +154,10 @@ func RegisterProductRoutes(api *echo.Group, db *gorm.DB) { c.Response().Header().Set("X-Request-Duration-ms", strconv.FormatInt(duration, 10)) return c.JSON(http.StatusOK, echo.Map{ - "products": result, - "count": len(result), + "products": result, + "count": len(result), "request_duration_ms": duration, }) }) -} \ No newline at end of file +} diff --git a/api/sales/sales_order_grid_api.go b/api/sales/sales_order_grid_api.go index 2409e9a..1b819d5 100644 --- a/api/sales/sales_order_grid_api.go +++ b/api/sales/sales_order_grid_api.go @@ -11,8 +11,8 @@ import ( //"github.com/labstack/echo/v4/middleware" "gorm.io/gorm" - "magento.GO/model/entity/sales" "magento.GO/config" + "magento.GO/model/entity/sales" ) // RegisterSalesOrderGridRoutes registers the routes for SalesOrderGrid CRUD operations with basic auth @@ -120,4 +120,4 @@ PUT /api/orders/:id - Update order by ID DELETE /api/orders/:id - Delete order by ID See Echo routing docs: https://echo.labstack.com/docs/routing -*/ \ No newline at end of file +*/ diff --git a/cmd/cron.go b/cmd/cron.go index cf17c49..d148b89 100644 --- a/cmd/cron.go +++ b/cmd/cron.go @@ -2,12 +2,12 @@ package cmd import ( "fmt" + "magento.GO/cron" "os" "strings" - "magento.GO/cron" //"magento.GO/cron/jobs" - "magento.GO/config" "github.com/spf13/cobra" + "magento.GO/config" ) var jobName string @@ -37,4 +37,4 @@ var cronStartCmd = &cobra.Command{ func init() { cronStartCmd.Flags().StringVarP(&jobName, "job", "j", "", "Run a single cron job by name and exit") rootCmd.AddCommand(cronStartCmd) -} \ No newline at end of file +} diff --git a/cmd/migrate.go b/cmd/migrate.go index dcdaf22..5c99e99 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -28,20 +28,20 @@ var migrateCmd = &cobra.Command{ func runSQLMigrations() { dbURL := config.GetMigrationDSN() - + migrationsPath := filepath.Join("file://", config.GetBasePath(), "migrations") - + m, err := migrate.New(migrationsPath, dbURL) if err != nil { fmt.Printf("SQL migration initialization failed: %v\n", err) return } - + if err := m.Up(); err != nil && err != migrate.ErrNoChange { fmt.Printf("SQL migration failed: %v\n", err) return } - + fmt.Println("SQL migrations applied successfully") } @@ -63,7 +63,7 @@ func runGORMMigrations() { fmt.Printf("GORM AutoMigrate failed: %v\n", err) return } - + fmt.Println("GORM model migrations completed") } diff --git a/cmd/product_json.go b/cmd/product_json.go index 6b96814..54d8669 100644 --- a/cmd/product_json.go +++ b/cmd/product_json.go @@ -3,13 +3,13 @@ package cmd import ( "encoding/json" "fmt" - "magento.GO/config" - productRepo "magento.GO/model/repository/product" - "magento.GO/model/entity/product" - "time" "github.com/spf13/cobra" "gorm.io/gorm" "gorm.io/gorm/clause" + "magento.GO/config" + "magento.GO/model/entity/product" + productRepo "magento.GO/model/repository/product" + "time" ) var migrateProductsCmd = &cobra.Command{ @@ -17,7 +17,7 @@ var migrateProductsCmd = &cobra.Command{ Short: "Migrate product data to JSON table with timing metrics", Run: func(cmd *cobra.Command, args []string) { startTotal := time.Now() - + db, err := config.NewDB() if err != nil { fmt.Printf("Database connection failed: %v\n", err) @@ -34,7 +34,6 @@ var migrateProductsCmd = &cobra.Command{ } fetchDuration := time.Since(startFetch) - startFetchJson := time.Now() var jsonProducts []product.ProductJson // Create map with composite keys @@ -43,10 +42,10 @@ var migrateProductsCmd = &cobra.Command{ if err != nil { fmt.Printf("Failed to fetch products: %v\n", err) return - }// Populate the map + } // Populate the map for index, entry := range jsonProducts { key := fmt.Sprintf("%d_%d", entry.EntityID, entry.StoreID) - existingEntries[key] = index + existingEntries[key] = index } fmt.Printf("Found %d existing product JSON entries\n", len(jsonProducts)) fetchJsonDuration := time.Since(startFetchJson) @@ -63,8 +62,7 @@ var migrateProductsCmd = &cobra.Command{ // Data processing timing processStart := time.Now() - fullData := map[string]interface{}{ - } + fullData := map[string]interface{}{} for k, v := range attributes { fullData[k] = v } @@ -86,7 +84,7 @@ var migrateProductsCmd = &cobra.Command{ existing.Attributes = jsonData existing.UpdatedAt = time.Now() updateBatch = append(updateBatch, existing) - + // Batch update when threshold reached if len(updateBatch) >= batchSize { fmt.Printf("Processing update batch of %d items\n", len(updateBatch)) @@ -98,11 +96,11 @@ var migrateProductsCmd = &cobra.Command{ } else { // Collect inserts insertBatch = append(insertBatch, product.ProductJson{ - EntityID: productID, - StoreID: 0, + EntityID: productID, + StoreID: 0, Attributes: jsonData, }) - + // Batch insert when threshold reached if len(insertBatch) >= batchSize { fmt.Printf("Processing insert batch of %d items\n", len(insertBatch)) @@ -132,7 +130,7 @@ var migrateProductsCmd = &cobra.Command{ } totalDuration := time.Since(startTotal) - + fmt.Printf(` === Indexing Report === Total products: %d @@ -147,7 +145,7 @@ Total time: %s totalDuration.Round(time.Millisecond), fetchDuration.Round(time.Millisecond), fetchJsonDuration.Round(time.Millisecond), - (totalProcessing/time.Duration(len(flatProducts))).Round(time.Microsecond), + (totalProcessing / time.Duration(len(flatProducts))).Round(time.Microsecond), totalDB.Round(time.Millisecond)) }, } @@ -161,7 +159,7 @@ func bulkInsert(db *gorm.DB, batch []product.ProductJson, batchSize int) error { defer func() { fmt.Printf("Inserted batch of %d items in %s\n", len(batch), time.Since(start)) }() - + result := db.CreateInBatches(batch, batchSize) if result.Error != nil { return result.Error @@ -173,7 +171,7 @@ func bulkInsert(db *gorm.DB, batch []product.ProductJson, batchSize int) error { func bulkUpdate(db *gorm.DB, batch []product.ProductJson, batchSize int) error { start := time.Now() totalUpdated := int64(0) - + err := db.Transaction(func(tx *gorm.DB) error { for i := 0; i < len(batch); i += batchSize { end := i + batchSize @@ -181,7 +179,7 @@ func bulkUpdate(db *gorm.DB, batch []product.ProductJson, batchSize int) error { end = len(batch) } chunk := batch[i:end] - + // Create a slice of update parameters updates := make([]map[string]interface{}, len(chunk)) for i, item := range chunk { @@ -207,7 +205,7 @@ func bulkUpdate(db *gorm.DB, batch []product.ProductJson, batchSize int) error { } return nil }) - + fmt.Printf("Updated %d records in %s\n", totalUpdated, time.Since(start)) return err } diff --git a/cmd/root.go b/cmd/root.go index 1935beb..d8ccbbf 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,8 +2,8 @@ package cmd import ( "fmt" - "os" "github.com/spf13/cobra" + "os" ) var rootCmd = &cobra.Command{ diff --git a/config/api.go b/config/api.go index 262fd40..4a3a4d0 100644 --- a/config/api.go +++ b/config/api.go @@ -4,4 +4,4 @@ package config func GetAuthSkipperPaths() []string { //Public API paths return []string{"/api/products", "/api/products/:id"} -} \ No newline at end of file +} diff --git a/config/app.go b/config/app.go index efc4632..c978165 100644 --- a/config/app.go +++ b/config/app.go @@ -2,9 +2,9 @@ package config import ( "os" - "sync" - "runtime" "path/filepath" + "runtime" + "sync" ) // AppConfig holds global application configuration @@ -12,11 +12,11 @@ var AppConfig *Config var once sync.Once type Config struct { - AppName string - Port string - Env string - Debug bool - MediaUrl string + AppName string + Port string + Env string + Debug bool + MediaUrl string // Add more fields as needed } diff --git a/config/db.go b/config/db.go index ae50a9b..fd59a95 100644 --- a/config/db.go +++ b/config/db.go @@ -2,11 +2,11 @@ package config import ( "fmt" - "os" - "log" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" + "log" + "os" "time" ) @@ -31,7 +31,7 @@ func NewDB() (*gorm.DB, error) { ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ - Logger: gormLogger, + Logger: gormLogger, PrepareStmt: true, // Enable prepared statements }) if err != nil { @@ -39,17 +39,17 @@ func NewDB() (*gorm.DB, error) { } // Get generic database object - sqlDB, err := db.DB() - if err != nil { - return nil, err - } + sqlDB, err := db.DB() + if err != nil { + return nil, err + } + + // Configure connection pool + sqlDB.SetMaxOpenConns(25) // Maximum open connections + sqlDB.SetMaxIdleConns(25) // Maximum idle connections + sqlDB.SetConnMaxLifetime(5 * time.Minute) // Maximum connection lifetime + sqlDB.SetConnMaxIdleTime(2 * time.Minute) // Maximum idle time - // Configure connection pool - sqlDB.SetMaxOpenConns(25) // Maximum open connections - sqlDB.SetMaxIdleConns(25) // Maximum idle connections - sqlDB.SetConnMaxLifetime(5 * time.Minute) // Maximum connection lifetime - sqlDB.SetConnMaxIdleTime(2 * time.Minute) // Maximum idle time - return db, nil } @@ -73,4 +73,4 @@ func GetMigrationDSN() string { os.Getenv("MYSQL_PORT"), os.Getenv("MYSQL_DB"), ) -} \ No newline at end of file +} diff --git a/config/env.go b/config/env.go index 294de82..0b5f49b 100644 --- a/config/env.go +++ b/config/env.go @@ -1,12 +1,12 @@ package config import ( - "log" "github.com/joho/godotenv" + "log" ) func LoadEnv() { _ = godotenv.Load() // If .env is missing, ignore error (env vars can be set by other means) log.Println("Environment variables loaded (if .env present)") -} \ No newline at end of file +} diff --git a/config/redis.go b/config/redis.go index a279bd1..2ec8bc3 100644 --- a/config/redis.go +++ b/config/redis.go @@ -2,12 +2,13 @@ package config import ( "context" - "os" "github.com/redis/go-redis/v9" + "os" ) // RedisClient is a global Redis client instance var RedisClient *redis.Client + //Accessed as config.RedisClient in other files func InitRedis() { @@ -25,4 +26,4 @@ func InitRedis() { func RedisCtx() context.Context { return context.Background() -} \ No newline at end of file +} diff --git a/core/log/log.go b/core/log/log.go index aa40025..969fab6 100644 --- a/core/log/log.go +++ b/core/log/log.go @@ -18,9 +18,9 @@ const ( ) var ( - logFile *os.File - logger *log.Logger - once sync.Once + logFile *os.File + logger *log.Logger + once sync.Once ) // Init initializes the logger and opens the log file @@ -69,4 +69,4 @@ func logWithLevel(level LogLevel, format string, v ...interface{}) { func Info(format string, v ...interface{}) { logWithLevel(INFO, format, v...) } func Warn(format string, v ...interface{}) { logWithLevel(WARN, format, v...) } func Error(format string, v ...interface{}) { logWithLevel(ERROR, format, v...) } -func Fatal(format string, v ...interface{}) { logWithLevel(FATAL, format, v...) } \ No newline at end of file +func Fatal(format string, v ...interface{}) { logWithLevel(FATAL, format, v...) } diff --git a/core/registry/registry.go b/core/registry/registry.go index 3e1eb1a..f5ff5f1 100644 --- a/core/registry/registry.go +++ b/core/registry/registry.go @@ -93,4 +93,4 @@ userID, ok := reqReg.Get("user_id") // Delete a request value reqReg.Delete("user_id") -*/ \ No newline at end of file +*/ diff --git a/cron/jobs/product_json.go b/cron/jobs/product_json.go index b6b9845..cdca89b 100644 --- a/cron/jobs/product_json.go +++ b/cron/jobs/product_json.go @@ -6,8 +6,7 @@ import ( ) func ProductJsonJob(params ...string) { - fmt.Println("Running ProductJsonJob at", time.Now()) - fmt.Println("Params:", params) - // Your job logic here + fmt.Println("Running ProductJsonJob at", time.Now()) + fmt.Println("Params:", params) + // Your job logic here } - diff --git a/cron/jobs/test.go b/cron/jobs/test.go index 8ce01db..e1d233b 100644 --- a/cron/jobs/test.go +++ b/cron/jobs/test.go @@ -7,18 +7,18 @@ import ( func TestJob(params ...string) { elapsed, b := testGo() - fmt.Printf("[TestJob] Loop completed in %.6f seconds. Last b=%d\n", elapsed, b) - fmt.Println("Params:", params) + fmt.Printf("[TestJob] Loop completed in %.6f seconds. Last b=%d\n", elapsed, b) + fmt.Println("Params:", params) } -func testGo() (float64, int){ +func testGo() (float64, int) { start := time.Now() var b int - // Start of the code to profile - for a := 0; a < 10000000; a++ { - b = (a * a) // Use blank identifier to ignore result - } - // End of the code to profile + // Start of the code to profile + for a := 0; a < 10000000; a++ { + b = (a * a) // Use blank identifier to ignore result + } + // End of the code to profile time := time.Since(start).Seconds() - return time, b -} \ No newline at end of file + return time, b +} diff --git a/cron/sheduler.go b/cron/sheduler.go index a58d489..4713fa5 100644 --- a/cron/sheduler.go +++ b/cron/sheduler.go @@ -2,8 +2,8 @@ package cron import ( "github.com/robfig/cron/v3" - "magento.GO/config" "log" + "magento.GO/config" ) func StartCron() *cron.Cron { @@ -18,4 +18,4 @@ func StartCron() *cron.Cron { } c.Start() return c -} \ No newline at end of file +} diff --git a/html/category.go b/html/category.go index c191928..fc434d7 100644 --- a/html/category.go +++ b/html/category.go @@ -1,18 +1,18 @@ package html import ( - "net/http" - "strconv" + "fmt" "github.com/labstack/echo/v4" "gorm.io/gorm" - categoryRepo "magento.GO/model/repository/category" "html/template" "log" + "magento.GO/config" parts "magento.GO/html/parts" + categoryRepo "magento.GO/model/repository/category" productRepo "magento.GO/model/repository/product" - "magento.GO/config" + "net/http" + "strconv" "time" - "fmt" ) // PaginationData holds all pagination-related information @@ -97,7 +97,7 @@ func calculatePagination(c echo.Context, totalItems int) PaginationData { func RegisterCategoryHTMLRoutes(e *echo.Echo, db *gorm.DB) { repo := categoryRepo.GetCategoryRepository(db) prodRepo := productRepo.GetProductRepository(db) - + e.GET("/category/:id", func(c echo.Context) error { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) @@ -185,17 +185,17 @@ func RegisterCategoryHTMLRoutes(e *echo.Echo, db *gorm.DB) { return c.Render(http.StatusOK, "parts/category_layout.html", map[string]interface{}{ "Category": cat, "Attributes": flat, - "Title": title, - "Products": products, - "CriticalCSS": template.CSS(criticalCSS), + "Title": title, + "Products": products, + "CriticalCSS": template.CSS(criticalCSS), "CategoryTreeHTML": template.HTML(categoryTreeHTML), - "MediaUrl": config.AppConfig.MediaUrl, - "Page": pagination.Page, - "TotalPages": pagination.TotalPages, - "Limit": pagination.Limit, - "PageNumbers": pagination.PageNumbers, - "PrevPage": pagination.PrevPage, - "NextPage": pagination.NextPage, + "MediaUrl": config.AppConfig.MediaUrl, + "Page": pagination.Page, + "TotalPages": pagination.TotalPages, + "Limit": pagination.Limit, + "PageNumbers": pagination.PageNumbers, + "PrevPage": pagination.PrevPage, + "NextPage": pagination.NextPage, }) }) -} \ No newline at end of file +} diff --git a/html/hello-world.go b/html/hello-world.go index 86c4524..b90e4b4 100644 --- a/html/hello-world.go +++ b/html/hello-world.go @@ -1,16 +1,16 @@ package html import ( - "net/http" - "time" - "github.com/labstack/echo/v4" "fmt" + "github.com/labstack/echo/v4" "html/template" + "net/http" + "time" ) var ( TemplateCompileTime time.Duration - Templates *template.Template + Templates *template.Template ) func initTemplates() { @@ -25,15 +25,17 @@ func initTemplates() { // RegisterHelloWorldRoute registers the /hello-world route func RegisterHelloWorldRoute(e *echo.Echo) { - initTemplates(); + initTemplates() e.GET("/hello-world", func(c echo.Context) error { - + // Retrieve the request registry from context reqRegIface := c.Get("RequestRegistry") var start time.Time var showTime bool if reqRegIface != nil { - if reqReg, ok := reqRegIface.(interface{ Get(string) (interface{}, bool) }); ok { + if reqReg, ok := reqRegIface.(interface { + Get(string) (interface{}, bool) + }); ok { if v, found := reqReg.Get("request_start"); found { if t, ok := v.(time.Time); ok { start = t @@ -53,12 +55,12 @@ func RegisterHelloWorldRoute(e *echo.Echo) { execTimeMs = "" } data := map[string]interface{}{ - "Message": "Hello World", - "ExecutionTime": execTime, - "ExecutionTimeMs": execTimeMs, - "TemplateCompileTime": TemplateCompileTime.String(), + "Message": "Hello World", + "ExecutionTime": execTime, + "ExecutionTimeMs": execTimeMs, + "TemplateCompileTime": TemplateCompileTime.String(), "TemplateCompileTimeMs": fmt.Sprintf("%.10f ms", float64(TemplateCompileTime.Nanoseconds())/1e6), } return c.Render(http.StatusOK, "hello_world.html", data) }) -} \ No newline at end of file +} diff --git a/html/image.go b/html/image.go index f490629..7002592 100644 --- a/html/image.go +++ b/html/image.go @@ -1,18 +1,18 @@ package html import ( - "net/http" - "strconv" - "os" - "image" - "strings" "encoding/base64" "fmt" + "github.com/chai2010/webp" + "github.com/disintegration/imaging" + "github.com/labstack/echo/v4" + "image" "io" + "net/http" + "os" "path/filepath" - "github.com/labstack/echo/v4" - "github.com/disintegration/imaging" - "github.com/chai2010/webp" + "strconv" + "strings" ) // RegisterImageRoutes registers image-related routes such as /image/webp @@ -124,7 +124,7 @@ func RegisterImageRoutes(e *echo.Echo) { ratioW := float64(width) / float64(origW) ratioH := float64(height) / float64(origH) var resizeW, resizeH int - + if ratioW < ratioH { // Width is the constraining factor resizeW = width @@ -176,8 +176,8 @@ func RegisterImageRoutes(e *echo.Echo) { case "webp": c.Response().Header().Set("Content-Type", "image/webp") opts := &webp.Options{ - Quality: float32(quality), - Exact: true, // Preserve color accuracy + Quality: float32(quality), + Exact: true, // Preserve color accuracy Lossless: true, // Use lossless compression for best quality } webp.Encode(io.MultiWriter(c.Response(), f), img, opts) @@ -187,4 +187,4 @@ func RegisterImageRoutes(e *echo.Echo) { } return nil }) -} \ No newline at end of file +} diff --git a/html/parts/critical_css.go b/html/parts/critical_css.go index 2356cde..7a39c83 100644 --- a/html/parts/critical_css.go +++ b/html/parts/critical_css.go @@ -1,8 +1,8 @@ package parts import ( - "os" "log" + "os" "sync" ) diff --git a/html/product.go b/html/product.go index c72efc7..703dc39 100644 --- a/html/product.go +++ b/html/product.go @@ -1,20 +1,20 @@ package html import ( - "net/http" - "strconv" "github.com/labstack/echo/v4" "gorm.io/gorm" - productRepo "magento.GO/model/repository/product" "html/template" parts "magento.GO/html/parts" + productRepo "magento.GO/model/repository/product" + "net/http" + "strconv" //"io" + "bytes" "log" "magento.GO/config" - "strings" categoryRepo "magento.GO/model/repository/category" + "strings" "sync" - "bytes" "time" ) @@ -129,7 +129,6 @@ func getLastCategoryID(idsVal interface{}) (uint, bool) { func RegisterProductHTMLRoutes(e *echo.Echo, db *gorm.DB) { repo := productRepo.GetProductRepository(db) catRepo := categoryRepo.GetCategoryRepository(db) - e.GET("/product/:ids", func(c echo.Context) error { idsParam := c.Param("ids") @@ -158,7 +157,7 @@ func RegisterProductHTMLRoutes(e *echo.Echo, db *gorm.DB) { if prod, ok := flatProducts[id]; ok { // Add breadcrumbs if category_ids is present if idsVal, ok := prod["category_ids"]; ok { - + if lastCatID, ok := getLastCategoryID(idsVal); ok && lastCatID > 0 { cat, _, err := catRepo.GetByIDWithAttributesAndFlat(lastCatID, 0) if err == nil && cat != nil && cat.Path != "" { @@ -200,12 +199,11 @@ func RegisterProductHTMLRoutes(e *echo.Echo, db *gorm.DB) { criticalCSS = "" } - return c.Render(http.StatusOK, "products.html", map[string]interface{}{ - "Products": products, - "Title": "Product Page - " + products[0]["name"].(string) + " - " + products[0]["sku"].(string) + " - Magento.GO", - "CriticalCSS": template.CSS(criticalCSS), - "MediaUrl": config.AppConfig.MediaUrl, + "Products": products, + "Title": "Product Page - " + products[0]["name"].(string) + " - " + products[0]["sku"].(string) + " - Magento.GO", + "CriticalCSS": template.CSS(criticalCSS), + "MediaUrl": config.AppConfig.MediaUrl, "CategoryTreeHTML": template.HTML(categoryTreeHTML), }) }) @@ -213,4 +211,3 @@ func RegisterProductHTMLRoutes(e *echo.Echo, db *gorm.DB) { // Register image routes in a separate file RegisterImageRoutes(e) } - diff --git a/html/template.go b/html/template.go index 9173d38..b75f9d0 100644 --- a/html/template.go +++ b/html/template.go @@ -1,9 +1,9 @@ package html import ( + "github.com/labstack/echo/v4" "html/template" "io" - "github.com/labstack/echo/v4" ) // Template is the HTML template renderer @@ -37,13 +37,13 @@ var templateFuncs = template.FuncMap{ "add": func(a, b int) int { return a + b }, "sub": func(a, b int) int { return a - b }, "mul": func(a, b int) int { return a * b }, - "div": func(a, b int) int { + "div": func(a, b int) int { if b == 0 { return 0 } - return a / b + return a / b }, - + // Comparison helpers "eq": func(a, b interface{}) bool { return a == b }, "ne": func(a, b interface{}) bool { return a != b }, @@ -51,7 +51,7 @@ var templateFuncs = template.FuncMap{ "gt": func(a, b int) bool { return a > b }, "le": func(a, b int) bool { return a <= b }, "ge": func(a, b int) bool { return a >= b }, - + // Slice helpers "until": func(count int) []int { s := make([]int, count) @@ -71,7 +71,7 @@ var templateFuncs = template.FuncMap{ } return r }, - + // Map helper "dict": func(values ...interface{}) map[string]interface{} { if len(values)%2 != 0 { @@ -108,4 +108,4 @@ func NewTemplate() *Template { return &Template{ Templates: template.Must(template.New("").Funcs(templateFuncs).ParseGlob("html/parts/*.html")), } -} \ No newline at end of file +} diff --git a/magento.go b/magento.go index d637657..b541bc1 100644 --- a/magento.go +++ b/magento.go @@ -1,24 +1,24 @@ package main import ( - "log" - "os" - "time" - "strconv" - "html/template" "fmt" - "net/http" - "strings" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" - "magento.GO/config" - salesApi "magento.GO/api/sales" - productApi "magento.GO/api/product" + "html/template" + "log" categoryApi "magento.GO/api/category" - html "magento.GO/html" - "magento.GO/core/registry" + productApi "magento.GO/api/product" + salesApi "magento.GO/api/sales" + "magento.GO/config" "magento.GO/core/cache" corelog "magento.GO/core/log" + "magento.GO/core/registry" + html "magento.GO/html" + "net/http" + "os" + "strconv" + "strings" + "time" ) var GlobalRegistry = registry.NewRegistry() @@ -114,7 +114,7 @@ func main() { corelog.Info("Database connection successful.") e := echo.New() - + // Middleware to add cache control headers e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { @@ -135,28 +135,28 @@ func main() { e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { start := time.Now() - + // Wrap the response writer w := &responseWriterWithTiming{ ResponseWriter: c.Response().Writer, - start: start, + start: start, } c.Response().Writer = w - + err := next(c) - + // If headers haven't been written yet, write them now if !w.headerWritten { duration := time.Since(start) msWithPrecision := float64(duration.Microseconds()) / 1000.0 // Convert to ms with decimals - + w.Header().Set("X-Page-Generation-Time-ms", fmt.Sprintf("%.3f", msWithPrecision)) w.Header().Set("X-Page-Generation-Time-μs", strconv.FormatInt(duration.Microseconds(), 10)) w.Header().Set("X-Page-Generation-Time", duration.String()) w.Header().Set("Server-Timing", fmt.Sprintf("app;dur=%.3f;desc=\"Magento.GO Response Time\"", msWithPrecision)) w.headerWritten = true } - + return err } }) @@ -190,7 +190,7 @@ func main() { // Health check endpoint (no auth required) e.GET("/health", func(c echo.Context) error { return c.JSON(http.StatusOK, echo.Map{ - "status": "healthy", + "status": "healthy", "service": "GoGento", "version": "1.0.1", }) @@ -245,7 +245,7 @@ func (r *responseWriterWithTiming) WriteHeader(code int) { if !r.headerWritten { duration := time.Since(r.start) msWithPrecision := float64(duration.Microseconds()) / 1000.0 // Convert to ms with decimals - + r.Header().Set("X-Page-Generation-Time-ms", fmt.Sprintf("%.3f", msWithPrecision)) r.Header().Set("Server-Timing", fmt.Sprintf("app;dur=%.3f;desc=\"Magento.GO Response Time\"", msWithPrecision)) r.headerWritten = true @@ -258,4 +258,4 @@ func (r *responseWriterWithTiming) Write(b []byte) (int, error) { r.WriteHeader(http.StatusOK) } return r.ResponseWriter.Write(b) -} \ No newline at end of file +} diff --git a/model/entity/category/category.go b/model/entity/category/category.go index cb101c1..0936257 100644 --- a/model/entity/category/category.go +++ b/model/entity/category/category.go @@ -1,28 +1,28 @@ package category import ( - "time" + "time" ) type Category struct { - EntityID uint `gorm:"column:entity_id;primaryKey;autoIncrement"` - AttributeSetID uint16 `gorm:"column:attribute_set_id;type:smallint unsigned;not null;default:0"` - ParentID uint `gorm:"column:parent_id;type:int unsigned;not null;default:0"` - CreatedAt time.Time `gorm:"column:created_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoCreateTime"` - UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoUpdateTime"` - Path string `gorm:"column:path;type:varchar(255);not null"` - Position int `gorm:"column:position;not null"` - Level int `gorm:"column:level;not null;default:0"` - ChildrenCount int `gorm:"column:children_count;not null"` - Products []CategoryProduct `gorm:"foreignKey:CategoryID;references:EntityID"` - Ints []CategoryInt `gorm:"foreignKey:EntityID;references:EntityID"` - Varchars []CategoryVarchar `gorm:"foreignKey:EntityID;references:EntityID"` - Texts []CategoryText `gorm:"foreignKey:EntityID;references:EntityID"` + EntityID uint `gorm:"column:entity_id;primaryKey;autoIncrement"` + AttributeSetID uint16 `gorm:"column:attribute_set_id;type:smallint unsigned;not null;default:0"` + ParentID uint `gorm:"column:parent_id;type:int unsigned;not null;default:0"` + CreatedAt time.Time `gorm:"column:created_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoCreateTime"` + UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoUpdateTime"` + Path string `gorm:"column:path;type:varchar(255);not null"` + Position int `gorm:"column:position;not null"` + Level int `gorm:"column:level;not null;default:0"` + ChildrenCount int `gorm:"column:children_count;not null"` + Products []CategoryProduct `gorm:"foreignKey:CategoryID;references:EntityID"` + Ints []CategoryInt `gorm:"foreignKey:EntityID;references:EntityID"` + Varchars []CategoryVarchar `gorm:"foreignKey:EntityID;references:EntityID"` + Texts []CategoryText `gorm:"foreignKey:EntityID;references:EntityID"` } // TableName specifies the table name func (Category) TableName() string { - return "catalog_category_entity" + return "catalog_category_entity" } /* Usage Examples: @@ -52,4 +52,4 @@ func (Category) TableName() string { ```go db.Delete(&category) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/category/category_int.go b/model/entity/category/category_int.go index cc92785..4275dee 100644 --- a/model/entity/category/category_int.go +++ b/model/entity/category/category_int.go @@ -43,4 +43,4 @@ func (CategoryInt) TableName() string { ```go db.Delete(&attr) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/category/category_product.go b/model/entity/category/category_product.go index 0ef07f8..75f8c2b 100644 --- a/model/entity/category/category_product.go +++ b/model/entity/category/category_product.go @@ -1,15 +1,15 @@ package category type CategoryProduct struct { - EntityID uint `gorm:"column:entity_id;primaryKey;autoIncrement"` - CategoryID uint `gorm:"column:category_id;type:int unsigned;not null;default:0"` - ProductID uint `gorm:"column:product_id;type:int unsigned;not null;default:0"` - Position int `gorm:"column:position;not null;default:0"` + EntityID uint `gorm:"column:entity_id;primaryKey;autoIncrement"` + CategoryID uint `gorm:"column:category_id;type:int unsigned;not null;default:0"` + ProductID uint `gorm:"column:product_id;type:int unsigned;not null;default:0"` + Position int `gorm:"column:position;not null;default:0"` } // TableName specifies the table name func (CategoryProduct) TableName() string { - return "catalog_category_product" + return "catalog_category_product" } /* Usage Examples: @@ -39,4 +39,4 @@ func (CategoryProduct) TableName() string { ```go db.Delete(&catProd) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/category/category_text.go b/model/entity/category/category_text.go index be6a994..74742e6 100644 --- a/model/entity/category/category_text.go +++ b/model/entity/category/category_text.go @@ -43,4 +43,4 @@ func (CategoryText) TableName() string { ```go db.Delete(&attr) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/category/category_varchar.go b/model/entity/category/category_varchar.go index e4cbdc9..492e27f 100644 --- a/model/entity/category/category_varchar.go +++ b/model/entity/category/category_varchar.go @@ -43,4 +43,4 @@ func (CategoryVarchar) TableName() string { ```go db.Delete(&attr) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/eav_attribute.go b/model/entity/eav_attribute.go index d3a175b..f9bf1e0 100644 --- a/model/entity/eav_attribute.go +++ b/model/entity/eav_attribute.go @@ -1,27 +1,27 @@ package entity type EavAttribute struct { - AttributeID uint16 `gorm:"column:attribute_id;primaryKey;autoIncrement"` - EntityTypeID uint16 `gorm:"column:entity_type_id;type:smallint unsigned;not null;default:0"` - AttributeCode string `gorm:"column:attribute_code;type:varchar(255);not null"` - AttributeModel *string `gorm:"column:attribute_model;type:varchar(255)"` - BackendModel *string `gorm:"column:backend_model;type:varchar(255)"` - BackendType string `gorm:"column:backend_type;type:varchar(8);not null;default:static"` - BackendTable *string `gorm:"column:backend_table;type:varchar(255)"` - FrontendModel *string `gorm:"column:frontend_model;type:varchar(255)"` - FrontendInput *string `gorm:"column:frontend_input;type:varchar(50)"` - FrontendLabel *string `gorm:"column:frontend_label;type:varchar(255)"` - FrontendClass *string `gorm:"column:frontend_class;type:varchar(255)"` - SourceModel *string `gorm:"column:source_model;type:varchar(255)"` - IsRequired uint16 `gorm:"column:is_required;type:smallint unsigned;not null;default:0"` - IsUserDefined uint16 `gorm:"column:is_user_defined;type:smallint unsigned;not null;default:0"` - DefaultValue *string `gorm:"column:default_value;type:text"` - IsUnique uint16 `gorm:"column:is_unique;type:smallint unsigned;not null;default:0"` - Note *string `gorm:"column:note;type:varchar(255)"` + AttributeID uint16 `gorm:"column:attribute_id;primaryKey;autoIncrement"` + EntityTypeID uint16 `gorm:"column:entity_type_id;type:smallint unsigned;not null;default:0"` + AttributeCode string `gorm:"column:attribute_code;type:varchar(255);not null"` + AttributeModel *string `gorm:"column:attribute_model;type:varchar(255)"` + BackendModel *string `gorm:"column:backend_model;type:varchar(255)"` + BackendType string `gorm:"column:backend_type;type:varchar(8);not null;default:static"` + BackendTable *string `gorm:"column:backend_table;type:varchar(255)"` + FrontendModel *string `gorm:"column:frontend_model;type:varchar(255)"` + FrontendInput *string `gorm:"column:frontend_input;type:varchar(50)"` + FrontendLabel *string `gorm:"column:frontend_label;type:varchar(255)"` + FrontendClass *string `gorm:"column:frontend_class;type:varchar(255)"` + SourceModel *string `gorm:"column:source_model;type:varchar(255)"` + IsRequired uint16 `gorm:"column:is_required;type:smallint unsigned;not null;default:0"` + IsUserDefined uint16 `gorm:"column:is_user_defined;type:smallint unsigned;not null;default:0"` + DefaultValue *string `gorm:"column:default_value;type:text"` + IsUnique uint16 `gorm:"column:is_unique;type:smallint unsigned;not null;default:0"` + Note *string `gorm:"column:note;type:varchar(255)"` } func (EavAttribute) TableName() string { - return "eav_attribute" + return "eav_attribute" } /* Usage Examples: @@ -43,4 +43,4 @@ func (EavAttribute) TableName() string { 4. Delete: db.Delete(&attr) -*/ \ No newline at end of file +*/ diff --git a/model/entity/flag.go b/model/entity/flag.go index 839afdb..174d8b4 100644 --- a/model/entity/flag.go +++ b/model/entity/flag.go @@ -1,20 +1,20 @@ package entity import ( - "time" + "time" ) type Flag struct { - FlagID uint `gorm:"column:flag_id;primaryKey;autoIncrement"` - FlagCode string `gorm:"column:flag_code;type:varchar(255);not null"` - State uint16 `gorm:"column:state;type:smallint unsigned;not null;default:0"` - FlagData string `gorm:"column:flag_data;type:mediumtext"` - LastUpdate time.Time `gorm:"column:last_update;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoUpdateTime"` + FlagID uint `gorm:"column:flag_id;primaryKey;autoIncrement"` + FlagCode string `gorm:"column:flag_code;type:varchar(255);not null"` + State uint16 `gorm:"column:state;type:smallint unsigned;not null;default:0"` + FlagData string `gorm:"column:flag_data;type:mediumtext"` + LastUpdate time.Time `gorm:"column:last_update;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoUpdateTime"` } // TableName specifies the table name func (Flag) TableName() string { - return "flag" + return "flag" } /* Usage Examples: @@ -43,4 +43,4 @@ func (Flag) TableName() string { ```go db.Delete(&flag) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product.go b/model/entity/product/product.go index 623c854..daca88e 100644 --- a/model/entity/product/product.go +++ b/model/entity/product/product.go @@ -1,33 +1,33 @@ package product import ( - "time" - "magento.GO/model/entity/category" + "magento.GO/model/entity/category" + "time" ) type Product struct { - EntityID uint `gorm:"column:entity_id;primaryKey;autoIncrement"` - AttributeSetID uint16 `gorm:"column:attribute_set_id;type:smallint unsigned;not null;default:0"` - TypeID string `gorm:"column:type_id;type:varchar(32);not null;default:simple"` - SKU string `gorm:"column:sku;type:varchar(64);not null"` - HasOptions uint16 `gorm:"column:has_options;type:smallint;not null;default:0"` - RequiredOptions uint16 `gorm:"column:required_options;type:smallint unsigned;not null;default:0"` - CreatedAt time.Time `gorm:"column:created_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoCreateTime"` - UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoUpdateTime"` - Categories []category.Category `gorm:"many2many:catalog_category_product;joinForeignKey:ProductID;joinReferences:CategoryID"` - Varchars []ProductVarchar `gorm:"foreignKey:EntityID;references:EntityID"` - Ints []ProductInt `gorm:"foreignKey:EntityID;references:EntityID"` - Decimals []ProductDecimal `gorm:"foreignKey:EntityID;references:EntityID"` - Texts []ProductText `gorm:"foreignKey:EntityID;references:EntityID"` - Datetimes []ProductDatetime `gorm:"foreignKey:EntityID;references:EntityID"` - MediaGallery []ProductMediaGallery `gorm:"many2many:catalog_product_entity_media_gallery_value_to_entity;joinForeignKey:EntityID;joinReferences:ValueID"` - StockItem StockItem `gorm:"foreignKey:EntityID;references:ProductID"` - ProductIndexPrices []ProductIndexPrice `gorm:"foreignKey:EntityID;references:EntityID"` + EntityID uint `gorm:"column:entity_id;primaryKey;autoIncrement"` + AttributeSetID uint16 `gorm:"column:attribute_set_id;type:smallint unsigned;not null;default:0"` + TypeID string `gorm:"column:type_id;type:varchar(32);not null;default:simple"` + SKU string `gorm:"column:sku;type:varchar(64);not null"` + HasOptions uint16 `gorm:"column:has_options;type:smallint;not null;default:0"` + RequiredOptions uint16 `gorm:"column:required_options;type:smallint unsigned;not null;default:0"` + CreatedAt time.Time `gorm:"column:created_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoCreateTime"` + UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;autoUpdateTime"` + Categories []category.Category `gorm:"many2many:catalog_category_product;joinForeignKey:ProductID;joinReferences:CategoryID"` + Varchars []ProductVarchar `gorm:"foreignKey:EntityID;references:EntityID"` + Ints []ProductInt `gorm:"foreignKey:EntityID;references:EntityID"` + Decimals []ProductDecimal `gorm:"foreignKey:EntityID;references:EntityID"` + Texts []ProductText `gorm:"foreignKey:EntityID;references:EntityID"` + Datetimes []ProductDatetime `gorm:"foreignKey:EntityID;references:EntityID"` + MediaGallery []ProductMediaGallery `gorm:"many2many:catalog_product_entity_media_gallery_value_to_entity;joinForeignKey:EntityID;joinReferences:ValueID"` + StockItem StockItem `gorm:"foreignKey:EntityID;references:ProductID"` + ProductIndexPrices []ProductIndexPrice `gorm:"foreignKey:EntityID;references:EntityID"` } // TableName specifies the table name func (Product) TableName() string { - return "catalog_product_entity" + return "catalog_product_entity" } /* Usage Examples: @@ -57,4 +57,4 @@ func (Product) TableName() string { ```go db.Delete(&product) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_attribute.go b/model/entity/product/product_attribute.go index cf3e2fb..4ba45ce 100644 --- a/model/entity/product/product_attribute.go +++ b/model/entity/product/product_attribute.go @@ -1,16 +1,16 @@ package product type ProductAttributeInt struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` - StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` - EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` - Value int `gorm:"column:value"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` + StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` + EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` + Value int `gorm:"column:value"` } // TableName specifies the table name func (ProductAttributeInt) TableName() string { - return "catalog_product_entity_int" + return "catalog_product_entity_int" } /* Usage Examples: @@ -41,4 +41,4 @@ func (ProductAttributeInt) TableName() string { ```go db.Delete(&attrInt) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_attribute_decimal.go b/model/entity/product/product_attribute_decimal.go index e91b17e..02eee1d 100644 --- a/model/entity/product/product_attribute_decimal.go +++ b/model/entity/product/product_attribute_decimal.go @@ -1,16 +1,16 @@ package product type ProductAttributeDecimal struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` - StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` - EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` - Value float64 `gorm:"column:value;type:decimal(20,6)"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` + StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` + EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` + Value float64 `gorm:"column:value;type:decimal(20,6)"` } // TableName specifies the table name func (ProductAttributeDecimal) TableName() string { - return "catalog_product_entity_decimal" + return "catalog_product_entity_decimal" } /* Usage Examples: @@ -41,4 +41,4 @@ func (ProductAttributeDecimal) TableName() string { ```go db.Delete(&attrDecimal) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_attribute_text.go b/model/entity/product/product_attribute_text.go index 880a284..7cda234 100644 --- a/model/entity/product/product_attribute_text.go +++ b/model/entity/product/product_attribute_text.go @@ -1,16 +1,16 @@ package product type ProductAttributeText struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` - StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` - EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` - Value string `gorm:"column:value;type:mediumtext"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` + StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` + EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` + Value string `gorm:"column:value;type:mediumtext"` } // TableName specifies the table name func (ProductAttributeText) TableName() string { - return "catalog_product_entity_text" + return "catalog_product_entity_text" } /* Usage Examples: @@ -41,4 +41,4 @@ func (ProductAttributeText) TableName() string { ```go db.Delete(&attrText) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_datetime.go b/model/entity/product/product_datetime.go index b854050..1bbc55a 100644 --- a/model/entity/product/product_datetime.go +++ b/model/entity/product/product_datetime.go @@ -3,15 +3,15 @@ package product import "time" type ProductDatetime struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` - StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` - EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` - Value time.Time `gorm:"column:value"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` + StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` + EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` + Value time.Time `gorm:"column:value"` } func (ProductDatetime) TableName() string { - return "catalog_product_entity_datetime" + return "catalog_product_entity_datetime" } /* Usage Examples: @@ -25,4 +25,4 @@ func (ProductDatetime) TableName() string { db.Model(&attr).Update("Value", time.Now()) 4. Delete: db.Delete(&attr) -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_decimal.go b/model/entity/product/product_decimal.go index 521d033..cadec8d 100644 --- a/model/entity/product/product_decimal.go +++ b/model/entity/product/product_decimal.go @@ -1,15 +1,15 @@ package product type ProductDecimal struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` - StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` - EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` - Value float64 `gorm:"column:value"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` + StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` + EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` + Value float64 `gorm:"column:value"` } func (ProductDecimal) TableName() string { - return "catalog_product_entity_decimal" + return "catalog_product_entity_decimal" } /* Usage Examples: @@ -23,4 +23,4 @@ func (ProductDecimal) TableName() string { db.Model(&attr).Update("Value", 100.00) 4. Delete: db.Delete(&attr) -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_gallery.go b/model/entity/product/product_gallery.go index 06d429d..460fbb5 100644 --- a/model/entity/product/product_gallery.go +++ b/model/entity/product/product_gallery.go @@ -1,27 +1,27 @@ package product import ( - "time" "gorm.io/gorm" entity "magento.GO/model/entity" + "time" ) type ProductGallery struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;not null"` - StoreID uint16 `gorm:"column:store_id;not null"` - EntityID uint `gorm:"column:entity_id;not null"` - Position int `gorm:"column:position;not null;default:0"` - Value string `gorm:"column:value;type:varchar(255)"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;not null"` + StoreID uint16 `gorm:"column:store_id;not null"` + EntityID uint `gorm:"column:entity_id;not null"` + Position int `gorm:"column:position;not null;default:0"` + Value string `gorm:"column:value;type:varchar(255)"` // Relationships - Attribute entity.EavAttribute `gorm:"foreignKey:AttributeID;references:AttributeID"` - Product Product `gorm:"foreignKey:EntityID;references:EntityID"` + Attribute entity.EavAttribute `gorm:"foreignKey:AttributeID;references:AttributeID"` + Product Product `gorm:"foreignKey:EntityID;references:EntityID"` //Store entity.Store `gorm:"foreignKey:StoreID;references:StoreID"` - CreatedAt time.Time `gorm:"column:created_at"` - UpdatedAt time.Time `gorm:"column:updated_at"` - DeletedAt gorm.DeletedAt `gorm:"index"` + CreatedAt time.Time `gorm:"column:created_at"` + UpdatedAt time.Time `gorm:"column:updated_at"` + DeletedAt gorm.DeletedAt `gorm:"index"` } // TableName specifies the table name @@ -60,4 +60,4 @@ func (ProductGallery) TableName() string { ```go db.Delete(&gallery) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_index_price.go b/model/entity/product/product_index_price.go index f530509..f7f8a33 100644 --- a/model/entity/product/product_index_price.go +++ b/model/entity/product/product_index_price.go @@ -1,15 +1,15 @@ package product type ProductIndexPrice struct { - EntityID uint `gorm:"column:entity_id;primaryKey"` - CustomerGroupID uint `gorm:"column:customer_group_id;primaryKey"` - WebsiteID uint16 `gorm:"column:website_id;primaryKey"` - TaxClassID uint16 `gorm:"column:tax_class_id;default:0"` - Price float64 `gorm:"column:price"` - FinalPrice float64 `gorm:"column:final_price"` - MinPrice float64 `gorm:"column:min_price"` - MaxPrice float64 `gorm:"column:max_price"` - TierPrice float64 `gorm:"column:tier_price"` + EntityID uint `gorm:"column:entity_id;primaryKey"` + CustomerGroupID uint `gorm:"column:customer_group_id;primaryKey"` + WebsiteID uint16 `gorm:"column:website_id;primaryKey"` + TaxClassID uint16 `gorm:"column:tax_class_id;default:0"` + Price float64 `gorm:"column:price"` + FinalPrice float64 `gorm:"column:final_price"` + MinPrice float64 `gorm:"column:min_price"` + MaxPrice float64 `gorm:"column:max_price"` + TierPrice float64 `gorm:"column:tier_price"` } // TableName specifies the table name @@ -46,4 +46,4 @@ func (ProductIndexPrice) TableName() string { ```go db.Delete(&price) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_int.go b/model/entity/product/product_int.go index 035ae6e..9f5f9ec 100644 --- a/model/entity/product/product_int.go +++ b/model/entity/product/product_int.go @@ -1,15 +1,15 @@ package product type ProductInt struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` - StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` - EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` - Value int `gorm:"column:value"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` + StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` + EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` + Value int `gorm:"column:value"` } func (ProductInt) TableName() string { - return "catalog_product_entity_int" + return "catalog_product_entity_int" } /* Usage Examples: @@ -23,4 +23,4 @@ func (ProductInt) TableName() string { db.Model(&attr).Update("Value", 2) 4. Delete: db.Delete(&attr) -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_json.go b/model/entity/product/product_json.go index f28478b..7d03647 100644 --- a/model/entity/product/product_json.go +++ b/model/entity/product/product_json.go @@ -11,13 +11,13 @@ type ProductJson struct { EntityID uint `gorm:"column:entity_id;uniqueIndex:unq_entity_store"` // Changed to uniqueIndex StoreID uint `gorm:"column:store_id;uniqueIndex:unq_entity_store;not null;default:0"` Attributes datatypes.JSON `gorm:"column:attribute_json;type:json not null"` - + // Timestamps CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"` UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"` - + // Relationship - Product Product `gorm:"foreignKey:EntityID;references:ID"` + Product Product `gorm:"foreignKey:EntityID;references:ID"` } func (ProductJson) TableName() string { @@ -52,4 +52,4 @@ type Product struct { // ... other fields JsonData []ProductJson `gorm:"foreignKey:EntityID"` } -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_link.go b/model/entity/product/product_link.go index 4c4388f..47e4e00 100644 --- a/model/entity/product/product_link.go +++ b/model/entity/product/product_link.go @@ -1,15 +1,15 @@ package product type ProductLink struct { - LinkID uint `gorm:"column:link_id;primaryKey;autoIncrement"` - ProductID uint `gorm:"column:product_id;type:int unsigned;not null;default:0"` - LinkedProductID uint `gorm:"column:linked_product_id;type:int unsigned;not null;default:0"` - LinkTypeID uint16 `gorm:"column:link_type_id;type:smallint unsigned;not null;default:0"` + LinkID uint `gorm:"column:link_id;primaryKey;autoIncrement"` + ProductID uint `gorm:"column:product_id;type:int unsigned;not null;default:0"` + LinkedProductID uint `gorm:"column:linked_product_id;type:int unsigned;not null;default:0"` + LinkTypeID uint16 `gorm:"column:link_type_id;type:smallint unsigned;not null;default:0"` } // TableName specifies the table name func (ProductLink) TableName() string { - return "catalog_product_link" + return "catalog_product_link" } /* Usage Examples: @@ -39,4 +39,4 @@ func (ProductLink) TableName() string { ```go db.Delete(&prodLink) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_media_gallery.go b/model/entity/product/product_media_gallery.go index 118a8de..cf7d8e2 100644 --- a/model/entity/product/product_media_gallery.go +++ b/model/entity/product/product_media_gallery.go @@ -6,14 +6,14 @@ import ( ) type ProductMediaGallery struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;not null"` - Value string `gorm:"column:value;type:varchar(255)"` - MediaType string `gorm:"column:media_type;type:varchar(32);not null;default:'image'"` - Disabled uint16 `gorm:"column:disabled;not null;default:0"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;not null"` + Value string `gorm:"column:value;type:varchar(255)"` + MediaType string `gorm:"column:media_type;type:varchar(32);not null;default:'image'"` + Disabled uint16 `gorm:"column:disabled;not null;default:0"` // Relationships - Attribute entity.EavAttribute `gorm:"foreignKey:AttributeID;references:AttributeID"` + Attribute entity.EavAttribute `gorm:"foreignKey:AttributeID;references:AttributeID"` } // TableName specifies the table name @@ -51,4 +51,4 @@ func (ProductMediaGallery) TableName() string { ```go db.Delete(&media) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_text.go b/model/entity/product/product_text.go index 343fd04..3f5538f 100644 --- a/model/entity/product/product_text.go +++ b/model/entity/product/product_text.go @@ -1,15 +1,15 @@ package product type ProductText struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` - StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` - EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` - Value string `gorm:"column:value;type:text"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` + StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` + EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` + Value string `gorm:"column:value;type:text"` } func (ProductText) TableName() string { - return "catalog_product_entity_text" + return "catalog_product_entity_text" } /* Usage Examples: @@ -23,4 +23,4 @@ func (ProductText) TableName() string { db.Model(&attr).Update("Value", "Updated description") 4. Delete: db.Delete(&attr) -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/product_varchar.go b/model/entity/product/product_varchar.go index b5ddc9d..25632a7 100644 --- a/model/entity/product/product_varchar.go +++ b/model/entity/product/product_varchar.go @@ -1,15 +1,15 @@ package product type ProductVarchar struct { - ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` - AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` - StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` - EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` - Value string `gorm:"column:value;type:varchar(255)"` + ValueID uint `gorm:"column:value_id;primaryKey;autoIncrement"` + AttributeID uint16 `gorm:"column:attribute_id;type:smallint unsigned;not null;default:0"` + StoreID uint16 `gorm:"column:store_id;type:smallint unsigned;not null;default:0"` + EntityID uint `gorm:"column:entity_id;type:int unsigned;not null;default:0"` + Value string `gorm:"column:value;type:varchar(255)"` } func (ProductVarchar) TableName() string { - return "catalog_product_entity_varchar" + return "catalog_product_entity_varchar" } /* Usage Examples: @@ -32,4 +32,4 @@ func (ProductVarchar) TableName() string { 4. Delete: db.Delete(&attr) -*/ \ No newline at end of file +*/ diff --git a/model/entity/product/stock_item.go b/model/entity/product/stock_item.go index 577f6bb..2874ce5 100644 --- a/model/entity/product/stock_item.go +++ b/model/entity/product/stock_item.go @@ -6,32 +6,32 @@ import ( ) type StockItem struct { - ItemID uint `gorm:"column:item_id;primaryKey;autoIncrement"` - ProductID uint `gorm:"column:product_id;not null"` - StockID uint16 `gorm:"column:stock_id;not null"` - Qty float64 `gorm:"column:qty"` - MinQty float64 `gorm:"column:min_qty;not null;default:0.0000"` - UseConfigMinQty uint16 `gorm:"column:use_config_min_qty;not null;default:1"` - IsQtyDecimal uint16 `gorm:"column:is_qty_decimal;not null;default:0"` - Backorders uint16 `gorm:"column:backorders;not null;default:0"` - UseConfigBackorders uint16 `gorm:"column:use_config_backorders;not null;default:1"` - MinSaleQty float64 `gorm:"column:min_sale_qty;not null;default:1.0000"` - UseConfigMinSaleQty uint16 `gorm:"column:use_config_min_sale_qty;not null;default:1"` - MaxSaleQty float64 `gorm:"column:max_sale_qty;not null;default:0.0000"` - UseConfigMaxSaleQty uint16 `gorm:"column:use_config_max_sale_qty;not null;default:1"` - IsInStock uint16 `gorm:"column:is_in_stock;not null;default:0"` - LowStockDate *time.Time `gorm:"column:low_stock_date"` - NotifyStockQty *float64 `gorm:"column:notify_stock_qty"` - UseConfigNotifyStockQty uint16 `gorm:"column:use_config_notify_stock_qty;not null;default:1"` - ManageStock uint16 `gorm:"column:manage_stock;not null;default:0"` - UseConfigManageStock uint16 `gorm:"column:use_config_manage_stock;not null;default:1"` - StockStatusChangedAuto uint16 `gorm:"column:stock_status_changed_auto;not null;default:0"` - UseConfigQtyIncrements uint16 `gorm:"column:use_config_qty_increments;not null;default:1"` - QtyIncrements float64 `gorm:"column:qty_increments;not null;default:0.0000"` - UseConfigEnableQtyInc uint16 `gorm:"column:use_config_enable_qty_inc;not null;default:1"` - EnableQtyIncrements uint16 `gorm:"column:enable_qty_increments;not null;default:0"` - IsDecimalDivided uint16 `gorm:"column:is_decimal_divided;not null;default:0"` - WebsiteID uint16 `gorm:"column:website_id;not null;default:0"` + ItemID uint `gorm:"column:item_id;primaryKey;autoIncrement"` + ProductID uint `gorm:"column:product_id;not null"` + StockID uint16 `gorm:"column:stock_id;not null"` + Qty float64 `gorm:"column:qty"` + MinQty float64 `gorm:"column:min_qty;not null;default:0.0000"` + UseConfigMinQty uint16 `gorm:"column:use_config_min_qty;not null;default:1"` + IsQtyDecimal uint16 `gorm:"column:is_qty_decimal;not null;default:0"` + Backorders uint16 `gorm:"column:backorders;not null;default:0"` + UseConfigBackorders uint16 `gorm:"column:use_config_backorders;not null;default:1"` + MinSaleQty float64 `gorm:"column:min_sale_qty;not null;default:1.0000"` + UseConfigMinSaleQty uint16 `gorm:"column:use_config_min_sale_qty;not null;default:1"` + MaxSaleQty float64 `gorm:"column:max_sale_qty;not null;default:0.0000"` + UseConfigMaxSaleQty uint16 `gorm:"column:use_config_max_sale_qty;not null;default:1"` + IsInStock uint16 `gorm:"column:is_in_stock;not null;default:0"` + LowStockDate *time.Time `gorm:"column:low_stock_date"` + NotifyStockQty *float64 `gorm:"column:notify_stock_qty"` + UseConfigNotifyStockQty uint16 `gorm:"column:use_config_notify_stock_qty;not null;default:1"` + ManageStock uint16 `gorm:"column:manage_stock;not null;default:0"` + UseConfigManageStock uint16 `gorm:"column:use_config_manage_stock;not null;default:1"` + StockStatusChangedAuto uint16 `gorm:"column:stock_status_changed_auto;not null;default:0"` + UseConfigQtyIncrements uint16 `gorm:"column:use_config_qty_increments;not null;default:1"` + QtyIncrements float64 `gorm:"column:qty_increments;not null;default:0.0000"` + UseConfigEnableQtyInc uint16 `gorm:"column:use_config_enable_qty_inc;not null;default:1"` + EnableQtyIncrements uint16 `gorm:"column:enable_qty_increments;not null;default:0"` + IsDecimalDivided uint16 `gorm:"column:is_decimal_divided;not null;default:0"` + WebsiteID uint16 `gorm:"column:website_id;not null;default:0"` // Relationships //Product has a field of type StockItem @@ -77,4 +77,4 @@ func (StockItem) TableName() string { ```go db.Delete(&stockItem) ``` -*/ \ No newline at end of file +*/ diff --git a/model/entity/sales/sales_order_grid.go b/model/entity/sales/sales_order_grid.go index 4e1b7d5..8157a0e 100644 --- a/model/entity/sales/sales_order_grid.go +++ b/model/entity/sales/sales_order_grid.go @@ -1,46 +1,46 @@ package sales import ( - "time" + "time" ) type SalesOrderGrid struct { - EntityID uint `gorm:"column:entity_id;primaryKey"` - Status string `gorm:"column:status;type:varchar(32)"` - StoreID *uint `gorm:"column:store_id"` - StoreName string `gorm:"column:store_name;type:varchar(255)"` - CustomerID *uint `gorm:"column:customer_id"` - BaseGrandTotal *float64 `gorm:"column:base_grand_total;type:decimal(20,4)"` - BaseTotalPaid *float64 `gorm:"column:base_total_paid;type:decimal(20,4)"` - GrandTotal *float64 `gorm:"column:grand_total;type:decimal(20,4)"` - TotalPaid *float64 `gorm:"column:total_paid;type:decimal(20,4)"` - IncrementID string `gorm:"column:increment_id;type:varchar(50)"` - BaseCurrencyCode string `gorm:"column:base_currency_code;type:varchar(3)"` - OrderCurrencyCode string `gorm:"column:order_currency_code;type:varchar(255)"` - ShippingName string `gorm:"column:shipping_name;type:varchar(255)"` - BillingName string `gorm:"column:billing_name;type:varchar(255)"` - CreatedAt *time.Time `gorm:"column:created_at"` - UpdatedAt *time.Time `gorm:"column:updated_at"` - BillingAddress string `gorm:"column:billing_address;type:varchar(255)"` - ShippingAddress string `gorm:"column:shipping_address;type:varchar(255)"` - ShippingInformation string `gorm:"column:shipping_information;type:varchar(255)"` - CustomerEmail string `gorm:"column:customer_email;type:varchar(255)"` - CustomerGroup string `gorm:"column:customer_group;type:varchar(255)"` - Subtotal *float64 `gorm:"column:subtotal;type:decimal(20,4)"` - ShippingAndHandling *float64 `gorm:"column:shipping_and_handling;type:decimal(20,4)"` - CustomerName string `gorm:"column:customer_name;type:varchar(255)"` - PaymentMethod string `gorm:"column:payment_method;type:varchar(255)"` - TotalRefunded *float64 `gorm:"column:total_refunded;type:decimal(20,4)"` - PickupLocationCode string `gorm:"column:pickup_location_code;type:varchar(255)"` - DisputeStatus string `gorm:"column:dispute_status;type:varchar(45)"` - // Relationships (examples, actual models should be defined if needed) - // Store Store `gorm:"foreignKey:StoreID"` - // Customer Customer `gorm:"foreignKey:CustomerID"` + EntityID uint `gorm:"column:entity_id;primaryKey"` + Status string `gorm:"column:status;type:varchar(32)"` + StoreID *uint `gorm:"column:store_id"` + StoreName string `gorm:"column:store_name;type:varchar(255)"` + CustomerID *uint `gorm:"column:customer_id"` + BaseGrandTotal *float64 `gorm:"column:base_grand_total;type:decimal(20,4)"` + BaseTotalPaid *float64 `gorm:"column:base_total_paid;type:decimal(20,4)"` + GrandTotal *float64 `gorm:"column:grand_total;type:decimal(20,4)"` + TotalPaid *float64 `gorm:"column:total_paid;type:decimal(20,4)"` + IncrementID string `gorm:"column:increment_id;type:varchar(50)"` + BaseCurrencyCode string `gorm:"column:base_currency_code;type:varchar(3)"` + OrderCurrencyCode string `gorm:"column:order_currency_code;type:varchar(255)"` + ShippingName string `gorm:"column:shipping_name;type:varchar(255)"` + BillingName string `gorm:"column:billing_name;type:varchar(255)"` + CreatedAt *time.Time `gorm:"column:created_at"` + UpdatedAt *time.Time `gorm:"column:updated_at"` + BillingAddress string `gorm:"column:billing_address;type:varchar(255)"` + ShippingAddress string `gorm:"column:shipping_address;type:varchar(255)"` + ShippingInformation string `gorm:"column:shipping_information;type:varchar(255)"` + CustomerEmail string `gorm:"column:customer_email;type:varchar(255)"` + CustomerGroup string `gorm:"column:customer_group;type:varchar(255)"` + Subtotal *float64 `gorm:"column:subtotal;type:decimal(20,4)"` + ShippingAndHandling *float64 `gorm:"column:shipping_and_handling;type:decimal(20,4)"` + CustomerName string `gorm:"column:customer_name;type:varchar(255)"` + PaymentMethod string `gorm:"column:payment_method;type:varchar(255)"` + TotalRefunded *float64 `gorm:"column:total_refunded;type:decimal(20,4)"` + PickupLocationCode string `gorm:"column:pickup_location_code;type:varchar(255)"` + DisputeStatus string `gorm:"column:dispute_status;type:varchar(45)"` + // Relationships (examples, actual models should be defined if needed) + // Store Store `gorm:"foreignKey:StoreID"` + // Customer Customer `gorm:"foreignKey:CustomerID"` } // TableName specifies the table name func (SalesOrderGrid) TableName() string { - return "sales_order_grid" + return "sales_order_grid" } /* Usage Examples: @@ -75,4 +75,4 @@ func (SalesOrderGrid) TableName() string { ```go db.Delete(&order) ``` -*/ \ No newline at end of file +*/ diff --git a/model/repository/category/category_repository.go b/model/repository/category/category_repository.go index 99ab194..b3989a8 100644 --- a/model/repository/category/category_repository.go +++ b/model/repository/category/category_repository.go @@ -1,22 +1,22 @@ package category import ( - "log" - "sync" "gorm.io/gorm" - categoryEntity "magento.GO/model/entity/category" + "log" entity "magento.GO/model/entity" + categoryEntity "magento.GO/model/entity/category" + "sync" ) var ( categoryAttrMetaCache map[uint]entity.EavAttribute - categoryAttrMetaOnce sync.Once - treeCache map[uint16][]*CategoryTreeNode - treeCacheLock sync.RWMutex + categoryAttrMetaOnce sync.Once + treeCache map[uint16][]*CategoryTreeNode + treeCacheLock sync.RWMutex // Singleton for CategoryRepository categoryRepoInstance *CategoryRepository - categoryRepoOnce sync.Once + categoryRepoOnce sync.Once ) // GetCategoryRepository returns the singleton instance of CategoryRepository @@ -31,7 +31,7 @@ func GetCategoryRepository(db *gorm.DB) *CategoryRepository { type CategoryRepository struct { db *gorm.DB // cache stores categories per store: cache[storeID][categoryID] = CategoryWithAttributes - cache map[uint16]map[uint]CategoryWithAttributes + cache map[uint16]map[uint]CategoryWithAttributes cacheLock sync.RWMutex } @@ -59,7 +59,7 @@ func (r *CategoryRepository) FetchAllWithAttributes(storeID uint16) ([]categoryE // Subsequent calls return the cached data for fast access. // Thread-safe for concurrent use. func (r *CategoryRepository) FetchAllWithAttributesMap(storeID uint16) (map[uint]CategoryWithAttributes, error) { - + if r.cache == nil { r.cache = make(map[uint16]map[uint]CategoryWithAttributes) } @@ -70,7 +70,6 @@ func (r *CategoryRepository) FetchAllWithAttributesMap(storeID uint16) (map[uint } } - // Not cached: load from DB var categories []categoryEntity.Category err := r.db. @@ -114,7 +113,6 @@ func (r *CategoryRepository) InvalidateCache() { treeCacheLock.Unlock() } - func (r *CategoryRepository) GetByIDWithAttributesAndFlat(id uint, storeID uint16) (*categoryEntity.Category, map[string]map[string]interface{}, error) { cats, flats, err := r.GetByIDsWithAttributesAndFlat([]uint{id}, storeID) if err != nil { @@ -195,8 +193,8 @@ func FlattenCategoryAttributesWithLabels( label = *attr.FrontendLabel } flat[attr.AttributeCode] = map[string]interface{}{ - "value": v.Value, - "label": label, + "value": v.Value, + "label": label, "store_id": v.StoreID, } } @@ -209,8 +207,8 @@ func FlattenCategoryAttributesWithLabels( label = *attr.FrontendLabel } flat[attr.AttributeCode] = map[string]interface{}{ - "value": v.Value, - "label": label, + "value": v.Value, + "label": label, "store_id": v.StoreID, } } @@ -223,14 +221,14 @@ func FlattenCategoryAttributesWithLabels( label = *attr.FrontendLabel } flat[attr.AttributeCode] = map[string]interface{}{ - "value": v.Value, - "label": label, + "value": v.Value, + "label": label, "store_id": v.StoreID, } } } // Add core fields if needed - flat["entity_id"] = map[string]interface{}{ "value": category.EntityID, "label": "Entity ID", "store_id": 0 } + flat["entity_id"] = map[string]interface{}{"value": category.EntityID, "label": "Entity ID", "store_id": 0} // ...add more core fields as needed return flat } @@ -317,9 +315,9 @@ func (r *CategoryRepository) GetByIDsWithAttributes(ids []uint, storeID uint16) } type CategoryTreeNode struct { - Category categoryEntity.Category + Category categoryEntity.Category Attributes map[string]map[string]interface{} - Children []*CategoryTreeNode + Children []*CategoryTreeNode } // BuildCategoryTree builds a tree of categories (with flat attributes) starting from the given parentID (usually 0 for root). @@ -429,4 +427,3 @@ func (r *CategoryRepository) GetCacheCategory(storeID uint16, id uint) (interfac cat, found := cats[id] return cat, found } - diff --git a/model/repository/product/product_repository.go b/model/repository/product/product_repository.go index 9f37bd4..1bda832 100644 --- a/model/repository/product/product_repository.go +++ b/model/repository/product/product_repository.go @@ -6,25 +6,25 @@ package product import ( - productEntity "magento.GO/model/entity/product" - "gorm.io/gorm" "fmt" + "gorm.io/gorm" entity "magento.GO/model/entity" - "sync" + productEntity "magento.GO/model/entity/product" "os" + "sync" ) var ( - attributeCodeMap map[uint16]string - attributeCodeMapOnce sync.Once - flatProductsCache = make(map[uint16]map[uint]map[string]interface{}) - flatProductsCacheOnce sync.Once - flatProductsCacheLock sync.RWMutex - cacheDisabled = os.Getenv("PRODUCT_FLAT_CACHE") == "off" + attributeCodeMap map[uint16]string + attributeCodeMapOnce sync.Once + flatProductsCache = make(map[uint16]map[uint]map[string]interface{}) + flatProductsCacheOnce sync.Once + flatProductsCacheLock sync.RWMutex + cacheDisabled = os.Getenv("PRODUCT_FLAT_CACHE") == "off" // Singleton for ProductRepository productRepoInstance *ProductRepository - productRepoOnce sync.Once + productRepoOnce sync.Once ) // GetProductRepository returns the singleton instance of ProductRepository @@ -284,13 +284,13 @@ func FlattenProductAttributesWithCodes(product *productEntity.Product, attrMap m // Flatten stock item if product.StockItem.ProductID != 0 { stock := map[string]interface{}{ - "item_id": product.StockItem.ItemID, - "qty": product.StockItem.Qty, - "is_in_stock": product.StockItem.IsInStock, - "min_qty": product.StockItem.MinQty, + "item_id": product.StockItem.ItemID, + "qty": product.StockItem.Qty, + "is_in_stock": product.StockItem.IsInStock, + "min_qty": product.StockItem.MinQty, "max_sale_qty": product.StockItem.MaxSaleQty, "manage_stock": product.StockItem.ManageStock, - "website_id": product.StockItem.WebsiteID, + "website_id": product.StockItem.WebsiteID, } attrs["stock_item"] = stock } @@ -326,4 +326,4 @@ func LoadAttributeCodeMap(db *gorm.DB) (map[uint16]string, error) { m[a.AttributeID] = a.AttributeCode } return m, nil -} \ No newline at end of file +} diff --git a/model/repository/sales/sales_order_grid_repository.go b/model/repository/sales/sales_order_grid_repository.go index 8d49442..f880234 100644 --- a/model/repository/sales/sales_order_grid_repository.go +++ b/model/repository/sales/sales_order_grid_repository.go @@ -1,8 +1,8 @@ package sales import ( - salesEntity "magento.GO/model/entity/sales" "gorm.io/gorm" + salesEntity "magento.GO/model/entity/sales" ) type SalesOrderGridRepository struct { @@ -38,4 +38,4 @@ func (r *SalesOrderGridRepository) Update(order *salesEntity.SalesOrderGrid) err func (r *SalesOrderGridRepository) Delete(id uint) error { return r.db.Delete(&salesEntity.SalesOrderGrid{}, id).Error -} \ No newline at end of file +} diff --git a/service/product/product_service.go b/service/product/product_service.go index 1ee5793..fd5aba0 100644 --- a/service/product/product_service.go +++ b/service/product/product_service.go @@ -55,4 +55,4 @@ func (s *ProductService) UpdateProduct(id uint, input *ProductInput) error { func (s *ProductService) DeleteProduct(id uint) error { return s.repo.Delete(id) -} \ No newline at end of file +} diff --git a/service/sales/sales_order_grid_service.go b/service/sales/sales_order_grid_service.go index 95fdca6..970fb30 100644 --- a/service/sales/sales_order_grid_service.go +++ b/service/sales/sales_order_grid_service.go @@ -31,4 +31,4 @@ func (s *SalesOrderGridService) UpdateOrder(order *entity.SalesOrderGrid) error func (s *SalesOrderGridService) DeleteOrder(id uint) error { return s.repo.Delete(id) -} \ No newline at end of file +}