Обновлена среда разработки и добавлен новый функционал в сервер

- Улучшена среда разработки, поправлены косяки старта дебагера

- Добавлен логер в приложение

- Добавлен cli framework
This commit is contained in:
Michael Makarochkin 2025-02-28 02:47:17 +03:00
parent ed623c3236
commit f6521b9332
16 changed files with 289 additions and 54 deletions

View File

@ -1 +0,0 @@
out.log

View File

@ -1,3 +0,0 @@
module pink_fox
go 1.24.0

View File

@ -1,23 +0,0 @@
package main
import (
"fmt"
"net/http"
"os"
)
func main() {
http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "ok")
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "Hello world!")
})
fmt.Println("Starting server at port 12001...")
if err := http.ListenAndServe(":12001", nil); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View File

@ -9,8 +9,8 @@ services:
- "12001:12001" - "12001:12001"
- "12002:2345" - "12002:2345"
volumes: volumes:
- ./application:/app - ./pink_fox_app:/app
- ./dist:/app/dist - ./dist:/dist
- ./environment:/var/environment/pink_fox - ./environment:/var/environment/pink_fox
- ./log:/var/log/pink_fox - ./log:/var/log/pink_fox
- ./.storage/go:/go - ./.storage/go:/go

2
pink_fox_app/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
out.log
err.log

11
pink_fox_app/go.mod Normal file
View File

@ -0,0 +1,11 @@
module pink_fox
go 1.24.0
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

13
pink_fox_app/go.sum Normal file
View File

@ -0,0 +1,13 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,47 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"os"
)
var (
port = 12001
config = "/var/environment/pink_fox/config.yml"
rootCmd = &cobra.Command{
Use: "pink_fox",
Short: "Розовый лис\nСквозь ссылок паутину —\nСвет в ночи горит.",
}
serverCmd = &cobra.Command{
Use: "server",
Short: "Запуск сервера",
Run: func(cmd *cobra.Command, args []string) {
err := NewServer().Execute(config, port)
if err != nil {
exit(err)
}
},
}
)
func init() {
serverCmd.Flags().IntVarP(&port, "port", "p", port, "Порт доступ к приложению")
serverCmd.Flags().StringVarP(&config, "config", "c", config, "Путь к файлу конфигурации")
rootCmd.AddCommand(serverCmd)
}
func Execute() error {
if err := rootCmd.Execute(); err != nil {
return fmt.Errorf("cmd: %s", err)
}
return nil
}
func exit(err error) {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

View File

@ -0,0 +1,57 @@
package cmd
import (
"database/sql"
"fmt"
"net/http"
conf2 "pink_fox/internal/app/config"
"pink_fox/internal/app/db"
"pink_fox/internal/app/log"
)
type Server struct {
}
func NewServer() *Server {
return &Server{}
}
func (it *Server) Execute(defaultConfig string, defaultPort int) error {
conf, err := conf2.LoadConfig(defaultConfig, defaultPort)
if err != nil {
exit(err)
}
conn, err := db.CreateConnection(&conf.Db)
if err != nil {
exit(err)
}
defer func(conn *sql.DB) {
_ = conn.Close()
}(conn)
logger, err := log.NewLogger(conf.LogFile)
if err != nil {
exit(err)
}
defer func(logger *log.Logger) {
_ = logger.Close()
}(logger)
// TODO доделать
http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "ok")
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "Hello world!")
})
fmt.Printf("Starting server at port %d...", conf.Port)
if err = http.ListenAndServe(fmt.Sprintf(":%d", conf.Port), nil); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,41 @@
package config
import (
"fmt"
"gopkg.in/yaml.v3"
"os"
)
type DB struct {
Host string `yaml:"host"`
User string `yaml:"user"`
Password string `yaml:"password"`
Port string `yaml:"port"`
Database string `yaml:"database"`
}
type Config struct {
Port int `yaml:"port"`
Db DB `yaml:"db"`
LogFile string `yaml:"logFile"`
}
func LoadConfig(defaultConfig string, defaultPort int) (*Config, error) {
data, err := os.ReadFile(defaultConfig)
if err != nil {
return nil, fmt.Errorf("ошибка чтения файла конфига: %s", err)
}
var config Config
err = yaml.Unmarshal(data, &config)
if err != nil {
return nil, fmt.Errorf("не получилось распарсить конфиг: %s", err)
}
return setDefaultValue(&config, defaultPort), nil
}
func setDefaultValue(config *Config, defaultPort int) *Config {
if config.Port == 0 {
config.Port = defaultPort
}
return config
}

View File

@ -0,0 +1,30 @@
package db
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
"pink_fox/internal/app/config"
"time"
)
func CreateConnection(dbConf *config.DB) (*sql.DB, error) {
psqlInfo := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
dbConf.Host, dbConf.Port, dbConf.User, dbConf.Password, dbConf.Database)
var err error
var db *sql.DB
for i := 0; i < 10; i++ {
db, err = sql.Open("postgres", psqlInfo)
if err == nil {
err = db.Ping()
if err == nil {
return db, nil
}
}
time.Sleep(200 * time.Millisecond)
}
return nil, fmt.Errorf("не удалось подключиться к базе данных: %v", err)
}

View File

@ -0,0 +1,30 @@
package log
import (
"fmt"
"log/slog"
"os"
)
type Logger struct {
file *os.File
Logger *slog.Logger
}
func NewLogger(path string) (*Logger, error) {
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return nil, fmt.Errorf("не удалось открыть файл лога: %s", err)
}
logger := slog.New(slog.NewTextHandler(file, nil))
return &Logger{
file: file,
Logger: logger,
}, nil
}
func (it *Logger) Close() error {
return it.file.Close()
}

View File

@ -0,0 +1,3 @@
package timer
// TODO

15
pink_fox_app/main.go Normal file
View File

@ -0,0 +1,15 @@
package main
import (
"fmt"
"os"
"pink_fox/internal/app/cmd"
)
func main() {
err := cmd.Execute()
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

View File

@ -14,29 +14,32 @@ if [[ ! -d "/go/bin" ]]; then
fi fi
if [ "$ARG1" = "debug" ]; then if [ "$ARG1" = "debug" ]; then
# Приложение готовое для дебага # Собираем приложение для дебага
cd /app || exit 1 cd /app || exit 1
rm -f "./dist/local_app_name" rm -f "/dist/local_app_name"
echo "" > out.log echo "" > /app/out.log
echo "" > /app/err.log
echo "" > /app/dlv.log
pkill -f /app/dist/local_app_name pkill -f /dist/local_app_name
go build -gcflags "all=-N -l" -o ./dist/local_app_name 2> /app/out.log go build -gcflags "all=-N -l" -o /dist/local_app_name > /app/out.log 2> /app/err.log
if [ ! -s "/app/out.log" ]; then if [ ! -s "/app/err.log" ]; then
dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec /app/dist/local_app_name > /app/out.log 2>&1 dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec /dist/local_app_name server > /app/out.log 2> /app/dlv.log
fi fi
else else
# Простое приложение # Собираем приложение
cd /app || exit 1 cd /app || exit 1
rm -f "./dist/local_app_name" rm -f "/dist/local_app_name"
echo "" > out.log echo "" > /app/out.log
echo "" > /app/err.log
pkill -f /app/dist/local_app_name pkill -f /dist/local_app_name
go build -o "./dist/local_app_name" 2> /app/out.log go build -o "/dist/local_app_name" > /app/out.log 2> /app/err.log
if [ ! -s "/app/out.log" ]; then if [ ! -s "/app/err.log" ]; then
/app/dist/local_app_name > /app/out.log 2>&1 /dist/local_app_name server > /app/out.log 2> /app/err.log
fi fi
fi fi

View File

@ -6,6 +6,10 @@ import subprocess
import sys import sys
import os import os
file_out = "out.log"
file_err = "err.log"
url_ping = "http://localhost:12001/ping"
# Останавливаем контейнер без задержки # Останавливаем контейнер без задержки
subprocess.run(["docker", "compose", "stop", "site", "-t", "0"]) subprocess.run(["docker", "compose", "stop", "site", "-t", "0"])
@ -22,30 +26,36 @@ attempt_count = 0
while True: while True:
try: try:
# Отправляем запрос для проверки поднялся ли HTTP сервер # Отправляем запрос для проверки поднялся ли HTTP сервер
response = requests.get("http://localhost:12001/ping", timeout=5) response = requests.get(url_ping, timeout=5)
# Проверяем ответ
if response.status_code == 200: if response.status_code == 200:
print("Сервер запущен") print("Сервер запущен")
break break
except requests.ConnectionError: except requests.ConnectionError:
# смотрим есть ли ошибки в err.log
try: try:
with open("out.log", 'r') as file: with open(file_err, 'r') as ferr:
err_content = ferr.read().strip()
if err_content:
print("Имеется ошибка компиляции или запуска, смотри файл " + file_err)
exit(1)
except FileNotFoundError:
print("Лог файл" + file_err + " не найден, проверьте путь")
print("Текущая директория" + os.getcwd())
# смотрим есть ли запись о старте дебагера
try:
with open(file_out, 'r') as file:
content = file.read().strip() content = file.read().strip()
if content: if content:
# Лог файл не пуст, возможно, есть ошибки сборки # Лог файл не пуст, возможно запущен дебагер и ping дожидаться не стоит
if content.startswith("API server listening at: [::]:2345"): if content.startswith("API server listening at: [::]:2345"):
# Лог файл начинается с сообщения о старте дебагера, ошибок нет # Лог файл начинается с сообщения о старте дебагера, ошибок нет
break break
else:
print("Имеется ошибка компиляции, смотри файл out.log")
exit(1)
except FileNotFoundError: except FileNotFoundError:
print("Лог файл не найден, проверьте путь") print("Лог файл " + file_out + " не найден, проверьте путь")
print("Текущая директория" + os.getcwd()) print("Текущая директория" + os.getcwd())
# Если не удалось подключиться, увеличиваем счетчик попыток # Если не удалось подключиться или не обнаружен старт дебагера то увеличиваем счетчик попыток
attempt_count += 1 attempt_count += 1
print(f"Ждем... ({attempt_count})") print(f"Ждем... ({attempt_count})")