mirror of https://gitlab.com/ngerakines/tavern.git
158 lines
3.8 KiB
Go
158 lines
3.8 KiB
Go
package start
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/getsentry/sentry-go"
|
|
"github.com/urfave/cli/v2"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/ngerakines/tavern/config"
|
|
"github.com/ngerakines/tavern/errors"
|
|
"github.com/ngerakines/tavern/g"
|
|
"github.com/ngerakines/tavern/storage"
|
|
)
|
|
|
|
var InitServiceCommand = cli.Command{
|
|
Name: "init-service",
|
|
Usage: "Initialize the service",
|
|
Flags: []cli.Flag{
|
|
&config.DomainFlag,
|
|
&config.DatabaseFlag,
|
|
&cli.StringFlag{
|
|
Name: "pem",
|
|
Usage: "The path to the private key PEM file.",
|
|
Required: true,
|
|
},
|
|
},
|
|
Action: initServiceCommandAction,
|
|
}
|
|
|
|
func initServiceCommandAction(cliCtx *cli.Context) error {
|
|
logger, err := config.Logger(cliCtx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
domain := cliCtx.String("domain")
|
|
siteBase := fmt.Sprintf("https://%s", domain)
|
|
|
|
logger.Info("Starting",
|
|
zap.String("command", cliCtx.Command.Name),
|
|
zap.String("GOOS", runtime.GOOS),
|
|
zap.String("site", siteBase),
|
|
zap.String("env", cliCtx.String("environment")))
|
|
|
|
sentryConfig, err := config.NewSentryConfig(cliCtx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if sentryConfig.Enabled {
|
|
err = sentry.Init(sentry.ClientOptions{
|
|
Dsn: sentryConfig.Key,
|
|
Environment: cliCtx.String("environment"),
|
|
Release: fmt.Sprintf("%s-%s", g.Release, g.GitCommit),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
|
scope.SetTags(map[string]string{"container": "server"})
|
|
})
|
|
defer sentry.Recover()
|
|
}
|
|
|
|
db, dbClose, err := config.DB(cliCtx, logger)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer dbClose()
|
|
|
|
actorID := fmt.Sprintf("https://%s/server", domain)
|
|
actor := storage.EmptyPayload()
|
|
actor["@context"] = "https://www.w3.org/ns/activitystreams"
|
|
actor["id"] = actorID
|
|
actor["type"] = "Service"
|
|
actor["inbox"] = fmt.Sprintf("%s/inbox", actorID)
|
|
actor["outbox"] = fmt.Sprintf("%s/outbox", actorID)
|
|
actor["name"] = domain
|
|
actor["summary"] = domain
|
|
actor["preferredUsername"] = domain
|
|
actor["url"] = fmt.Sprintf("https://%s/", domain)
|
|
|
|
ctx := context.Background()
|
|
|
|
pemBytes, err := ioutil.ReadFile(cliCtx.String("pem"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
block, _ := pem.Decode(pemBytes)
|
|
keyID, ok := block.Headers["id"]
|
|
if !ok {
|
|
return fmt.Errorf("missing header: id")
|
|
}
|
|
if len(keyID) == 0 || !strings.HasPrefix(keyID, fmt.Sprintf("https://%s/", domain)) {
|
|
return fmt.Errorf("invalid key id: %s", keyID)
|
|
}
|
|
|
|
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to parse private key bytes: %w", err)
|
|
}
|
|
|
|
publicKeyBytes, err := x509.MarshalPKIXPublicKey(privateKey.Public())
|
|
if err != nil {
|
|
return fmt.Errorf("unable to marshal public key: %w", err)
|
|
}
|
|
|
|
var publicKeyBuffer bytes.Buffer
|
|
if err = pem.Encode(&publicKeyBuffer, &pem.Block{
|
|
Type: "PUBLIC KEY",
|
|
Bytes: publicKeyBytes,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
publicKey := string(publicKeyBuffer.Bytes())
|
|
|
|
txErr := storage.TransactionalStorage(ctx, storage.DefaultStorage(storage.LoggingSQLDriver{Driver: db, Logger: logger}), func(s storage.Storage) error {
|
|
keyPayload := storage.EmptyPayload()
|
|
keyPayload["id"] = keyID
|
|
keyPayload["owner"] = actorID
|
|
keyPayload["publicKeyPem"] = publicKey
|
|
actor["publicKey"] = keyPayload
|
|
|
|
err = s.CreateActor(ctx, actorID, actor)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
actorRowID, err := s.ActorRowIDForActorID(ctx, actorID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = s.RecordActorKey(ctx, actorRowID, keyID, publicKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = s.RecordActorAlias(ctx, actorRowID, actorID, storage.ActorAliasSelf); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if txErr != nil {
|
|
logger.Error("error creating service", zap.Error(err), zap.Strings("error_chain", errors.ErrorChain(err)))
|
|
}
|
|
return txErr
|
|
}
|