init: learning-service skeleton
Микросервис обучения портала: тесты, курсы, видео-уроки, доступы, public-ссылки для кандидатов с email-валидацией. В этой итерации: - Skeleton (config, migrate, main, health) по паттерну tasks/candidates - Migration 001_init: 10 таблиц (tests/questions/answers/attempts/ attempt_answers + courses/lessons/lesson_progress + access_grants + public_tokens) с подробными комментариями why - Tests: полный CRUD + вопросы/ответы; non-owner'у is_correct и explanation скрываются в выдаче - Заглушки 501 для attempts / courses / lessons / video-stream / access / public-tokens — следующие итерации - k8s: namespace, configmap, secrets, postgres, deployment с HPA, service с portal-discovery annotations - Dockerfile, Makefile, .gitignore См. README.md для полного списка отложенного и инструкций запуска. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
97
internal/config/config.go
Normal file
97
internal/config/config.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Package config — env-based конфиг learning-сервиса. Стиль envStr идентичен
|
||||
// candidates/tasks/booking — чтобы k8s-манифесты выглядели единообразно.
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ServerPort string
|
||||
DatabaseURL string
|
||||
MigrationsDir string
|
||||
|
||||
// InternalAPIKey — общий ключ с порталом для server-to-server auth.
|
||||
// Portal-proxy ставит X-Internal-Key + X-User-Id; сервис верифицирует
|
||||
// ключ и доверяет X-User-Id (JWT уже провалидирован на портале).
|
||||
InternalAPIKey string
|
||||
|
||||
// PortalURL — для in-app уведомлений (назначен курс/тест/пройден тест).
|
||||
// Если пусто — notify-вызовы тихо no-op'ятся, основной flow работает.
|
||||
PortalURL string
|
||||
|
||||
// PublicBaseURL — внешний origin, под которым отдаются public-ссылки
|
||||
// для кандидатов (например, https://portal.estateliga.work). Используется
|
||||
// в формате URL'ей: <PUBLIC_BASE>/public/learning/test/<token>.
|
||||
PublicBaseURL string
|
||||
|
||||
// MinIO для видео-уроков. Структура та же что в telephony (records bucket),
|
||||
// но bucket отдельный — public-link'и читают видео через стрим-прокси.
|
||||
MinIOEndpoint string
|
||||
MinIOAccessKey string
|
||||
MinIOSecretKey string
|
||||
MinIOBucket string
|
||||
MinIOUseSSL bool
|
||||
|
||||
// RedisAddr — пустой = eventbus отключён, сервис работает без публикации
|
||||
// событий (попадание кандидата на тест не уведомит HR в realtime).
|
||||
RedisAddr string
|
||||
RedisPassword string
|
||||
RedisDB int
|
||||
|
||||
PodName string
|
||||
}
|
||||
|
||||
func Load() *Config {
|
||||
return &Config{
|
||||
ServerPort: envStr("SERVER_PORT", "3001"),
|
||||
DatabaseURL: envStr("DATABASE_URL", "postgres://learning:learning@localhost:5432/learning?sslmode=disable"),
|
||||
MigrationsDir: envStr("MIGRATIONS_DIR", "/migrations"),
|
||||
InternalAPIKey: envStr("INTERNAL_API_KEY", envStr("PORTAL_INTERNAL_API_KEY", "")),
|
||||
PortalURL: envStr("PORTAL_URL", ""),
|
||||
PublicBaseURL: envStr("PUBLIC_BASE_URL", ""),
|
||||
MinIOEndpoint: envStr("MINIO_ENDPOINT", ""),
|
||||
MinIOAccessKey: envStr("MINIO_ACCESS_KEY", ""),
|
||||
MinIOSecretKey: envStr("MINIO_SECRET_KEY", ""),
|
||||
MinIOBucket: envStr("MINIO_BUCKET", "learning-videos"),
|
||||
MinIOUseSSL: envBool("MINIO_USE_SSL", false),
|
||||
RedisAddr: envStr("REDIS_ADDR", ""),
|
||||
RedisPassword: envStr("REDIS_PASSWORD", ""),
|
||||
RedisDB: envInt("REDIS_DB", 0),
|
||||
PodName: envStr("POD_NAME", hostname()),
|
||||
}
|
||||
}
|
||||
|
||||
func envInt(key string, def int) int {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if n, err := strconv.Atoi(v); err == nil {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func envBool(key string, def bool) bool {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if b, err := strconv.ParseBool(v); err == nil {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func envStr(key, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func hostname() string {
|
||||
h, err := os.Hostname()
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
}
|
||||
return h
|
||||
}
|
||||
Reference in New Issue
Block a user