mirror of
https://github.com/Direct-Dev-Ru/go-lcg.git
synced 2025-11-16 09:39:56 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 57f51c5d0e | |||
| a78e1d24bf | |||
| d0b53607c4 | |||
| 487f3d484c |
@@ -60,6 +60,8 @@ RUN chown -R ollama:ollama /app/data 2>/dev/null || \
|
|||||||
(chown -R 1000:1000 /app/data 2>/dev/null || true)
|
(chown -R 1000:1000 /app/data 2>/dev/null || true)
|
||||||
|
|
||||||
# Настройки по умолчанию
|
# Настройки по умолчанию
|
||||||
|
ENV TZ='Asia/Omsk'
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
ENV LCG_PROVIDER=ollama
|
ENV LCG_PROVIDER=ollama
|
||||||
ENV LCG_HOST=http://127.0.0.1:11434/
|
ENV LCG_HOST=http://127.0.0.1:11434/
|
||||||
ENV LCG_MODEL=qwen2.5-coder:1.5b
|
ENV LCG_MODEL=qwen2.5-coder:1.5b
|
||||||
|
|||||||
@@ -46,27 +46,31 @@ build-all-podman: build-binaries build-podman ## Собрать бинарник
|
|||||||
|
|
||||||
run: ## Запустить контейнер (Docker)
|
run: ## Запустить контейнер (Docker)
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name $(CONTAINER_NAME) \
|
--name ${CONTAINER_NAME} \
|
||||||
-p 8080:8080 \
|
-p 8989:8080 \
|
||||||
-p 11434:11434 \
|
|
||||||
-v ollama-data:/home/ollama/.ollama \
|
-v ollama-data:/home/ollama/.ollama \
|
||||||
-v lcg-results:/app/data/results \
|
-v lcg-results:/app/data/results \
|
||||||
-v lcg-prompts:/app/data/prompts \
|
-v lcg-prompts:/app/data/prompts \
|
||||||
-v lcg-config:/app/data/config \
|
-v lcg-config:/app/data/config \
|
||||||
$(IMAGE_NAME):$(IMAGE_TAG)
|
${IMAGE_NAME}:${IMAGE_TAG}
|
||||||
@echo "Контейнер $(CONTAINER_NAME) запущен"
|
@echo "Контейнер ${CONTAINER_NAME} запущен"
|
||||||
|
|
||||||
run-podman: ## Запустить контейнер (Podman)
|
run-podman: ## Запустить контейнер (Podman)
|
||||||
|
echo "Запустить контейнер ${CONTAINER_NAME}"
|
||||||
|
echo "IMAGE_NAME: ${IMAGE_NAME}"
|
||||||
|
echo "IMAGE_TAG: ${IMAGE_TAG}"
|
||||||
|
echo "CONTAINER_NAME: ${CONTAINER_NAME}"
|
||||||
|
|
||||||
podman run -d \
|
podman run -d \
|
||||||
--name $(CONTAINER_NAME) \
|
--name ${CONTAINER_NAME} \
|
||||||
-p 8080:8080 \
|
--restart always \
|
||||||
-p 11434:11434 \
|
-p 8989:8080 \
|
||||||
-v ollama-data:/home/ollama/.ollama \
|
-v ollama-data:/home/ollama/.ollama \
|
||||||
-v lcg-results:/app/data/results \
|
-v lcg-results:/app/data/results \
|
||||||
-v lcg-prompts:/app/data/prompts \
|
-v lcg-prompts:/app/data/prompts \
|
||||||
-v lcg-config:/app/data/config \
|
-v lcg-config:/app/data/config \
|
||||||
$(IMAGE_NAME):$(IMAGE_TAG)
|
${IMAGE_NAME}:${IMAGE_TAG}
|
||||||
@echo "Контейнер $(CONTAINER_NAME) запущен"
|
@echo "Контейнер ${CONTAINER_NAME} запущен"
|
||||||
|
|
||||||
stop: ## Остановить контейнер (Docker)
|
stop: ## Остановить контейнер (Docker)
|
||||||
docker stop $(CONTAINER_NAME) || true
|
docker stop $(CONTAINER_NAME) || true
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ mkdir -p "${LCG_PROMPT_FOLDER:-/app/data/prompts}"
|
|||||||
mkdir -p "${LCG_CONFIG_FOLDER:-/app/data/config}"
|
mkdir -p "${LCG_CONFIG_FOLDER:-/app/data/config}"
|
||||||
|
|
||||||
# Настройка переменных окружения для Ollama
|
# Настройка переменных окружения для Ollama
|
||||||
export OLLAMA_HOST="${OLLAMA_HOST:-127.0.0.1}"
|
export OLLAMA_HOST="${OLLAMA_HOST:-0.0.0.0}"
|
||||||
export OLLAMA_PORT="${OLLAMA_PORT:-11434}"
|
export OLLAMA_PORT="${OLLAMA_PORT:-11434}"
|
||||||
export OLLAMA_ORIGINS="*"
|
export OLLAMA_ORIGINS="*"
|
||||||
|
|
||||||
# Настройка переменных окружения для LCG
|
# Настройка переменных окружения для LCG
|
||||||
export LCG_PROVIDER="${LCG_PROVIDER:-ollama}"
|
export LCG_PROVIDER="${LCG_PROVIDER:-ollama}"
|
||||||
export LCG_HOST="${LCG_HOST:-http://127.0.0.1:11434/}"
|
export LCG_HOST="${LCG_HOST:-http://0.0.0.0:11434/}"
|
||||||
export LCG_MODEL="${LCG_MODEL:-qwen2.5-coder:1.5b}"
|
export LCG_MODEL="${LCG_MODEL:-qwen2.5-coder:1.5b}"
|
||||||
export LCG_RESULT_FOLDER="${LCG_RESULT_FOLDER:-/app/data/results}"
|
export LCG_RESULT_FOLDER="${LCG_RESULT_FOLDER:-/app/data/results}"
|
||||||
export LCG_PROMPT_FOLDER="${LCG_PROMPT_FOLDER:-/app/data/prompts}"
|
export LCG_PROMPT_FOLDER="${LCG_PROMPT_FOLDER:-/app/data/prompts}"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v.2.0.16
|
v.2.0.18
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v.2.0.16
|
v.2.0.18
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ metadata:
|
|||||||
namespace: lcg
|
namespace: lcg
|
||||||
data:
|
data:
|
||||||
# Основные настройки
|
# Основные настройки
|
||||||
LCG_VERSION: "v.2.0.16"
|
LCG_VERSION: "v.2.0.18"
|
||||||
LCG_BASE_PATH: "/lcg"
|
LCG_BASE_PATH: "/lcg"
|
||||||
LCG_SERVER_HOST: "0.0.0.0"
|
LCG_SERVER_HOST: "0.0.0.0"
|
||||||
LCG_SERVER_PORT: "8080"
|
LCG_SERVER_PORT: "8080"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ metadata:
|
|||||||
namespace: lcg
|
namespace: lcg
|
||||||
labels:
|
labels:
|
||||||
app: lcg
|
app: lcg
|
||||||
version: v.2.0.16
|
version: v.2.0.18
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
@@ -18,7 +18,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: lcg
|
- name: lcg
|
||||||
image: kuznetcovay/lcg:v.2.0.16
|
image: kuznetcovay/lcg:v.2.0.18
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ resources:
|
|||||||
# Common labels
|
# Common labels
|
||||||
# commonLabels:
|
# commonLabels:
|
||||||
# app: lcg
|
# app: lcg
|
||||||
# version: v.2.0.16
|
# version: v.2.0.18
|
||||||
# managed-by: kustomize
|
# managed-by: kustomize
|
||||||
|
|
||||||
# Images
|
# Images
|
||||||
# images:
|
# images:
|
||||||
# - name: lcg
|
# - name: lcg
|
||||||
# newName: kuznetcovay/lcg
|
# newName: kuznetcovay/lcg
|
||||||
# newTag: v.2.0.16
|
# newTag: v.2.0.18
|
||||||
|
|||||||
@@ -127,7 +127,6 @@ func getTokenFromCookie(r *http.Request) (string, error) {
|
|||||||
func setAuthCookie(w http.ResponseWriter, token string) {
|
func setAuthCookie(w http.ResponseWriter, token string) {
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: "auth_token",
|
Name: "auth_token",
|
||||||
Domain: config.AppConfig.Server.Domain,
|
|
||||||
Value: token,
|
Value: token,
|
||||||
Path: config.AppConfig.Server.CookiePath,
|
Path: config.AppConfig.Server.CookiePath,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
@@ -136,9 +135,16 @@ func setAuthCookie(w http.ResponseWriter, token string) {
|
|||||||
MaxAge: config.AppConfig.Server.CookieTTLHours * 60 * 60,
|
MaxAge: config.AppConfig.Server.CookieTTLHours * 60 * 60,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем домен если указан
|
// Устанавливаем Domain только если это не IP адрес и не 0.0.0.0
|
||||||
|
// При доступе по IP адресу не устанавливаем Domain, иначе cookie не будет работать
|
||||||
if config.AppConfig.Server.Domain != "" {
|
if config.AppConfig.Server.Domain != "" {
|
||||||
cookie.Domain = config.AppConfig.Server.Domain
|
domain := config.AppConfig.Server.Domain
|
||||||
|
// Проверяем, не является ли домен IP адресом или 0.0.0.0
|
||||||
|
if !isIPAddress(domain) && domain != "0.0.0.0" && domain != "::" && domain != "::1" {
|
||||||
|
cookie.Domain = domain
|
||||||
|
}
|
||||||
|
// Если domain пустой, 0.0.0.0 или IP адрес - не устанавливаем Domain
|
||||||
|
// Браузер автоматически применит cookie к текущему хосту
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
@@ -156,9 +162,12 @@ func clearAuthCookie(w http.ResponseWriter) {
|
|||||||
MaxAge: -1, // Удаляем cookie
|
MaxAge: -1, // Удаляем cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем домен если указан
|
// Устанавливаем Domain только если это не IP адрес
|
||||||
if config.AppConfig.Server.Domain != "" {
|
if config.AppConfig.Server.Domain != "" {
|
||||||
cookie.Domain = config.AppConfig.Server.Domain
|
domain := config.AppConfig.Server.Domain
|
||||||
|
if !isIPAddress(domain) && domain != "0.0.0.0" && domain != "::" && domain != "::1" {
|
||||||
|
cookie.Domain = domain
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
|
|||||||
@@ -8,11 +8,20 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/direct-dev-ru/linux-command-gpt/config"
|
"github.com/direct-dev-ru/linux-command-gpt/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CSRFTokenLifetimeHours минимальное время жизни CSRF токена в часах (не менее 12 часов)
|
||||||
|
CSRFTokenLifetimeHours = 12
|
||||||
|
// CSRFTokenLifetimeSeconds минимальное время жизни CSRF токена в секундах
|
||||||
|
CSRFTokenLifetimeSeconds = CSRFTokenLifetimeHours * 60 * 60
|
||||||
|
)
|
||||||
|
|
||||||
// CSRFManager управляет CSRF токенами
|
// CSRFManager управляет CSRF токенами
|
||||||
type CSRFManager struct {
|
type CSRFManager struct {
|
||||||
secretKey []byte
|
secretKey []byte
|
||||||
@@ -113,13 +122,14 @@ func (c *CSRFManager) ValidateToken(token, userID string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем время жизни токена (24 часа)
|
// Проверяем время жизни токена (минимум 12 часов)
|
||||||
timestamp, err := parseInt64(timestampStr)
|
timestamp, err := parseInt64(timestampStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().Unix()-timestamp > 24*60*60 {
|
// Минимальное время жизни токена: 12 часов (не менее 12 часов согласно требованиям)
|
||||||
|
if time.Now().Unix()-timestamp > CSRFTokenLifetimeSeconds {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +163,7 @@ func GetCSRFTokenFromCookie(r *http.Request) string {
|
|||||||
|
|
||||||
// setCSRFCookie устанавливает CSRF токен в cookie
|
// setCSRFCookie устанавливает CSRF токен в cookie
|
||||||
func setCSRFCookie(w http.ResponseWriter, token string) {
|
func setCSRFCookie(w http.ResponseWriter, token string) {
|
||||||
|
// Минимальное время жизни токена: 12 часов (не менее 12 часов согласно требованиям)
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: "csrf_token",
|
Name: "csrf_token",
|
||||||
Value: token,
|
Value: token,
|
||||||
@@ -160,12 +171,19 @@ func setCSRFCookie(w http.ResponseWriter, token string) {
|
|||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
Secure: config.AppConfig.Server.CookieSecure,
|
Secure: config.AppConfig.Server.CookieSecure,
|
||||||
SameSite: http.SameSiteLaxMode, // Более мягкий режим для reverse proxy
|
SameSite: http.SameSiteLaxMode, // Более мягкий режим для reverse proxy
|
||||||
MaxAge: 1 * 60 * 60,
|
MaxAge: CSRFTokenLifetimeSeconds, // Минимум 12 часов в секундах
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем домен если указан
|
// Устанавливаем Domain только если это не IP адрес и не 0.0.0.0
|
||||||
|
// При доступе по IP адресу не устанавливаем Domain, иначе cookie не будет работать
|
||||||
if config.AppConfig.Server.Domain != "" {
|
if config.AppConfig.Server.Domain != "" {
|
||||||
cookie.Domain = config.AppConfig.Server.Domain
|
domain := config.AppConfig.Server.Domain
|
||||||
|
// Проверяем, не является ли домен IP адресом или 0.0.0.0
|
||||||
|
if !isIPAddress(domain) && domain != "0.0.0.0" && domain != "::" && domain != "::1" {
|
||||||
|
cookie.Domain = domain
|
||||||
|
}
|
||||||
|
// Если domain пустой, 0.0.0.0 или IP адрес - не устанавливаем Domain
|
||||||
|
// Браузер автоматически применит cookie к текущему хосту
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
@@ -183,9 +201,12 @@ func СlearCSRFCookie(w http.ResponseWriter) {
|
|||||||
MaxAge: -1,
|
MaxAge: -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем домен если указан
|
// Устанавливаем Domain только если это не IP адрес
|
||||||
if config.AppConfig.Server.Domain != "" {
|
if config.AppConfig.Server.Domain != "" {
|
||||||
cookie.Domain = config.AppConfig.Server.Domain
|
domain := config.AppConfig.Server.Domain
|
||||||
|
if !isIPAddress(domain) && domain != "0.0.0.0" && domain != "::" && domain != "::1" {
|
||||||
|
cookie.Domain = domain
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
@@ -261,3 +282,17 @@ func InitCSRFManager() error {
|
|||||||
func GetCSRFManager() *CSRFManager {
|
func GetCSRFManager() *CSRFManager {
|
||||||
return csrfManager
|
return csrfManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isIPAddress проверяет, является ли строка IPv4 адресом
|
||||||
|
func isIPAddress(s string) bool {
|
||||||
|
parts := strings.Split(s, ".")
|
||||||
|
if len(parts) != 4 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, part := range parts {
|
||||||
|
if num, err := strconv.Atoi(part); err != nil || num < 0 || num > 255 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,8 +72,9 @@ var ExecutePageCSSTemplate = template.Must(template.New("execute_css").Parse(`
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.header h1 {
|
.header h1 {
|
||||||
|
margin: 0;
|
||||||
font-size: 2.5em;
|
font-size: 2.5em;
|
||||||
margin-bottom: 10px;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
.header p {
|
.header p {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
|
|||||||
Reference in New Issue
Block a user