Compare commits
3 Commits
fd1ee0611b
...
5eb8e21eda
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5eb8e21eda | ||
|
|
778b48cc12 | ||
|
|
1f1354e72b |
35
.gitea/scripts/hygiene-check.sh
Normal file
35
.gitea/scripts/hygiene-check.sh
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
fail=0
|
||||||
|
|
||||||
|
while IFS= read -r -d '' path; do
|
||||||
|
base="$(basename "$path")"
|
||||||
|
case "$base" in
|
||||||
|
.DS_Store|.env)
|
||||||
|
echo "::error file=$path::tracked local-only file is forbidden"
|
||||||
|
fail=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$path" in
|
||||||
|
*node_modules/*|node_modules/*)
|
||||||
|
echo "::error file=$path::tracked node_modules content is forbidden"
|
||||||
|
fail=1
|
||||||
|
;;
|
||||||
|
*.tmp|*.temp|*.bak|*.orig|*.rej|*.zip|*.tar|*.tar.gz|*.tgz|*.rar|*.7z)
|
||||||
|
echo "::error file=$path::tracked temporary/archive artifact is forbidden"
|
||||||
|
fail=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -f "$path" ]; then
|
||||||
|
size="$(wc -c < "$path" | tr -d ' ')"
|
||||||
|
if [ "${size:-0}" -gt 52428800 ]; then
|
||||||
|
echo "::error file=$path::tracked file is larger than 50 MiB"
|
||||||
|
fail=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < <(git ls-files -z)
|
||||||
|
|
||||||
|
exit "$fail"
|
||||||
@@ -5,8 +5,15 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
hygiene:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: bash .gitea/scripts/hygiene-check.sh
|
||||||
|
|
||||||
go:
|
go:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: hygiene
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
@@ -22,6 +29,7 @@ jobs:
|
|||||||
|
|
||||||
python:
|
python:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: hygiene
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: python3 -m compileall src alembic
|
- run: python3 -m compileall src alembic
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"monitoring-tg/internal/aiservice"
|
"monitoring-tg/internal/aiservice"
|
||||||
|
"monitoring-tg/internal/dbretry"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
)
|
)
|
||||||
@@ -69,7 +70,7 @@ func main() {
|
|||||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
pool, err := pgxpool.New(ctx, cfg.databaseURL())
|
pool, err := dbretry.Connect(ctx, cfg.databaseURL(), 2*time.Minute)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("db_connect_failed", "error", err)
|
slog.Error("db_connect_failed", "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"monitoring-tg/internal/aiservice"
|
"monitoring-tg/internal/aiservice"
|
||||||
|
"monitoring-tg/internal/dbretry"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
@@ -49,6 +50,7 @@ type config struct {
|
|||||||
LLMTimeout time.Duration
|
LLMTimeout time.Duration
|
||||||
AIServiceURL string
|
AIServiceURL string
|
||||||
AIServiceToken string
|
AIServiceToken string
|
||||||
|
InternalAPIKey string
|
||||||
MinioEndpoint string
|
MinioEndpoint string
|
||||||
MinioAccessKey string
|
MinioAccessKey string
|
||||||
MinioSecretKey string
|
MinioSecretKey string
|
||||||
@@ -134,7 +136,7 @@ func main() {
|
|||||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
pool, err := pgxpool.New(ctx, cfg.databaseURL())
|
pool, err := dbretry.Connect(ctx, cfg.databaseURL(), 2*time.Minute)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("db_connect_failed", "error", err)
|
slog.Error("db_connect_failed", "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -190,6 +192,9 @@ func (a *app) serveHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
writeError(w, http.StatusNotFound, "not found")
|
writeError(w, http.StatusNotFound, "not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !a.checkInternalAuth(w, r) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
switch {
|
switch {
|
||||||
@@ -226,6 +231,18 @@ func (a *app) serveHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *app) checkInternalAuth(w http.ResponseWriter, r *http.Request) bool {
|
||||||
|
want := strings.TrimSpace(a.cfg.InternalAPIKey)
|
||||||
|
if want == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if r.Header.Get("X-Internal-Key") != want {
|
||||||
|
writeError(w, http.StatusUnauthorized, "unauthorized")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (a *app) apiPath(path string) string {
|
func (a *app) apiPath(path string) string {
|
||||||
base := strings.TrimRight(a.cfg.PublicBasePath, "/")
|
base := strings.TrimRight(a.cfg.PublicBasePath, "/")
|
||||||
if base != "" && strings.HasPrefix(path, base+"/") {
|
if base != "" && strings.HasPrefix(path, base+"/") {
|
||||||
@@ -1872,6 +1889,7 @@ func loadConfig() config {
|
|||||||
LLMTimeout: time.Duration(envInt("LLM_TIMEOUT_SECONDS", 120)) * time.Second,
|
LLMTimeout: time.Duration(envInt("LLM_TIMEOUT_SECONDS", 120)) * time.Second,
|
||||||
AIServiceURL: env("AI_SERVICE_URL", ""),
|
AIServiceURL: env("AI_SERVICE_URL", ""),
|
||||||
AIServiceToken: env("AI_SERVICE_TOKEN", ""),
|
AIServiceToken: env("AI_SERVICE_TOKEN", ""),
|
||||||
|
InternalAPIKey: env("INTERNAL_API_KEY", env("PORTAL_INTERNAL_API_KEY", "")),
|
||||||
MinioEndpoint: env("MINIO_ENDPOINT", ""),
|
MinioEndpoint: env("MINIO_ENDPOINT", ""),
|
||||||
MinioAccessKey: env("MINIO_ACCESS_KEY", ""),
|
MinioAccessKey: env("MINIO_ACCESS_KEY", ""),
|
||||||
MinioSecretKey: env("MINIO_SECRET_KEY", ""),
|
MinioSecretKey: env("MINIO_SECRET_KEY", ""),
|
||||||
|
|||||||
44
internal/dbretry/dbretry.go
Normal file
44
internal/dbretry/dbretry.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package dbretry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Connect(ctx context.Context, databaseURL string, maxWait time.Duration) (*pgxpool.Pool, error) {
|
||||||
|
deadline := time.Now().Add(maxWait)
|
||||||
|
var lastErr error
|
||||||
|
|
||||||
|
for attempt := 1; ; attempt++ {
|
||||||
|
pool, err := pgxpool.New(ctx, databaseURL)
|
||||||
|
if err == nil {
|
||||||
|
if pingErr := pool.Ping(ctx); pingErr == nil {
|
||||||
|
return pool, nil
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("ping postgres: %w", pingErr)
|
||||||
|
pool.Close()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("connect postgres: %w", err)
|
||||||
|
}
|
||||||
|
lastErr = err
|
||||||
|
|
||||||
|
if time.Now().After(deadline) {
|
||||||
|
return nil, fmt.Errorf("connect postgres after retry: %w", lastErr)
|
||||||
|
}
|
||||||
|
sleep := time.Duration(attempt) * time.Second
|
||||||
|
if sleep > 5*time.Second {
|
||||||
|
sleep = 5 * time.Second
|
||||||
|
}
|
||||||
|
timer := time.NewTimer(sleep)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
timer.Stop()
|
||||||
|
return nil, fmt.Errorf("connect postgres cancelled: %w", ctx.Err())
|
||||||
|
case <-timer.C:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ stringData:
|
|||||||
TG_PHONE: "+971524994695"
|
TG_PHONE: "+971524994695"
|
||||||
TG_SESSION_STRING: ""
|
TG_SESSION_STRING: ""
|
||||||
POSTGRES_PASSWORD: "parser"
|
POSTGRES_PASSWORD: "parser"
|
||||||
|
INTERNAL_API_KEY: "36fe89ed40c01fdc54d3cf4e3fcacc8751dc456a4a1acd394e9fed48257c5734"
|
||||||
AI_SERVICE_TOKEN: "d18bcacf9e02bae1806ee6b6eeda62b95be6a915c0a22936d9a700128b275442"
|
AI_SERVICE_TOKEN: "d18bcacf9e02bae1806ee6b6eeda62b95be6a915c0a22936d9a700128b275442"
|
||||||
MINIO_ACCESS_KEY: "admjn"
|
MINIO_ACCESS_KEY: "admjn"
|
||||||
MINIO_SECRET_KEY: "TropicalMacaw9Fantasize"
|
MINIO_SECRET_KEY: "TropicalMacaw9Fantasize"
|
||||||
|
|||||||
Reference in New Issue
Block a user