Compare commits
10 Commits
0918affe9a
...
5e737ebdd3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e737ebdd3 | ||
|
|
59514f721a | ||
|
|
f6029f4a66 | ||
|
|
66300be495 | ||
|
|
a92e836cc1 | ||
|
|
97f25814a1 | ||
|
|
1b91b72ffb | ||
|
|
85e49c9e9f | ||
|
|
9444bab05f | ||
|
|
ae3a484139 |
@@ -1,5 +1,5 @@
|
||||
# Stage 1: Build
|
||||
FROM docker.io/library/golang:1.23-alpine AS builder
|
||||
FROM docker.io/library/golang:1.25-alpine AS builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/rideaware/admin-panel/internal/config"
|
||||
"github.com/rideaware/admin-panel/internal/database"
|
||||
@@ -17,11 +18,22 @@ import (
|
||||
// authenticated routes, and starts the HTTP server on the configured port.
|
||||
func main() {
|
||||
cfg := config.Load()
|
||||
|
||||
// Set Gin mode based on environment (default to release)
|
||||
if os.Getenv("GIN_MODE") == "" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
middleware.Init()
|
||||
database.Init(cfg)
|
||||
defer database.Close()
|
||||
|
||||
router := gin.Default()
|
||||
router := gin.New()
|
||||
router.Use(gin.Logger())
|
||||
router.Use(gin.Recovery())
|
||||
|
||||
// Trust only localhost proxy in production
|
||||
router.SetTrustedProxies([]string{"127.0.0.1", "localhost", "::1"})
|
||||
|
||||
router.LoadHTMLGlob("web/templates/*.html")
|
||||
router.Static("/static", "web/static")
|
||||
|
||||
13
go.mod
13
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/rideaware/admin-panel
|
||||
|
||||
go 1.23
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.10
|
||||
|
||||
@@ -9,8 +9,7 @@ require (
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/wneessen/go-mail v0.4.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -33,9 +32,9 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
24
go.sum
24
go.sum
@@ -23,7 +23,6 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -70,26 +69,23 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/wneessen/go-mail v0.4.0 h1:Oo4HLIV8My7G9JuZkoOX6eipXQD+ACvIqURYeIzUc88=
|
||||
github.com/wneessen/go-mail v0.4.0/go.mod h1:zxOlafWCP/r6FEhAaRgH4IC1vg2YXxO0Nar9u0IScZ8=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/smtp"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rideaware/admin-panel/internal/config"
|
||||
"github.com/rideaware/admin-panel/internal/database"
|
||||
|
||||
"github.com/wneessen/go-mail"
|
||||
)
|
||||
|
||||
// SendUpdate sends a newsletter with the given subject and body to all subscriber emails stored in the database.
|
||||
// It returns a human-readable status message and, when subscriber retrieval fails, the underlying error.
|
||||
// - If retrieving subscribers fails: returns "Failed to retrieve subscribers" and the error.
|
||||
// - If no subscribers are found: returns "No subscribers found." and nil.
|
||||
// - If sending to a specific subscriber fails: returns "Failed to send to <email>" and nil.
|
||||
// - On success: returns "Email has been sent to all subscribers." and nil.
|
||||
// Note: logging the newsletter entry in the database is attempted after sending and any logging failure is non-fatal.
|
||||
func SendUpdate(subject, body string) (string, error) {
|
||||
subscribers, err := database.GetAllEmails()
|
||||
if err != nil {
|
||||
@@ -28,6 +22,7 @@ func SendUpdate(subject, body string) (string, error) {
|
||||
if len(subscribers) == 0 {
|
||||
return "No subscribers found.", nil
|
||||
}
|
||||
|
||||
var succeeded, failed int
|
||||
for _, email := range subscribers {
|
||||
if send(subject, body, email) {
|
||||
@@ -36,79 +31,119 @@ func SendUpdate(subject, body string) (string, error) {
|
||||
failed++
|
||||
}
|
||||
}
|
||||
|
||||
if err := database.LogNewsletter(subject, body); err != nil {
|
||||
log.Printf("Error logging newsletter: %v", err)
|
||||
}
|
||||
|
||||
if failed == 0 {
|
||||
return fmt.Sprintf("Email sent to all %d subscribers.", succeeded), nil
|
||||
}
|
||||
return fmt.Sprintf("Sent to %d/%d subscribers; %d failed.", succeeded, succeeded+failed, failed), nil
|
||||
}
|
||||
|
||||
// send constructs and sends an HTML newsletter update to the specified recipient using the current SMTP configuration.
|
||||
// It embeds an unsubscribe link for the recipient and returns true if the message was sent successfully, false if client creation, message setup, or sending fails.
|
||||
// send constructs and sends an HTML newsletter update to the specified recipient
|
||||
func send(subject, body, recipient string) bool {
|
||||
cfg := config.Current
|
||||
|
||||
client, err := mail.NewClient(
|
||||
cfg.SMTPServer,
|
||||
mail.WithPort(cfg.SMTPPort),
|
||||
mail.WithSMTPAuth(mail.SMTPAuthPlain),
|
||||
mail.WithUsername(cfg.SMTPUser),
|
||||
mail.WithPassword(cfg.SMTPPassword),
|
||||
mail.WithTimeout(10*time.Second),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create mail client: %v", err)
|
||||
return false
|
||||
}
|
||||
defer client.Close()
|
||||
log.Printf("Attempting to send email to %s via %s:%d", recipient, cfg.SMTPServer, cfg.SMTPPort)
|
||||
|
||||
m := mail.NewMsg()
|
||||
if err := m.From(cfg.SenderEmail); err != nil {
|
||||
log.Printf("Failed to set from: %v", err)
|
||||
return false
|
||||
}
|
||||
if err := m.To(recipient); err != nil {
|
||||
log.Printf("Failed to set to: %v", err)
|
||||
return false
|
||||
}
|
||||
m.Subject(subject)
|
||||
addr := fmt.Sprintf("%s:%d", cfg.SMTPServer, cfg.SMTPPort)
|
||||
|
||||
unsubLink := fmt.Sprintf("https://%s/unsubscribe?email=%s",
|
||||
cfg.BaseURL, url.QueryEscape(recipient))
|
||||
|
||||
// Build HTML body with unsubscribe link
|
||||
htmlBody := buildHTMLBody(body, unsubLink)
|
||||
m.SetBodyString(mail.TypeTextHTML, htmlBody)
|
||||
message := buildMessage(cfg.SenderEmail, recipient, subject, htmlBody)
|
||||
|
||||
if err := client.Send(m); err != nil {
|
||||
log.Printf("Failed to send email to %s: %v", recipient, err)
|
||||
// Create TLS connection
|
||||
tlsconfig := &tls.Config{
|
||||
ServerName: cfg.SMTPServer,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
|
||||
conn, err := tls.Dial("tcp", addr, tlsconfig)
|
||||
if err != nil {
|
||||
log.Printf("Failed to connect to SMTP %s: %v", addr, err)
|
||||
return false
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Create SMTP client
|
||||
client, err := smtp.NewClient(conn, cfg.SMTPServer)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create SMTP client: %v", err)
|
||||
return false
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// Authenticate
|
||||
auth := smtp.PlainAuth("", cfg.SMTPUser, cfg.SMTPPassword, cfg.SMTPServer)
|
||||
if err := client.Auth(auth); err != nil {
|
||||
log.Printf("SMTP auth failed for %s: %v", cfg.SMTPUser, err)
|
||||
return false
|
||||
}
|
||||
|
||||
// Send the email
|
||||
if err := client.Mail(cfg.SenderEmail); err != nil {
|
||||
log.Printf("MAIL command failed: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if err := client.Rcpt(recipient); err != nil {
|
||||
log.Printf("RCPT command failed for %s: %v", recipient, err)
|
||||
return false
|
||||
}
|
||||
|
||||
w, err := client.Data()
|
||||
if err != nil {
|
||||
log.Printf("DATA command failed: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(message))
|
||||
if err != nil {
|
||||
log.Printf("Failed to write message: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
log.Printf("Failed to close DATA: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
client.Quit()
|
||||
|
||||
log.Printf("Update email sent to: %s", recipient)
|
||||
return true
|
||||
}
|
||||
|
||||
func buildMessage(from, to, subject, body string) string {
|
||||
msg := fmt.Sprintf("From: %s\r\n", from)
|
||||
msg += fmt.Sprintf("To: %s\r\n", to)
|
||||
msg += fmt.Sprintf("Subject: %s\r\n", subject)
|
||||
msg += "MIME-Version: 1.0\r\n"
|
||||
msg += "Content-Type: text/html; charset=\"utf-8\"\r\n"
|
||||
msg += "\r\n"
|
||||
msg += body
|
||||
return msg
|
||||
}
|
||||
|
||||
// buildHTMLBody constructs the final HTML email body by appending an unsubscribe footer to the user-provided content.
|
||||
// It handles both complete HTML documents and HTML fragments.
|
||||
func buildHTMLBody(body, unsubLink string) string {
|
||||
footer := fmt.Sprintf(
|
||||
"<br><br><hr><p style='font-size: 12px; color: #666;'>If you ever wish to unsubscribe, "+
|
||||
"please click <a href='%s'>here</a>.</p>",
|
||||
unsubLink)
|
||||
|
||||
// If body contains closing html tag, insert before it
|
||||
if strings.Contains(strings.ToLower(body), "</html>") {
|
||||
return strings.Replace(body, "</html>", footer+"</html>", 1)
|
||||
}
|
||||
|
||||
// If body contains closing body tag, insert before it
|
||||
if strings.Contains(strings.ToLower(body), "</body>") {
|
||||
return strings.Replace(body, "</body>", footer+"</body>", 1)
|
||||
}
|
||||
|
||||
// Otherwise just append
|
||||
return body + footer
|
||||
}
|
||||
174
scripts/build.sh
Executable file
174
scripts/build.sh
Executable file
@@ -0,0 +1,174 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Default values
|
||||
IMAGE_NAME="rideaware-admin"
|
||||
IMAGE_TAG="latest"
|
||||
NO_CACHE=false
|
||||
RUN_CONTAINER=false
|
||||
CONTAINER_NAME="rideaware-admin"
|
||||
|
||||
# Help function
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-t, --tag TAG Image tag (default: latest)
|
||||
-n, --name NAME Image name (default: rideaware)
|
||||
-r, --run Run container after build
|
||||
-c, --container NAME Container name when running (default: rideaware-admin)
|
||||
--no-cache Build without cache
|
||||
-h, --help Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
$0 # Build as rideaware:latest
|
||||
$0 -t v1.0 # Build as rideaware:v1.0
|
||||
$0 -t dev --run # Build and run
|
||||
$0 --no-cache -t prod # Build without cache as rideaware:prod
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-t|--tag)
|
||||
IMAGE_TAG="$2"
|
||||
shift 2
|
||||
;;
|
||||
-n|--name)
|
||||
IMAGE_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
-r|--run)
|
||||
RUN_CONTAINER=true
|
||||
shift
|
||||
;;
|
||||
-c|--container)
|
||||
CONTAINER_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--no-cache)
|
||||
NO_CACHE=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unknown option: $1${NC}"
|
||||
show_help
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
FULL_IMAGE="$IMAGE_NAME:$IMAGE_TAG"
|
||||
BUILD_ARGS=""
|
||||
|
||||
if [ "$NO_CACHE" = true ]; then
|
||||
BUILD_ARGS="--no-cache"
|
||||
fi
|
||||
|
||||
# Function to stop and remove container
|
||||
cleanup_container() {
|
||||
local name=$1
|
||||
|
||||
if podman ps -a --format "{{.Names}}" | grep -q "^${name}\$"; then
|
||||
echo -e "${YELLOW}Removing existing container: $name${NC}"
|
||||
|
||||
# Stop if running
|
||||
if podman ps --format "{{.Names}}" | grep -q "^${name}\$"; then
|
||||
echo " Stopping container..."
|
||||
podman kill "$name" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Remove
|
||||
echo " Removing container..."
|
||||
if podman rm "$name" 2>/dev/null; then
|
||||
echo -e "${GREEN} ✓ Container removed${NC}"
|
||||
else
|
||||
echo -e "${RED} ✗ Failed to remove container${NC}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Building Podman Image ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
echo -e "${YELLOW}Image: $FULL_IMAGE${NC}"
|
||||
echo ""
|
||||
|
||||
if ! podman build $BUILD_ARGS -f Dockerfile -t "$FULL_IMAGE" .; then
|
||||
echo -e "${RED}✗ Build failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Image built successfully${NC}"
|
||||
echo ""
|
||||
|
||||
# Show image info
|
||||
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Image Details ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
podman images "$IMAGE_NAME:$IMAGE_TAG" \
|
||||
--format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.Created}}"
|
||||
echo ""
|
||||
|
||||
if [ "$RUN_CONTAINER" = true ]; then
|
||||
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Starting Container ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
|
||||
# Cleanup existing container
|
||||
if ! cleanup_container "$CONTAINER_NAME"; then
|
||||
echo -e "${RED}✗ Failed to clean up existing container${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Starting new container: $CONTAINER_NAME"
|
||||
|
||||
if podman run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-p 5001:5001 \
|
||||
--env-file .env \
|
||||
"$FULL_IMAGE"; then
|
||||
echo -e "${GREEN}✓ Container running: $CONTAINER_NAME${NC}"
|
||||
echo ""
|
||||
|
||||
# Wait for startup
|
||||
sleep 2
|
||||
|
||||
echo -e "${YELLOW}Container logs:${NC}"
|
||||
podman logs "$CONTAINER_NAME"
|
||||
echo ""
|
||||
|
||||
echo -e "${GREEN}API available at: http://localhost:5001${NC}"
|
||||
echo -e "${YELLOW}To view logs: podman logs -f $CONTAINER_NAME${NC}"
|
||||
echo -e "${YELLOW}To stop: podman kill $CONTAINER_NAME${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Failed to start container${NC}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}To run the container:${NC}"
|
||||
echo " podman run -d --name $CONTAINER_NAME -p 5001:5001 --env-file .env $FULL_IMAGE"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Or use this script with --run:${NC}"
|
||||
echo " $0 -t $IMAGE_TAG --run"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Done!${NC}"
|
||||
Reference in New Issue
Block a user