Add Ubuntu deployment: scripts, SSL, systemd and setup.sh
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Env and secrets
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
*.exe
|
||||||
|
shop
|
||||||
|
/vendor/
|
||||||
|
|
||||||
|
# Docs and dev
|
||||||
|
README.md
|
||||||
|
scripts/
|
||||||
+10
-2
@@ -10,5 +10,13 @@ ADMIN_PASSWORD=change_me
|
|||||||
# Секрет для сессий (обязательно смените в продакшене)
|
# Секрет для сессий (обязательно смените в продакшене)
|
||||||
SESSION_SECRET=your_random_secret_key_here
|
SESSION_SECRET=your_random_secret_key_here
|
||||||
|
|
||||||
# Domain for Caddy SSL (production)
|
# Часовой пояс сервера (Ubuntu)
|
||||||
DOMAIN=shop.example.com
|
TZ=Europe/Moscow
|
||||||
|
|
||||||
|
# --- Продакшен на Ubuntu с доменом ---
|
||||||
|
# DOMAIN=shop.example.com
|
||||||
|
# CADDY_EMAIL=admin@example.com
|
||||||
|
# CADDYFILE=./Caddyfile.prod
|
||||||
|
|
||||||
|
# Для доступа по IP без домена оставьте DOMAIN закомментированным
|
||||||
|
# и используйте Caddyfile по умолчанию (HTTP на порту 80)
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
|
*.sh text eol=lf
|
||||||
@@ -1,14 +1,5 @@
|
|||||||
{
|
# HTTP — доступ по IP или localhost (без домена)
|
||||||
admin localhost:2019
|
|
||||||
}
|
|
||||||
|
|
||||||
# Локальная разработка — HTTP на порту 80
|
|
||||||
:80 {
|
:80 {
|
||||||
|
encode gzip
|
||||||
reverse_proxy app:8080
|
reverse_proxy app:8080
|
||||||
}
|
}
|
||||||
|
|
||||||
# Продакшен — автоматический SSL от Let's Encrypt
|
|
||||||
# Раскомментируйте и закомментируйте блок :80 выше
|
|
||||||
# {$DOMAIN} {
|
|
||||||
# reverse_proxy app:8080
|
|
||||||
# }
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
email {$CADDY_EMAIL}
|
||||||
|
}
|
||||||
|
|
||||||
|
{$DOMAIN} {
|
||||||
|
encode gzip
|
||||||
|
reverse_proxy app:8080
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@ FROM alpine:3.21
|
|||||||
|
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|
||||||
|
ENV TZ=UTC
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY --from=builder /shop /app/shop
|
COPY --from=builder /shop /app/shop
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
.PHONY: help install deploy up down logs restart ssl update
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Shop — команды для Ubuntu"
|
||||||
|
@echo ""
|
||||||
|
@echo " make install — установить Docker (sudo)"
|
||||||
|
@echo " make deploy — первый запуск проекта"
|
||||||
|
@echo " make up — поднять контейнеры"
|
||||||
|
@echo " make down — остановить контейнеры"
|
||||||
|
@echo " make logs — логи всех сервисов"
|
||||||
|
@echo " make restart — перезапуск"
|
||||||
|
@echo " make ssl — включить SSL (DOMAIN=... EMAIL=...)"
|
||||||
|
@echo " make update — git pull + пересборка"
|
||||||
|
|
||||||
|
install:
|
||||||
|
sudo ./scripts/install-ubuntu.sh
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
chmod +x scripts/*.sh
|
||||||
|
./scripts/deploy.sh
|
||||||
|
|
||||||
|
up:
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
down:
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
logs:
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
restart:
|
||||||
|
docker compose restart
|
||||||
|
|
||||||
|
ssl:
|
||||||
|
@test -n "$(DOMAIN)" || (echo "Укажите DOMAIN=shop.example.com EMAIL=admin@example.com"; exit 1)
|
||||||
|
./scripts/enable-ssl.sh "$(DOMAIN)" "$(EMAIL)"
|
||||||
|
|
||||||
|
update:
|
||||||
|
./scripts/update.sh
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
Главная страница интернет-магазина на **Go**, **PostgreSQL 17**, **Docker Compose** и **Caddy** (обратный прокси + SSL).
|
Главная страница интернет-магазина на **Go**, **PostgreSQL 17**, **Docker Compose** и **Caddy** (обратный прокси + SSL).
|
||||||
|
|
||||||
|
Проект настроен для развёртывания на **Ubuntu 22.04 / 24.04**.
|
||||||
|
|
||||||
## Стек
|
## Стек
|
||||||
|
|
||||||
| Компонент | Технология |
|
| Компонент | Технология |
|
||||||
@@ -9,20 +11,74 @@
|
|||||||
| Backend | Go 1.23, `net/http`, `pgx/v5` |
|
| Backend | Go 1.23, `net/http`, `pgx/v5` |
|
||||||
| База данных | PostgreSQL 17 |
|
| База данных | PostgreSQL 17 |
|
||||||
| Прокси / SSL | Caddy 2 |
|
| Прокси / SSL | Caddy 2 |
|
||||||
|
| ОС | Ubuntu 22.04 / 24.04 |
|
||||||
| Контейнеризация | Docker Compose |
|
| Контейнеризация | Docker Compose |
|
||||||
|
|
||||||
## Быстрый старт
|
## Быстрый старт на Ubuntu
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Скопировать переменные окружения
|
# Клонировать репозиторий
|
||||||
cp .env.example .env
|
git clone https://git.evilfox.cc/test2/shop-go.git /opt/shop
|
||||||
|
cd /opt/shop
|
||||||
|
|
||||||
# Запустить все сервисы
|
# 1. Установить Docker (один раз, с sudo)
|
||||||
docker compose up --build -d
|
sudo ./scripts/install-ubuntu.sh
|
||||||
|
|
||||||
# Открыть в браузере
|
# Перелогиниться или:
|
||||||
# http://localhost
|
newgrp docker
|
||||||
# http://localhost/admin/login — админка
|
|
||||||
|
# 2. Запустить магазин
|
||||||
|
chmod +x scripts/*.sh
|
||||||
|
./scripts/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Откройте в браузере:
|
||||||
|
- `http://IP_СЕРВЕРА` — магазин
|
||||||
|
- `http://IP_СЕРВЕРА/admin/login` — админка
|
||||||
|
|
||||||
|
Или через Make:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make install # sudo, установка Docker
|
||||||
|
make deploy # первый запуск
|
||||||
|
make logs # просмотр логов
|
||||||
|
```
|
||||||
|
|
||||||
|
## Продакшен с доменом и SSL
|
||||||
|
|
||||||
|
1. Настройте DNS: A-запись домена → IP сервера Ubuntu
|
||||||
|
2. Откройте порты 80 и 443 (скрипт `install-ubuntu.sh` настраивает ufw)
|
||||||
|
3. Включите SSL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/enable-ssl.sh shop.example.com admin@example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
Или:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make ssl DOMAIN=shop.example.com EMAIL=admin@example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
Caddy автоматически получит сертификат Let's Encrypt.
|
||||||
|
|
||||||
|
Вручную через `.env`:
|
||||||
|
|
||||||
|
```env
|
||||||
|
DOMAIN=shop.example.com
|
||||||
|
CADDY_EMAIL=admin@example.com
|
||||||
|
CADDYFILE=./Caddyfile.prod
|
||||||
|
```
|
||||||
|
|
||||||
|
Затем: `docker compose up -d --force-recreate caddy`
|
||||||
|
|
||||||
|
## Автозапуск при загрузке Ubuntu
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp systemd/shop.service /etc/systemd/system/
|
||||||
|
# Отредактируйте WorkingDirectory, если проект не в /opt/shop
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now shop
|
||||||
```
|
```
|
||||||
|
|
||||||
## Админ
|
## Админ
|
||||||
@@ -35,7 +91,9 @@ ADMIN_PASSWORD=change_me
|
|||||||
SESSION_SECRET=your_random_secret_key_here
|
SESSION_SECRET=your_random_secret_key_here
|
||||||
```
|
```
|
||||||
|
|
||||||
Вход: **http://localhost/admin/login**
|
Скрипт `deploy.sh` генерирует пароли автоматически при первом запуске.
|
||||||
|
|
||||||
|
Вход: `/admin/login`
|
||||||
|
|
||||||
При изменении `ADMIN_PASSWORD` пароль обновится в БД при следующем запуске.
|
При изменении `ADMIN_PASSWORD` пароль обновится в БД при следующем запуске.
|
||||||
|
|
||||||
@@ -46,29 +104,26 @@ SESSION_SECRET=your_random_secret_key_here
|
|||||||
| Caddy | 80, 443 | Обратный прокси, SSL |
|
| Caddy | 80, 443 | Обратный прокси, SSL |
|
||||||
| App | 8080 (внутри) | Go-приложение |
|
| App | 8080 (внутри) | Go-приложение |
|
||||||
| PostgreSQL | 5432 (внутри) | База данных |
|
| PostgreSQL | 5432 (внутри) | База данных |
|
||||||
| Caddy Admin | 2019 | Панель управления Caddy |
|
|
||||||
|
|
||||||
## SSL в продакшене
|
## Обновление на сервере
|
||||||
|
|
||||||
1. Укажите домен в `.env`:
|
|
||||||
```
|
|
||||||
DOMAIN=shop.example.com
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Обновите `Caddyfile` — раскомментируйте блок с `{$DOMAIN}` и закомментируйте `:80`.
|
|
||||||
|
|
||||||
3. Убедитесь, что порты 80 и 443 открыты. Caddy автоматически получит сертификат Let's Encrypt.
|
|
||||||
|
|
||||||
## Локальная разработка без Docker
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Запустить только PostgreSQL
|
./scripts/update.sh
|
||||||
|
# или
|
||||||
|
make update
|
||||||
|
```
|
||||||
|
|
||||||
|
## Локальная разработка (Ubuntu)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Только PostgreSQL в Docker
|
||||||
docker compose up db -d
|
docker compose up db -d
|
||||||
|
|
||||||
# Установить зависимости и запустить приложение
|
export DB_HOST=localhost DB_USER=shop DB_PASSWORD=shop_secret DB_NAME=shop
|
||||||
|
export ADMIN_EMAIL=admin@shop.local ADMIN_PASSWORD=change_me
|
||||||
|
|
||||||
go mod tidy
|
go mod tidy
|
||||||
go run ./cmd/shop
|
go run ./cmd/shop
|
||||||
|
|
||||||
# http://localhost:8080
|
# http://localhost:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -76,15 +131,19 @@ go run ./cmd/shop
|
|||||||
|
|
||||||
```
|
```
|
||||||
shop/
|
shop/
|
||||||
├── cmd/shop/main.go # Точка входа
|
├── cmd/shop/main.go
|
||||||
├── internal/
|
├── internal/
|
||||||
│ ├── database/ # Подключение к PostgreSQL
|
├── migrations/
|
||||||
│ ├── handlers/ # HTTP-обработчики, шаблоны, статика
|
├── scripts/
|
||||||
│ ├── models/ # Модели данных
|
│ ├── install-ubuntu.sh # установка Docker + ufw
|
||||||
│ └── repository/ # Запросы к БД
|
│ ├── deploy.sh # первый запуск
|
||||||
├── migrations/ # SQL-миграции
|
│ ├── enable-ssl.sh # SSL для домена
|
||||||
├── Caddyfile # Конфигурация Caddy
|
│ └── update.sh # обновление
|
||||||
|
├── systemd/shop.service # автозапуск
|
||||||
|
├── Caddyfile # HTTP (IP / localhost)
|
||||||
|
├── Caddyfile.prod # HTTPS (домен)
|
||||||
├── docker-compose.yml
|
├── docker-compose.yml
|
||||||
|
├── Makefile
|
||||||
└── Dockerfile
|
└── Dockerfile
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -108,3 +167,9 @@ docker compose down
|
|||||||
# С удалением данных БД
|
# С удалением данных БД
|
||||||
docker compose down -v
|
docker compose down -v
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Требования к серверу Ubuntu
|
||||||
|
|
||||||
|
- 1 CPU, 1 GB RAM (минимум)
|
||||||
|
- Ubuntu 22.04 или 24.04 LTS
|
||||||
|
- Открытые порты 22 (SSH), 80, 443
|
||||||
|
|||||||
+2
-1
@@ -13,6 +13,7 @@ import (
|
|||||||
"shop/internal/config"
|
"shop/internal/config"
|
||||||
"shop/internal/database"
|
"shop/internal/database"
|
||||||
"shop/internal/handlers"
|
"shop/internal/handlers"
|
||||||
|
"shop/internal/middleware"
|
||||||
"shop/internal/repository"
|
"shop/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ func main() {
|
|||||||
addr := ":" + getEnv("APP_PORT", "8080")
|
addr := ":" + getEnv("APP_PORT", "8080")
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: mux,
|
Handler: middleware.TrustProxy(mux),
|
||||||
ReadTimeout: 10 * time.Second,
|
ReadTimeout: 10 * time.Second,
|
||||||
WriteTimeout: 30 * time.Second,
|
WriteTimeout: 30 * time.Second,
|
||||||
IdleTimeout: 60 * time.Second,
|
IdleTimeout: 60 * time.Second,
|
||||||
|
|||||||
+7
-8
@@ -7,6 +7,7 @@ services:
|
|||||||
POSTGRES_USER: ${DB_USER:-shop}
|
POSTGRES_USER: ${DB_USER:-shop}
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-shop_secret}
|
POSTGRES_PASSWORD: ${DB_PASSWORD:-shop_secret}
|
||||||
POSTGRES_DB: ${DB_NAME:-shop}
|
POSTGRES_DB: ${DB_NAME:-shop}
|
||||||
|
TZ: ${TZ:-UTC}
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
- ./migrations/001_init.sql:/docker-entrypoint-initdb.d/001_init.sql:ro
|
- ./migrations/001_init.sql:/docker-entrypoint-initdb.d/001_init.sql:ro
|
||||||
@@ -25,21 +26,17 @@ services:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: shop-app
|
container_name: shop-app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
env_file: .env
|
||||||
environment:
|
environment:
|
||||||
DB_HOST: db
|
DB_HOST: db
|
||||||
DB_PORT: "5432"
|
DB_PORT: "5432"
|
||||||
DB_USER: ${DB_USER:-shop}
|
|
||||||
DB_PASSWORD: ${DB_PASSWORD:-shop_secret}
|
|
||||||
DB_NAME: ${DB_NAME:-shop}
|
|
||||||
APP_PORT: "8080"
|
APP_PORT: "8080"
|
||||||
ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@shop.local}
|
|
||||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-change_me}
|
|
||||||
SESSION_SECRET: ${SESSION_SECRET:-dev_session_secret}
|
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
networks:
|
networks:
|
||||||
- shop-net
|
- shop-net
|
||||||
|
init: true
|
||||||
|
|
||||||
caddy:
|
caddy:
|
||||||
image: caddy:2-alpine
|
image: caddy:2-alpine
|
||||||
@@ -48,17 +45,19 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
- "443:443"
|
||||||
- "2019:2019"
|
env_file: .env
|
||||||
environment:
|
environment:
|
||||||
DOMAIN: ${DOMAIN:-localhost}
|
DOMAIN: ${DOMAIN:-localhost}
|
||||||
|
CADDY_EMAIL: ${CADDY_EMAIL:-}
|
||||||
volumes:
|
volumes:
|
||||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
- ${CADDYFILE:-./Caddyfile}:/etc/caddy/Caddyfile:ro
|
||||||
- caddy_data:/data
|
- caddy_data:/data
|
||||||
- caddy_config:/config
|
- caddy_config:/config
|
||||||
depends_on:
|
depends_on:
|
||||||
- app
|
- app
|
||||||
networks:
|
networks:
|
||||||
- shop-net
|
- shop-net
|
||||||
|
init: true
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
|
|||||||
@@ -58,27 +58,36 @@ func (s *Session) Validate(token string) (string, bool) {
|
|||||||
return email, true
|
return email, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) SetCookie(w http.ResponseWriter, token string) {
|
func (s *Session) SetCookie(w http.ResponseWriter, r *http.Request, token string) {
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
Name: cookieName,
|
Name: cookieName,
|
||||||
Value: token,
|
Value: token,
|
||||||
Path: "/admin",
|
Path: "/admin",
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
|
Secure: s.isSecure(r),
|
||||||
SameSite: http.SameSiteLaxMode,
|
SameSite: http.SameSiteLaxMode,
|
||||||
MaxAge: int(sessionTTL.Seconds()),
|
MaxAge: int(sessionTTL.Seconds()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) ClearCookie(w http.ResponseWriter) {
|
func (s *Session) ClearCookie(w http.ResponseWriter, r *http.Request) {
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
Name: cookieName,
|
Name: cookieName,
|
||||||
Value: "",
|
Value: "",
|
||||||
Path: "/admin",
|
Path: "/admin",
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
|
Secure: s.isSecure(r),
|
||||||
MaxAge: -1,
|
MaxAge: -1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Session) isSecure(r *http.Request) bool {
|
||||||
|
if r.TLS != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return strings.EqualFold(r.Header.Get("X-Forwarded-Proto"), "https")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Session) FromRequest(r *http.Request) (string, bool) {
|
func (s *Session) FromRequest(r *http.Request) (string, bool) {
|
||||||
c, err := r.Cookie(cookieName)
|
c, err := r.Cookie(cookieName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ func (h *AdminHandler) Login(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h.session.SetCookie(w, token)
|
h.session.SetCookie(w, r, token)
|
||||||
http.Redirect(w, r, "/admin/", http.StatusSeeOther)
|
http.Redirect(w, r, "/admin/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *AdminHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
func (h *AdminHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
||||||
h.session.ClearCookie(w)
|
h.session.ClearCookie(w, r)
|
||||||
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TrustProxy восстанавливает схему HTTPS за Caddy/nginx.
|
||||||
|
func TrustProxy(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if proto := r.Header.Get("X-Forwarded-Proto"); strings.EqualFold(proto, "https") {
|
||||||
|
r.URL.Scheme = "https"
|
||||||
|
}
|
||||||
|
if host := r.Header.Get("X-Forwarded-Host"); host != "" {
|
||||||
|
r.Host = host
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "${ROOT_DIR}"
|
||||||
|
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
echo "Docker не найден. Сначала: sudo ./scripts/install-ubuntu.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f .env ]]; then
|
||||||
|
cp .env.example .env
|
||||||
|
SECRET=$(openssl rand -hex 32)
|
||||||
|
DB_PASS=$(openssl rand -hex 16)
|
||||||
|
sed -i "s/your_random_secret_key_here/${SECRET}/" .env
|
||||||
|
sed -i "s/DB_PASSWORD=shop_secret/DB_PASSWORD=${DB_PASS}/" .env
|
||||||
|
sed -i "s/ADMIN_PASSWORD=change_me/ADMIN_PASSWORD=$(openssl rand -base64 18 | tr -d '/+=')/" .env
|
||||||
|
echo "Создан .env с автоматически сгенерированными паролями."
|
||||||
|
echo "Проверьте ADMIN_EMAIL и DOMAIN перед продакшеном."
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker compose pull --ignore-buildable 2>/dev/null || true
|
||||||
|
docker compose up --build -d
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Сервисы запущены."
|
||||||
|
echo " Магазин: http://$(hostname -I | awk '{print $1}')"
|
||||||
|
echo " Админка: http://$(hostname -I | awk '{print $1}')/admin/login"
|
||||||
|
echo ""
|
||||||
|
echo "Логи: docker compose logs -f"
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Переключение Caddy на продакшен-режим с SSL (Ubuntu + домен)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "${ROOT_DIR}"
|
||||||
|
|
||||||
|
if [[ ! -f .env ]]; then
|
||||||
|
echo "Сначала запустите: ./scripts/deploy.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOMAIN="${1:-}"
|
||||||
|
EMAIL="${2:-}"
|
||||||
|
|
||||||
|
if [[ -z "${DOMAIN}" || -z "${EMAIL}" ]]; then
|
||||||
|
echo "Использование: ./scripts/enable-ssl.sh <домен> <email>"
|
||||||
|
echo "Пример: ./scripts/enable-ssl.sh shop.example.com admin@example.com"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
set_env() {
|
||||||
|
local key="$1" val="$2"
|
||||||
|
if grep -q "^${key}=" .env; then
|
||||||
|
sed -i "s|^${key}=.*|${key}=${val}|" .env
|
||||||
|
else
|
||||||
|
echo "${key}=${val}" >> .env
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_env DOMAIN "${DOMAIN}"
|
||||||
|
set_env CADDY_EMAIL "${EMAIL}"
|
||||||
|
set_env CADDYFILE "./Caddyfile.prod"
|
||||||
|
|
||||||
|
docker compose up -d --force-recreate caddy
|
||||||
|
|
||||||
|
echo "SSL включён для https://${DOMAIN}"
|
||||||
|
echo "Убедитесь, что DNS A-запись указывает на этот сервер."
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Установка Docker и Docker Compose на Ubuntu 22.04 / 24.04
|
||||||
|
|
||||||
|
if [[ "${EUID}" -ne 0 ]]; then
|
||||||
|
echo "Запустите с sudo: sudo ./scripts/install-ubuntu.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -qi ubuntu /etc/os-release 2>/dev/null; then
|
||||||
|
echo "Скрипт предназначен для Ubuntu."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y ca-certificates curl gnupg ufw
|
||||||
|
|
||||||
|
# Docker official repo
|
||||||
|
install -m 0755 -d /etc/apt/keyrings
|
||||||
|
if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
|
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo \
|
||||||
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||||
|
$(. /etc/os-release && echo "${VERSION_CODENAME}") stable" \
|
||||||
|
> /etc/apt/sources.list.d/docker.list
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
|
||||||
|
systemctl enable --now docker
|
||||||
|
|
||||||
|
# Firewall
|
||||||
|
ufw allow OpenSSH
|
||||||
|
ufw allow 80/tcp
|
||||||
|
ufw allow 443/tcp
|
||||||
|
ufw --force enable
|
||||||
|
|
||||||
|
SUDO_USER_NAME="${SUDO_USER:-$USER}"
|
||||||
|
if id "${SUDO_USER_NAME}" &>/dev/null; then
|
||||||
|
usermod -aG docker "${SUDO_USER_NAME}"
|
||||||
|
echo "Пользователь ${SUDO_USER_NAME} добавлен в группу docker."
|
||||||
|
echo "Перелогиньтесь или выполните: newgrp docker"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Docker установлен: $(docker --version)"
|
||||||
|
echo "Compose: $(docker compose version)"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "${ROOT_DIR}"
|
||||||
|
|
||||||
|
git pull origin main
|
||||||
|
docker compose up --build -d
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
echo "Обновление завершено."
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Быстрая установка на Ubuntu — запуск: sudo bash setup.sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
if [[ "${EUID}" -ne 0 ]]; then
|
||||||
|
echo "Запустите: sudo bash setup.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
bash scripts/install-ubuntu.sh
|
||||||
|
|
||||||
|
REAL_USER="${SUDO_USER:-ubuntu}"
|
||||||
|
if id "${REAL_USER}" &>/dev/null; then
|
||||||
|
sudo -u "${REAL_USER}" bash scripts/deploy.sh
|
||||||
|
else
|
||||||
|
bash scripts/deploy.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Готово! Откройте http://$(hostname -I | awk '{print $1}')"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Shop internet store (Docker Compose)
|
||||||
|
Requires=docker.service
|
||||||
|
After=docker.service network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
WorkingDirectory=/opt/shop
|
||||||
|
ExecStart=/usr/bin/docker compose up -d --remove-orphans
|
||||||
|
ExecStop=/usr/bin/docker compose down
|
||||||
|
TimeoutStartSec=300
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user