Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions cmd/certificate/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package main

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"flag"
"log"
"math/big"
"net"
"os"
"time"
)

func main() {
certOut := flag.String("cert", "cert.pem", "path to output certificate file")
keyOut := flag.String("key", "key.pem", "path to output private key file")
flag.Parse()

// создаём шаблон сертификата
cert := &x509.Certificate{
// указываем уникальный номер сертификата
SerialNumber: big.NewInt(1658),
// заполняем базовую информацию о владельце сертификата
Subject: pkix.Name{
Organization: []string{"Yandex.Praktikum"},
Country: []string{"RU"},
},
// разрешаем использование сертификата для 127.0.0.1 и ::1
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
// сертификат верен, начиная со времени создания
NotBefore: time.Now(),
// время жизни сертификата — 10 лет
NotAfter: time.Now().AddDate(10, 0, 0),
SubjectKeyId: []byte{1, 2, 3, 4, 6},
// устанавливаем использование ключа для цифровой подписи,
// а также клиентской и серверной авторизации
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}

// создаём новый приватный RSA-ключ длиной 4096 бит
// обратите внимание, что для генерации ключа и сертификата
// используется rand.Reader в качестве источника случайных данных
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
log.Fatalf("failed to generate RSA private key: %v", err)
}

// создаём сертификат x.509
certBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &privateKey.PublicKey, privateKey)
if err != nil {
log.Fatalf("failed to create x509 certificate: %v", err)
}

// кодируем сертификат и ключ в формате PEM, который
// используется для хранения и обмена криптографическими ключами
var certPEM bytes.Buffer
err = pem.Encode(&certPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
})
if err != nil {
log.Fatalf("failed to PEM-encode certificate: %v", err)
}

var privateKeyPEM bytes.Buffer
err = pem.Encode(&privateKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
})
if err != nil {
log.Fatalf("failed to PEM-encode RSA private key: %v", err)
}

// Сохраняем сертификат и приватный ключ в файлы
if err = os.WriteFile(*certOut, certPEM.Bytes(), 0644); err != nil {
log.Fatalf("failed to write certificate to %s: %v", *certOut, err)
}

if err = os.WriteFile(*keyOut, privateKeyPEM.Bytes(), 0644); err != nil {
log.Fatalf("failed to write private key to %s: %v", *keyOut, err)
}
}
25 changes: 15 additions & 10 deletions cmd/shortener/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package main

import (
"context"
"errors"
"fmt"
"log"
"os"
"net/http"
"os/signal"
"syscall"
"time"
Expand All @@ -29,34 +30,38 @@ func main() {
fmt.Printf("Build version: %s\nBuild date: %s\nBuild commit: %s\n", checkLinkVar(buildVersion), checkLinkVar(buildDate), checkLinkVar(buildCommit))
application, err := app.New()
if err != nil {
log.Fatalf("Error initializing application.\n%s", err.Error())
log.Fatalf("failed to initialize application (check config flags, DB connection, file permissions): %s", err.Error())
}
application.SetupRoutes()

go func() {
if err := application.Run(); err != nil {
if err.Error() != "http: Server closed" {
log.Printf("Error running application: %s", err.Error())
if !errors.Is(err, http.ErrServerClosed) {
log.Printf("HTTP server exited with unexpected error: %s", err.Error())
}
}
}()

out := make(chan os.Signal, 1)
signal.Notify(out, syscall.SIGINT, syscall.SIGTERM)
<-out
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
defer stop()

<-ctx.Done()
stop() // освобождаем ресурсы сигнального механизма досрочно

log.Print("Shutdown process is started...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)

shutCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

shut := make(chan struct{})
go func() {
application.Shutdown(ctx)
application.Shutdown(shutCtx)
close(shut)
}()
select {
case <-shut:
log.Print("Shutdown process is completed!")
case <-ctx.Done():
case <-shutCtx.Done():
log.Print("Shutdown process timeout - exiting anyway")
}
}
15 changes: 15 additions & 0 deletions config.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"server_address": ":8080",
"base_url": "http://localhost:8080",
"env": "dev",
"file_storage_path": "data.json",
"database_dsn": "",
"delete_worker_count": 2,
"delete_channel_size": 1000,
"secret_key": "my-super-secret-key-change-in-production",
"audit_file": "",
"audit_url": "",
"enable_https": false,
"cert_file": "cert.pem",
"key_file": "key.pem"
}
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ require (
github.com/bytedance/sonic v1.14.2 // indirect
github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.28.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
Expand All @@ -42,11 +44,19 @@ require (
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.57.1 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
go.uber.org/mock v0.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/arch v0.23.0 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect
Expand Down
20 changes: 20 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
Expand All @@ -29,6 +31,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
Expand Down Expand Up @@ -93,8 +97,20 @@ github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI1
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand All @@ -107,6 +123,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
Expand All @@ -126,6 +144,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down
11 changes: 10 additions & 1 deletion internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ type App struct {
router *gin.Engine
urlHandler *handler.URLHandler
deleteWorker *worker.DeleteWorker
enableTLS bool
certFile string
keyFile string
}

// New создаёт и настраивает новое приложение:
Expand Down Expand Up @@ -152,6 +155,9 @@ func New() (*App, error) {
router: router,
urlHandler: urlHandler,
deleteWorker: deleteWorker,
enableTLS: cfg.Enable_HTTPS,
certFile: cfg.CertFile,
keyFile: cfg.KeyFile,
}, nil
}

Expand Down Expand Up @@ -201,6 +207,9 @@ func (a *App) SetupRoutes() {

// Run запускает HTTP-сервер.
func (a *App) Run() error {
if a.enableTLS {
return a.server.ListenAndServeTLS(a.certFile, a.keyFile)
}
return a.server.ListenAndServe()
}

Expand All @@ -210,6 +219,6 @@ func (a *App) Shutdown(ctx context.Context) {
a.deleteWorker.Stop()
}
if err := a.server.Shutdown(ctx); err != nil {
fmt.Println("Failed shutdown. error: ", err)
fmt.Printf("failed to shutdown HTTP server gracefully: %v\n", err)
}
}
4 changes: 2 additions & 2 deletions internal/audit/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func NewFileObserver(path string) *FileObserver {
func (f *FileObserver) Notify(event AuditEvent) {
data, err := json.Marshal(event)
if err != nil {
log.Printf("audit file: failed to marshal event: %v", err)
log.Printf("audit file: failed to marshal %q event: %v", event.Action, err)
return
}
data = append(data, '\n')
Expand All @@ -38,6 +38,6 @@ func (f *FileObserver) Notify(event AuditEvent) {
defer func() { _ = file.Close() }()

if _, err := file.Write(data); err != nil {
log.Printf("audit file: failed to write event: %v", err)
log.Printf("audit file: failed to write %q event to %s: %v", event.Action, f.path, err)
}
}
4 changes: 2 additions & 2 deletions internal/audit/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ func NewHTTPObserver(url string) *HTTPObserver {
func (h *HTTPObserver) Notify(event AuditEvent) {
data, err := json.Marshal(event)
if err != nil {
log.Printf("audit http: failed to marshal event: %v", err)
log.Printf("audit http: failed to marshal %q event: %v", event.Action, err)
return
}

resp, err := h.client.Post(h.url, "application/json", bytes.NewReader(data))
if err != nil {
log.Printf("audit http: failed to send event to %s: %v", h.url, err)
log.Printf("audit http: failed to send %q event to %s: %v", event.Action, h.url, err)
return
}
defer func() { _ = resp.Body.Close() }()
Expand Down
Loading
Loading