2025-03-07 20:34:31 -03:00
|
|
|
package main
|
2025-03-05 10:05:21 -03:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2025-03-11 09:45:41 -03:00
|
|
|
"database/sql"
|
2025-03-05 10:05:21 -03:00
|
|
|
"errors"
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
2025-03-11 09:45:41 -03:00
|
|
|
"log"
|
2025-03-05 10:05:21 -03:00
|
|
|
"log/slog"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"os/signal"
|
|
|
|
|
"syscall"
|
|
|
|
|
|
2025-03-07 20:40:31 -03:00
|
|
|
comicverse "forge.capytal.company/capytalcode/project-comicverse"
|
2025-03-05 10:05:21 -03:00
|
|
|
"forge.capytal.company/loreddev/x/tinyssert"
|
2025-03-11 09:46:03 -03:00
|
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
2025-03-11 09:45:41 -03:00
|
|
|
_ "github.com/tursodatabase/go-libsql"
|
2025-03-05 10:05:21 -03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
2025-03-07 20:33:39 -03:00
|
|
|
hostname = flag.String("hostname", "localhost", "Host to listen to")
|
2025-03-05 10:05:21 -03:00
|
|
|
port = flag.Uint("port", 8080, "Port to be used for the server.")
|
|
|
|
|
templatesDir = flag.String("templates", "", "Templates directory to be used instead of built-in ones.")
|
|
|
|
|
verbose = flag.Bool("verbose", false, "Print debug information on logs")
|
|
|
|
|
dev = flag.Bool("dev", false, "Run the server in debug mode.")
|
|
|
|
|
)
|
|
|
|
|
|
2025-03-11 09:45:41 -03:00
|
|
|
var (
|
|
|
|
|
databaseURL = getEnv("DATABASE_URL", "file://./libsql.db")
|
2025-03-11 09:46:03 -03:00
|
|
|
|
|
|
|
|
awsAccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID")
|
|
|
|
|
awsSecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
|
|
|
|
|
awsDefaultRegion = os.Getenv("AWS_DEFAULT_REGION")
|
|
|
|
|
awsEndpointURL = os.Getenv("AWS_ENDPOINT_URL")
|
2025-03-11 09:45:41 -03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func getEnv(key string, d string) string {
|
|
|
|
|
v := os.Getenv(key)
|
|
|
|
|
if v == "" {
|
|
|
|
|
return d
|
|
|
|
|
}
|
|
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-05 10:05:21 -03:00
|
|
|
func init() {
|
|
|
|
|
flag.Parse()
|
2025-03-11 09:45:41 -03:00
|
|
|
|
|
|
|
|
switch {
|
|
|
|
|
case databaseURL == "":
|
|
|
|
|
log.Fatal("DATABASE_URL should not be a empty value")
|
2025-03-11 09:46:03 -03:00
|
|
|
case awsAccessKeyID == "":
|
|
|
|
|
log.Fatal("AWS_ACCESS_KEY_ID should not be a empty value")
|
|
|
|
|
case awsDefaultRegion == "":
|
|
|
|
|
log.Fatal("AWS_DEFAULT_REGION should not be a empty value")
|
|
|
|
|
case awsEndpointURL == "":
|
|
|
|
|
log.Fatal("AWS_ENDPOINT_URL should not be a empty value")
|
2025-03-11 09:45:41 -03:00
|
|
|
}
|
2025-03-05 10:05:21 -03:00
|
|
|
}
|
|
|
|
|
|
2025-03-07 20:35:28 -03:00
|
|
|
func main() {
|
2025-03-05 10:05:21 -03:00
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
assertions := tinyssert.NewDisabledAssertions()
|
|
|
|
|
if *dev {
|
|
|
|
|
assertions = tinyssert.NewAssertions()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
level := slog.LevelError
|
|
|
|
|
if *dev {
|
|
|
|
|
level = slog.LevelDebug
|
|
|
|
|
} else if *verbose {
|
|
|
|
|
level = slog.LevelInfo
|
|
|
|
|
}
|
|
|
|
|
log := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level}))
|
|
|
|
|
|
2025-03-11 09:45:41 -03:00
|
|
|
db, err := sql.Open("libsql", databaseURL)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("Failed open connection to database", slog.String("error", err.Error()))
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-11 09:46:03 -03:00
|
|
|
credentials := aws.CredentialsProviderFunc(func(ctx context.Context) (aws.Credentials, error) {
|
|
|
|
|
return aws.Credentials{
|
|
|
|
|
AccessKeyID: awsAccessKeyID,
|
|
|
|
|
SecretAccessKey: awsSecretAccessKey,
|
|
|
|
|
CanExpire: false,
|
|
|
|
|
}, nil
|
|
|
|
|
})
|
|
|
|
|
storage := s3.New(s3.Options{
|
|
|
|
|
AppID: "comicverse-pre-alpha",
|
|
|
|
|
BaseEndpoint: &awsEndpointURL,
|
|
|
|
|
Region: awsDefaultRegion,
|
|
|
|
|
Credentials: &credentials,
|
|
|
|
|
})
|
|
|
|
|
|
2025-03-07 20:40:31 -03:00
|
|
|
opts := []comicverse.Option{
|
|
|
|
|
comicverse.WithContext(ctx),
|
|
|
|
|
comicverse.WithAssertions(assertions),
|
|
|
|
|
comicverse.WithLogger(log),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if *dev {
|
2025-03-07 20:48:11 -03:00
|
|
|
d := os.DirFS("./static")
|
|
|
|
|
opts = append(opts, comicverse.WithStaticFiles(d))
|
|
|
|
|
|
2025-03-07 20:40:31 -03:00
|
|
|
opts = append(opts, comicverse.WithDevelopmentMode())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app, err := comicverse.New(comicverse.Config{
|
2025-03-11 09:45:41 -03:00
|
|
|
DB: db,
|
|
|
|
|
S3: storage,
|
2025-03-07 20:40:31 -03:00
|
|
|
}, opts...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("Failed to initiate comicverse app", slog.String("error", err.Error()))
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
2025-03-05 10:05:21 -03:00
|
|
|
|
|
|
|
|
srv := &http.Server{
|
2025-03-07 20:33:39 -03:00
|
|
|
Addr: fmt.Sprintf("%s:%d", *hostname, *port),
|
2025-03-05 10:05:21 -03:00
|
|
|
Handler: app,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
|
defer stop()
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
log.Info("Starting application",
|
2025-03-07 20:33:39 -03:00
|
|
|
slog.String("host", *hostname),
|
2025-03-05 10:05:21 -03:00
|
|
|
slog.Uint64("port", uint64(*port)),
|
|
|
|
|
slog.Bool("verbose", *verbose),
|
|
|
|
|
slog.Bool("development", *dev))
|
|
|
|
|
|
|
|
|
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
2025-03-07 20:34:08 -03:00
|
|
|
log.Error("Failed to start application server", slog.String("error", err.Error()))
|
|
|
|
|
os.Exit(1)
|
2025-03-05 10:05:21 -03:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
<-c.Done()
|
|
|
|
|
|
|
|
|
|
log.Info("Stopping application gracefully")
|
|
|
|
|
if err := srv.Shutdown(ctx); err != nil {
|
2025-03-07 20:34:08 -03:00
|
|
|
log.Error("Failed to stop application server gracefully", slog.String("error", err.Error()))
|
|
|
|
|
os.Exit(1)
|
2025-03-05 10:05:21 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Info("FINAL")
|
|
|
|
|
os.Exit(0)
|
|
|
|
|
}
|