11 Commits

Author SHA1 Message Date
d1c63de5d1 Release v1.0.37
Some checks failed
Release Build / create-release (push) Failing after 6s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 17:57:02 +06:00
9e431a00ce Release v1.0.36
Some checks failed
Release Build / create-release (push) Failing after 6m14s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 17:44:23 +06:00
75822f7fb8 Release v1.0.35
Some checks failed
Release Build / create-release (push) Failing after 6m17s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 17:33:06 +06:00
707cd82717 Release v1.0.34
Some checks failed
Release Build / create-release (push) Failing after 6m29s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 17:11:14 +06:00
6f10caa69d Release v1.0.33
Some checks failed
Release Build / create-release (push) Successful in 6m37s
Release Build / create-docker-image (push) Failing after 37s
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 17:00:47 +06:00
da3538051e Release v1.0.33
Some checks failed
Release Build / create-release (push) Failing after 3s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:59:35 +06:00
032bdaa619 Release v1.0.33
Some checks failed
Release Build / create-release (push) Failing after 2s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:56:50 +06:00
b786ed1bf8 Release v1.0.33
Some checks failed
Release Build / create-release (push) Failing after 3s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:55:34 +06:00
231867a310 Release v1.0.33
Some checks failed
Release Build / create-release (push) Failing after 2s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:50:29 +06:00
9d82d42680 Release v1.0.32
Some checks failed
Release Build / create-release (push) Failing after 6s
Release Build / create-docker-image (push) Has been skipped
Release Build / update-to-release-branch (push) Has been skipped
2025-07-28 16:26:11 +06:00
9da418648c changed builder image
All checks were successful
Build Builder Docker Image / create-builder-docker-image (push) Successful in 5m42s
2025-07-28 16:02:16 +06:00
10 changed files with 580 additions and 41 deletions

View File

@@ -9,13 +9,14 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
# image: golang:1.21 # image: golang:1.21
image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest # image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:builder-1.0.32
image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest
steps: steps:
- name: Checkout repository - name: Checkout repository
run: | run: |
git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea
cd hello_gitea cd hello_gitea
git checkout ${{ github.ref }} git checkout ${{ github.ref }}
- name: Setup Go - name: Setup Go
run: | run: |
@@ -29,28 +30,59 @@ jobs:
run: | run: |
cd hello_gitea cd hello_gitea
mkdir -p bin mkdir -p bin
echo "Building for all platforms..."
mv usr/local/bin/quick-build quick-build.sh
chmod +x quick-build.sh
# Build for all platforms # Build for all platforms
GOOS=linux GOARCH=amd64 go build -o bin/hello-api-linux-amd64 main.go echo "Building for linux amd64..."
GOOS=linux GOARCH=arm64 go build -o bin/hello-api-linux-arm64 main.go quick-build.sh linux amd64
GOOS=windows GOARCH=amd64 go build -o bin/hello-api-windows-amd64.exe main.go
GOOS=darwin GOARCH=amd64 go build -o bin/hello-api-darwin-amd64 main.go echo "Building for linux arm64..."
GOOS=darwin GOARCH=arm64 go build -o bin/hello-api-darwin-arm64 main.go quick-build.sh linux arm64
echo "Building for windows amd64..."
quick-build.sh windows amd64
echo "Building for darwin amd64..."
quick-build.sh darwin amd64
echo "Building for darwin arm64..."
quick-build.sh darwin arm64
echo "Listing bin directory..."
ls -la bin
# Create archives # Create archives
echo "Creating archives..."
cd bin cd bin
# Create archives with correct file names
echo "Creating archives for linux amd64..."
tar -czf hello-api-linux-amd64.tar.gz hello-api-linux-amd64 tar -czf hello-api-linux-amd64.tar.gz hello-api-linux-amd64
tar -czf hello-api-linux-arm64.tar.gz hello-api-linux-arm64
tar -czf hello-api-windows-amd64.tar.gz hello-api-windows-amd64.exe
tar -czf hello-api-darwin-amd64.tar.gz hello-api-darwin-amd64
tar -czf hello-api-darwin-arm64.tar.gz hello-api-darwin-arm64
echo "Creating archives for linux arm64..."
tar -czf hello-api-linux-arm64.tar.gz hello-api-linux-arm64
echo "Creating archives for windows amd64..."
mv hello-api-windows-amd64 hello-api-windows-amd64.exe
tar -czf hello-api-windows-amd64.tar.gz hello-api-windows-amd64.exe
echo "Creating archives for darwin amd64..."
tar -czf hello-api-darwin-amd64.tar.gz hello-api-darwin-amd64
echo "Creating archives for darwin arm64..."
tar -czf hello-api-darwin-arm64.tar.gz hello-api-darwin-arm64
echo "Listing bin directory again ..."
ls -la ls -la
- name: Create Release - name: Create Release
run: | run: |
cd hello_gitea cd hello_gitea
# Create release using Gitea API # Create release using Gitea API
echo "Creating release..."
curl -X POST \ curl -X POST \
-H "Authorization: token ${{ secrets.GITEATOKEN }}" \ -H "Authorization: token ${{ secrets.GITEATOKEN }}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
@@ -63,12 +95,13 @@ jobs:
}' \ }' \
"https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases" "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases"
# Upload assets echo "Getting release id..."
RELEASE_ID=$(curl -s -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ RELEASE_ID=$(curl -s -H "Authorization: token ${{ secrets.GITEATOKEN }}" \
"https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/tags/${{ github.ref_name }}" | \ "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/tags/${{ github.ref_name }}" | \
jq -r '.id') jq -r '.id')
# Upload all binaries # Upload all binaries
echo "Uploading assets..."
for file in bin/*.tar.gz; do for file in bin/*.tar.gz; do
echo "Uploading $file..." echo "Uploading $file..."
curl -X POST \ curl -X POST \
@@ -78,6 +111,8 @@ jobs:
"https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/$RELEASE_ID/assets?name=$(basename $file)" "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/$RELEASE_ID/assets?name=$(basename $file)"
done done
echo "Release created successfully"
create-docker-image: create-docker-image:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
@@ -103,7 +138,7 @@ jobs:
run: | run: |
# Docker is already installed in docker:dind image # Docker is already installed in docker:dind image
docker --version docker --version
echo "Setting up Docker Buildx for multi-platform builds..."
# Setup Docker Buildx for multi-platform builds # Setup Docker Buildx for multi-platform builds
docker buildx create --name go-buildx --use docker buildx create --name go-buildx --use
docker buildx inspect --bootstrap docker buildx inspect --bootstrap
@@ -115,6 +150,7 @@ jobs:
- name: Build multi-platform Docker images - name: Build multi-platform Docker images
run: | run: |
cd hello_gitea cd hello_gitea
echo "Building multi-platform images using buildx..."
# Build multi-platform images using buildx # Build multi-platform images using buildx
docker buildx build \ docker buildx build \
--platform linux/amd64,linux/arm64 \ --platform linux/amd64,linux/arm64 \
@@ -122,8 +158,11 @@ jobs:
--tag ${{ secrets.DOCKERHUB_USERNAME }}/hello-api:latest \ --tag ${{ secrets.DOCKERHUB_USERNAME }}/hello-api:latest \
--push \ --push \
. .
echo "Multi-platform images built successfully"
- name: Remove buildx - name: Remove buildx
run: | run: |
echo "Removing buildx..."
docker buildx rm go-buildx docker buildx rm go-buildx
update-to-release-branch: update-to-release-branch:
@@ -134,6 +173,7 @@ jobs:
steps: steps:
- name: Create Release Branch - name: Create Release Branch
run: | run: |
echo "Creating release branch..."
echo "=== GitHub Variables ===" echo "=== GitHub Variables ==="
echo "github.ref = ${{ github.ref }}" echo "github.ref = ${{ github.ref }}"
echo "github.ref_name = ${{ github.ref_name }}" echo "github.ref_name = ${{ github.ref_name }}"
@@ -171,4 +211,5 @@ jobs:
# Push changes to release branch # Push changes to release branch
echo "Pushing changes to release branch..." echo "Pushing changes to release branch..."
git push origin release --force git push origin release --force
echo "Changes pushed to release branch successfully"

View File

@@ -1,5 +1,5 @@
# Build stage # Build stage
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
# Install git and ca-certificates # Install git and ca-certificates
RUN apk --no-cache add git ca-certificates RUN apk --no-cache add git ca-certificates

View File

@@ -9,5 +9,24 @@ RUN apt-get update && \
jq && \ jq && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
# Создаем рабочую директорию
WORKDIR /app
# Копируем файлы зависимостей
COPY go.mod go.sum ./
# Предварительно загружаем все зависимости
RUN go mod download && go mod verify
# Создаем скрипт для быстрой сборки
COPY scripts/quick-build.sh /usr/local/bin/quick-build
# делаем скрипт исполняемым
RUN chmod +x /usr/local/bin/quick-build
# Устанавливаем переменные окружения
ENV GOPATH=/go
ENV PATH=$PATH:/go/bin:/usr/local/bin
# (Опционально) Можно добавить команду по умолчанию # (Опционально) Можно добавить команду по умолчанию
CMD ["bash"] CMD ["bash"]

234
README.md
View File

@@ -1,12 +1,15 @@
# Hello Gitea API # Hello Gitea API
Простой REST API сервер, построенный на Go с использованием Gin framework. Современный REST API сервер, построенный на Go с использованием Gin framework и структурированного логирования.
## 🚀 Возможности ## 🚀 Возможности
- ✅ REST API с JSON ответами - ✅ REST API с JSON ответами
- ✅ Health check endpoint - ✅ Health check endpoint
- ✅ CORS поддержка - ✅ CORS поддержка
-**Современное структурированное логирование** (Go 1.24+)
-**Настраиваемые уровни и форматы логирования**
-**Системная информация и мониторинг**
- ✅ Мультиплатформенная сборка - ✅ Мультиплатформенная сборка
- ✅ Docker образы для Linux AMD64/ARM64 - ✅ Docker образы для Linux AMD64/ARM64
- ✅ Автоматические релизы через Gitea Actions - ✅ Автоматические релизы через Gitea Actions
@@ -37,6 +40,8 @@ go run main.go
## 🔧 Конфигурация ## 🔧 Конфигурация
### Основные настройки
Сервер запускается на порту 8080 по умолчанию. Можно изменить через переменную окружения: Сервер запускается на порту 8080 по умолчанию. Можно изменить через переменную окружения:
```bash ```bash
@@ -44,46 +49,98 @@ export PORT=3000
./hello-api ./hello-api
``` ```
### Настройка логирования
Приложение поддерживает гибкую настройку логирования через переменные окружения:
#### Уровни логирования (`LOG_LEVEL`)
```bash
# Доступные уровни (по возрастанию важности):
export LOG_LEVEL=DEBUG # Все сообщения
export LOG_LEVEL=INFO # Информационные и выше
export LOG_LEVEL=WARN # Предупреждения и выше
export LOG_LEVEL=ERROR # Только ошибки
```
#### Форматы логирования (`LOG_FORMAT`)
```bash
# JSON формат (по умолчанию) - для production
export LOG_FORMAT=json
# Текстовый формат - для разработки
export LOG_FORMAT=text
```
#### Примеры конфигурации
**Для разработки:**
```bash
export LOG_LEVEL=DEBUG
export LOG_FORMAT=text
export PORT=8080
go run main.go
```
**Для production:**
```bash
export LOG_LEVEL=INFO
export LOG_FORMAT=json
export PORT=8080
./hello-api
```
## 📡 API Endpoints ## 📡 API Endpoints
### GET / ### GET /
Основной endpoint Основной endpoint
**Ответ:** **Ответ:**
```json ```json
{ {
"message": "Hello, World!", "message": "Hello, World!",
"version": "1.0.0" "version": "1.0.33"
} }
``` ```
### GET /healthz ### GET /healthz
Health check endpoint Health check endpoint
**Ответ:** **Ответ:**
```json ```json
{ {
"status": "ok", "status": "ok",
"version": "1.0.0" "version": "1.0.33"
} }
``` ```
### GET /api/v1/info ### GET /api/v1/info
Информация о сервисе Информация о сервисе
**Ответ:** **Ответ:**
```json ```json
{ {
"service": "hello-api", "service": "hello-api",
"status": "running", "status": "running",
"version": "1.0.0" "version": "1.0.33"
} }
``` ```
### POST /api/v1/echo ### POST /api/v1/echo
Echo endpoint - возвращает отправленное сообщение Echo endpoint - возвращает отправленное сообщение
**Запрос:** **Запрос:**
```json ```json
{ {
"message": "Hello from client!" "message": "Hello from client!"
@@ -91,18 +148,149 @@ Echo endpoint - возвращает отправленное сообщение
``` ```
**Ответ:** **Ответ:**
```json ```json
{ {
"echo": "Hello from client!", "echo": "Hello from client!",
"version": "1.0.0" "version": "1.0.33"
} }
``` ```
## 🛠 Разработка ### GET /api/v1/config
**Новый endpoint** - системная информация и конфигурация
**Ответ:**
```json
{
"version": "1.0.33",
"system_info": {
"go_version": "go1.24.0",
"os": "linux",
"architecture": "amd64",
"num_cpu": 8,
"start_time": "2024-01-15T10:30:45Z"
},
"environment": {
"PATH": "/usr/local/bin:/usr/bin:/bin",
"PORT": "8080",
"LOG_LEVEL": "INFO"
},
"uptime": "2h30m15s"
}
```
### POST /api/v1/log-test
**Новый endpoint** - тестирование уровней логирования
**Запрос:**
```json
{
"level": "debug",
"message": "Test log message"
}
```
**Ответ:**
```json
{
"status": "logged",
"level": "debug",
"message": "Test log message"
}
```
## 📊 Логирование
### Структурированное логирование
Приложение использует современную систему логирования Go 1.24+ (`log/slog`) с поддержкой:
- **Структурированных логов** в JSON и текстовом формате
- **Уровней логирования** (DEBUG, INFO, WARN, ERROR)
- **Контекстной информации** (IP клиента, User-Agent, время запроса)
- **Автоматического форматирования** времени
- **Фильтрации чувствительных данных**
### Примеры логов
#### JSON формат (production)
```json
{
"time": "2024-01-15T10:30:45.123Z",
"level": "INFO",
"msg": "HTTP Request",
"method": "GET",
"path": "/api/v1/config",
"status": 200,
"latency": "1.234ms",
"client_ip": "192.168.1.100",
"user_agent": "curl/7.68.0",
"timestamp": "2024-01-15T10:30:45.123Z"
}
```
#### Текстовый формат (development)
``` text
2024-01-15T10:30:45.123Z INFO HTTP Request method=GET path=/api/v1/config status=200 latency=1.234ms client_ip=192.168.1.100 user_agent="curl/7.68.0"
```
## Основные изменения в README
### 🆕 **Новые разделы:**
1. **Конфигурация логирования** - подробное описание переменных окружения
2. **Форматы логирования** - примеры JSON и текстового формата
3. **Новые API endpoints** - `/api/v1/config` и `/api/v1/log-test`
4. **Безопасность логирования** - фильтрация чувствительных данных
### 📊 **Детальное описание логирования:**
- **Уровни логирования** (DEBUG, INFO, WARN, ERROR)
- **Форматы** (JSON для production, text для разработки)
- **Примеры логов** с реальными данными
- **Контекстная информация** в логах
### 🔧 **Практические примеры:**
- Настройка для разработки и production
- Команды для тестирования
- Отладочные приемы
### 📦 **Улучшенная структура:**
- Четкое разделение возможностей
- Пошаговые инструкции
- Примеры конфигурации
- Команды для тестирования
Теперь README полностью отражает современные возможности приложения с детальным описанием системы логирования! 🎉
### Логируемые события
- **HTTP запросы** с детальной информацией
- **Ошибки приложения** с контекстом
- **Системные события** (запуск, остановка)
- **API вызовы** с параметрами
- **Конфигурационные изменения**
### Безопасность
- **Автоматическая фильтрация** чувствительных переменных окружения
- **Безопасное логирование** без утечки секретов
- **Контролируемые уровни** для разных сред
## Разработка
### Зависимости ### Зависимости
- Go 1.21+ - Go 1.24+
- Gin framework - Gin framework
### Сборка ### Сборка
@@ -128,16 +316,42 @@ GOOS=darwin GOARCH=arm64 go build -o hello-api-darwin-arm64 main.go
### Тестирование ### Тестирование
```bash ```bash
# Запуск сервера # Запуск сервера с отладочным логированием
export LOG_LEVEL=DEBUG
export LOG_FORMAT=text
go run main.go go run main.go
# Тестирование API # Тестирование API
curl http://localhost:8080/ curl http://localhost:8080/
curl http://localhost:8080/healthz curl http://localhost:8080/healthz
curl http://localhost:8080/api/v1/info curl http://localhost:8080/api/v1/info
curl http://localhost:8080/api/v1/config
# Тестирование echo
curl -X POST http://localhost:8080/api/v1/echo \ curl -X POST http://localhost:8080/api/v1/echo \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"message":"Hello!"}' -d '{"message":"Hello!"}'
# Тестирование логирования
curl -X POST http://localhost:8080/api/v1/log-test \
-H "Content-Type: application/json" \
-d '{"level": "debug", "message": "Test log message"}'
```
### Отладка
Для детального анализа работы приложения:
```bash
# Включить отладочное логирование
export LOG_LEVEL=DEBUG
export LOG_FORMAT=text
# Запустить приложение
go run main.go
# В другом терминале - мониторинг логов
tail -f /var/log/application.log # если логи в файл
``` ```
## 🚀 CI/CD ## 🚀 CI/CD
@@ -161,8 +375,6 @@ curl -X POST http://localhost:8080/api/v1/echo \
Таким образом, данный файл обеспечивает автоматическую сборку и публикацию артефактов проекта при выпуске новых версий, что упрощает процесс релиза и гарантирует наличие актуальных образов и бинарников для пользователей. Таким образом, данный файл обеспечивает автоматическую сборку и публикацию артефактов проекта при выпуске новых версий, что упрощает процесс релиза и гарантирует наличие актуальных образов и бинарников для пользователей.
## 📄 Лицензия ## 📄 Лицензия
MIT License MIT Licens

View File

@@ -424,7 +424,7 @@ jobs:
- Создает релиз с именем текущей версии через Gitea API - Создает релиз с именем текущей версии через Gitea API
- Загружает бинарники как assets релиза - Загружает бинарники как assets релиза
для работы данного job используется кастомный образ - если бы мы использовали просто golang:1.24, то при каждом запуске данного job необходимо было бы скачивать jq и устанавливаться в контейнере (возможно со временем, что то еще потребовалось бы ...) для работы данного job используется кастомный образ - если бы мы использовали просто golang:1.24, то при каждом запуске данного job необходимо было бы скачивать jq и устанавливаться в контейнере (возможно со временем, что-то еще потребовалось бы ...)
```yaml ```yaml
- name: Setup Go container - name: Setup Go container

2
go.mod
View File

@@ -1,6 +1,6 @@
module direct-dev-ru/hello_gitea module direct-dev-ru/hello_gitea
go 1.21 go 1.24
require github.com/gin-gonic/gin v1.10.1 require github.com/gin-gonic/gin v1.10.1

239
main.go
View File

@@ -1,21 +1,127 @@
package main package main
import ( import (
"log" "context"
"log/slog"
"net/http" "net/http"
"os" "os"
"runtime"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
const version = "1.0.31" const version = "1.0.37"
// SystemInfo holds system information
type SystemInfo struct {
GoVersion string `json:"go_version"`
OS string `json:"os"`
Architecture string `json:"architecture"`
NumCPU int `json:"num_cpu"`
StartTime string `json:"start_time"`
}
var (
startTime = time.Now()
logger *slog.Logger
)
// setupLogger configures structured logging with different levels for dev/prod
func setupLogger() {
var logLevel slog.Level
var logFormat string
// Determine log level from environment
switch os.Getenv("LOG_LEVEL") {
case "DEBUG":
logLevel = slog.LevelDebug
case "INFO":
logLevel = slog.LevelInfo
case "WARN":
logLevel = slog.LevelWarn
case "ERROR":
logLevel = slog.LevelError
default:
logLevel = slog.LevelInfo
}
// Determine log format from environment
logFormat = os.Getenv("LOG_FORMAT")
if logFormat == "" {
logFormat = "json" // Default to JSON for production
}
// Configure logger based on format
var handler slog.Handler
opts := &slog.HandlerOptions{
Level: logLevel,
AddSource: true,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// Customize timestamp format
if a.Key == slog.TimeKey {
return slog.Attr{
Key: slog.TimeKey,
Value: slog.StringValue(a.Value.Time().Format("2006-01-02T15:04:05.000Z07:00")),
}
}
return a
},
}
if logFormat == "text" {
handler = slog.NewTextHandler(os.Stdout, opts)
} else {
handler = slog.NewJSONHandler(os.Stdout, opts)
}
logger = slog.New(handler)
slog.SetDefault(logger)
}
// logMiddleware creates a structured logging middleware for Gin
func logMiddleware() gin.HandlerFunc {
return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
// Create structured log entry
logger.Info("HTTP Request",
"method", param.Method,
"path", param.Path,
"status", param.StatusCode,
"latency", param.Latency,
"client_ip", param.ClientIP,
"user_agent", param.Request.UserAgent(),
"timestamp", param.TimeStamp.Format("2006-01-02T15:04:05.000Z07:00"),
)
return "" // Return empty string as we handle logging ourselves
})
}
// errorLogger logs errors with structured context
func ErrorLogger(err error, context ...any) {
logger.Error("Application Error",
append([]any{"error", err.Error()}, context...)...,
)
}
func main() { func main() {
// Setup structured logging
setupLogger()
logger.Info("Starting application",
"version", version,
"go_version", runtime.Version(),
"os", runtime.GOOS,
"architecture", runtime.GOARCH,
"num_cpu", runtime.NumCPU(),
)
// Set Gin mode // Set Gin mode
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
// Create router // Create router with custom logger
r := gin.Default() r := gin.New()
r.Use(logMiddleware())
r.Use(gin.Recovery())
// Add middleware for CORS // Add middleware for CORS
r.Use(func(c *gin.Context) { r.Use(func(c *gin.Context) {
@@ -33,6 +139,7 @@ func main() {
// Health check endpoint // Health check endpoint
r.GET("/healthz", func(c *gin.Context) { r.GET("/healthz", func(c *gin.Context) {
logger.Debug("Health check requested", "client_ip", c.ClientIP())
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"status": "ok", "status": "ok",
"version": version, "version": version,
@@ -41,6 +148,10 @@ func main() {
// Main endpoint // Main endpoint
r.GET("/", func(c *gin.Context) { r.GET("/", func(c *gin.Context) {
logger.Info("Root endpoint accessed",
"client_ip", c.ClientIP(),
"user_agent", c.Request.UserAgent(),
)
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "Hello, World!", "message": "Hello, World!",
"version": version, "version": version,
@@ -51,6 +162,7 @@ func main() {
api := r.Group("/api/v1") api := r.Group("/api/v1")
{ {
api.GET("/info", func(c *gin.Context) { api.GET("/info", func(c *gin.Context) {
logger.Debug("API info requested", "client_ip", c.ClientIP())
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"service": "hello-api", "service": "hello-api",
"version": version, "version": version,
@@ -64,17 +176,104 @@ func main() {
} }
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
logger.Warn("Invalid JSON in echo request",
"client_ip", c.ClientIP(),
"error", err.Error(),
)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid JSON", "error": "Invalid JSON",
}) })
return return
} }
logger.Info("Echo request processed",
"client_ip", c.ClientIP(),
"message_length", len(request.Message),
)
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"echo": request.Message, "echo": request.Message,
"version": version, "version": version,
}) })
}) })
// Configuration endpoint with detailed logging
api.GET("/config", func(c *gin.Context) {
logger.Debug("Configuration requested", "client_ip", c.ClientIP())
systemInfo := SystemInfo{
GoVersion: runtime.Version(),
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
NumCPU: runtime.NumCPU(),
StartTime: startTime.Format(time.RFC3339),
}
// Get environment variables (excluding sensitive ones)
envVars := make(map[string]string)
sensitiveCount := 0
for _, env := range os.Environ() {
// Skip sensitive environment variables
if !isSensitiveEnvVar(env) {
envVars[env] = os.Getenv(env)
} else {
sensitiveCount++
}
}
logger.Info("Configuration accessed",
"client_ip", c.ClientIP(),
"env_vars_count", len(envVars),
"sensitive_vars_filtered", sensitiveCount,
"uptime", time.Since(startTime).String(),
)
c.JSON(http.StatusOK, gin.H{
"version": version,
"system_info": systemInfo,
"environment": envVars,
"uptime": time.Since(startTime).String(),
})
})
// New logging endpoint to test log levels
api.POST("/log-test", func(c *gin.Context) {
var request struct {
Level string `json:"level"`
Message string `json:"message"`
}
if err := c.ShouldBindJSON(&request); err != nil {
logger.Warn("Invalid JSON in log-test request",
"client_ip", c.ClientIP(),
"error", err.Error(),
)
c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid JSON",
})
return
}
// Log based on requested level
switch request.Level {
case "debug":
logger.Debug(request.Message, "client_ip", c.ClientIP())
case "info":
logger.Info(request.Message, "client_ip", c.ClientIP())
case "warn":
logger.Warn(request.Message, "client_ip", c.ClientIP())
case "error":
logger.Error(request.Message, "client_ip", c.ClientIP())
default:
logger.Info(request.Message, "client_ip", c.ClientIP(), "level", request.Level)
}
c.JSON(http.StatusOK, gin.H{
"status": "logged",
"level": request.Level,
"message": request.Message,
})
})
} }
// Get port from environment or use default // Get port from environment or use default
@@ -83,9 +282,37 @@ func main() {
port = "8080" port = "8080"
} }
// Start server // Start server with structured logging
logger.Info("Server starting",
"port", port,
"mode", gin.Mode(),
)
ctx := context.Background()
go func() {
// Graceful shutdown handling
<-ctx.Done()
logger.Info("Shutdown signal received, stopping server...")
}()
err := r.Run(":" + port) err := r.Run(":" + port)
if err != nil { if err != nil {
log.Fatal(err) logger.Error("Server failed to start", "error", err.Error())
os.Exit(1)
} }
} }
// isSensitiveEnvVar checks if an environment variable contains sensitive information
func isSensitiveEnvVar(env string) bool {
sensitivePrefixes := []string{
"PASSWORD", "SECRET", "KEY", "TOKEN", "CREDENTIAL",
"AWS_", "GITHUB_", "DOCKER_", "KUBERNETES_",
}
for _, prefix := range sensitivePrefixes {
if len(env) >= len(prefix) && env[:len(prefix)] == prefix {
return true
}
}
return false
}

View File

@@ -2,7 +2,7 @@
BIN_DIR=bin BIN_DIR=bin
APP_NAME=hello-api APP_NAME=hello-api
VERSION=1.0.31 VERSION=1.0.37
build: build:
mkdir -p $(BIN_DIR) mkdir -p $(BIN_DIR)

21
scripts/quick-build.sh Normal file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
if [ ! -f go.mod ]; then
echo "go.mod not found in current directory"
exit 1
fi
# Проверяем, изменились ли зависимости
if [ ! -f go.sum ] || ! go mod verify >/dev/null 2>&1; then
echo "Dependencies changed, downloading..."
go mod download
fi
# Собираем для указанной платформы
if [ -n "$1" ] && [ -n "$2" ]; then
echo "Building for $1 $2..."
GOOS=$1 GOARCH=$2 go build -o "bin/hello-api-$1-$2" main.go
else
echo "Building for current platform..."
go build -o bin/hello-api main.go
fi

View File

@@ -36,6 +36,9 @@ get_version_interactive() {
echo "📝 Подтверждение:" echo "📝 Подтверждение:"
echo " Текущая версия: $CURRENT_VERSION" echo " Текущая версия: $CURRENT_VERSION"
echo " Новая версия: $VERSION" echo " Новая версия: $VERSION"
if [ "$CURRENT_VERSION" == "$VERSION" ]; then
echo "⚠️ Новая версия совпадает с текущей. Возможно будет обновлен тег ..."
fi
echo "" echo ""
read -r -p "Продолжить? (y/N): " CONFIRM read -r -p "Продолжить? (y/N): " CONFIRM
@@ -68,11 +71,27 @@ if ! git rev-parse --git-dir > /dev/null 2>&1; then
exit 1 exit 1
fi fi
# Проверяем, что нет незакоммиченных изменений # Проверяем, существует ли уже тег с такой версией
# if ! git diff-index --quiet HEAD --; then if git tag -l "v$VERSION" | grep -q "v$VERSION"; then
# echo "Ошибка: Есть незакоммиченные изменения. Сначала закоммитьте их." echo "⚠️ Тег v$VERSION уже существует!"
# exit 1 echo ""
# fi read -r -p "Обновить существующий тег? (y/N): " UPDATE_TAG
if [[ ! $UPDATE_TAG =~ ^[Yy]$ ]]; then
echo "❌ Обновление тега отменено"
exit 0
fi
echo "🔄 Обновляем существующий тег..."
# Удаляем локальный тег
git tag -d "v$VERSION" 2>/dev/null || true
# Удаляем удаленный тег
git push origin ":refs/tags/v$VERSION" 2>/dev/null || true
echo "✅ Старый тег удален"
fi
# Обновляем версию в main.go # Обновляем версию в main.go
echo "📝 Обновляем версию в main.go..." echo "📝 Обновляем версию в main.go..."