feat: initial commit

This commit is contained in:
2023-02-09 12:16:36 +01:00
commit 81dc1adfef
126 changed files with 11551 additions and 0 deletions

View File

@ -0,0 +1,149 @@
package app
import (
"archive/zip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"forge.cadoles.com/arcad/edge/pkg/app"
"forge.cadoles.com/arcad/edge/pkg/bundle"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
)
func PackageCommand() *cli.Command {
return &cli.Command{
Name: "package",
Usage: "Generate a new app package from given directory",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "directory",
Usage: "use source directory `DIR`",
Aliases: []string{"d"},
Required: true,
},
&cli.StringFlag{
Name: "output-dir",
Aliases: []string{"o"},
Usage: "use `DIR` as generated package destination",
},
},
Action: func(ctx *cli.Context) error {
appDir := ctx.String("directory")
outputDir := ctx.String("output-dir")
if outputDir == "" {
workdir, err := os.Getwd()
if err != nil {
return errors.Wrap(err, "could not retrieve current working directory")
}
outputDir = workdir
}
bundle := bundle.NewDirectoryBundle(appDir)
manifest, err := app.LoadAppManifest(bundle)
if err != nil {
return errors.Wrap(err, "could not load app manifest")
}
if err := os.MkdirAll(outputDir, 0o755); err != nil {
return errors.Wrapf(err, "could not create directory ''%s'", outputDir)
}
archiveName := fmt.Sprintf(
"%s_%s%s",
strings.ToLower(string(manifest.ID)),
string(manifest.Version),
".zip",
)
packagePath := filepath.Join(outputDir, archiveName)
if err := zipDirectory(appDir, packagePath); err != nil {
return errors.Wrapf(err, "could not zip directory ''%s'", appDir)
}
return nil
},
}
}
func zipDirectory(baseDir string, outputFile string) error {
outFile, err := os.Create(outputFile)
if err != nil {
return errors.WithStack(err)
}
defer func() {
if err := outFile.Close(); err != nil {
panic(errors.WithStack(err))
}
}()
w := zip.NewWriter(outFile)
if err := copyDir(w, baseDir+"/", ""); err != nil {
return errors.WithStack(err)
}
if err := w.Close(); err != nil {
return errors.WithStack(err)
}
return nil
}
func copyDir(writer *zip.Writer, baseDir string, zipBasePath string) error {
files, err := ioutil.ReadDir(baseDir)
if err != nil {
return errors.WithStack(err)
}
for _, file := range files {
if !file.IsDir() {
srcPath := baseDir + file.Name()
zipPath := zipBasePath + file.Name()
if err := copyFile(writer, srcPath, zipPath); err != nil {
return errors.WithStack(err)
}
} else if file.IsDir() {
newBase := baseDir + file.Name() + "/"
if err := copyDir(writer, newBase, zipBasePath+file.Name()+"/"); err != nil {
return errors.WithStack(err)
}
}
}
return nil
}
func copyFile(writer *zip.Writer, srcPath string, zipPath string) error {
r, err := os.Open(srcPath)
if err != nil {
return errors.WithStack(err)
}
defer func() {
if err := r.Close(); err != nil {
panic(errors.WithStack(err))
}
}()
f, err := writer.Create(zipPath)
if err != nil {
return errors.WithStack(err)
}
if _, err = io.Copy(f, r); err != nil {
return errors.WithStack(err)
}
return nil
}

View File

@ -0,0 +1,16 @@
package app
import (
"github.com/urfave/cli/v2"
)
func Root() *cli.Command {
return &cli.Command{
Name: "app",
Usage: "App related commands",
Subcommands: []*cli.Command{
RunCommand(),
PackageCommand(),
},
}
}

121
cmd/cli/command/app/run.go Normal file
View File

@ -0,0 +1,121 @@
package app
import (
"database/sql"
"net/http"
"path/filepath"
"forge.cadoles.com/arcad/edge/pkg/bus/memory"
appHTTP "forge.cadoles.com/arcad/edge/pkg/http"
"forge.cadoles.com/arcad/edge/pkg/module"
"forge.cadoles.com/arcad/edge/pkg/storage/sqlite"
"gitlab.com/wpetit/goweb/logger"
"forge.cadoles.com/arcad/edge/pkg/bundle"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
_ "modernc.org/sqlite"
)
func RunCommand() *cli.Command {
return &cli.Command{
Name: "run",
Usage: "Run the specified app bundle",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "path",
Usage: "use `PATH` as app bundle (zipped bundle or directory)",
Aliases: []string{"p"},
Value: ".",
},
&cli.StringFlag{
Name: "address",
Usage: "use `ADDRESS` as http server listening address",
Aliases: []string{"a"},
Value: ":8080",
},
&cli.StringFlag{
Name: "log-format",
Usage: "use `LOG-FORMAT` ('json' or 'human')",
Value: "human",
},
&cli.IntFlag{
Name: "log-level",
Usage: "use `LOG-LEVEL` (0: debug -> 5: fatal)",
Value: 0,
},
&cli.StringFlag{
Name: "storage-file",
Usage: "use `FILE` for SQLite storage database",
Value: "data.sqlite",
},
},
Action: func(ctx *cli.Context) error {
address := ctx.String("address")
path := ctx.String("path")
logFormat := ctx.String("log-format")
logLevel := ctx.Int("log-level")
storageFile := ctx.String("storage-file")
logger.SetFormat(logger.Format(logFormat))
logger.SetLevel(logger.Level(logLevel))
cmdCtx := ctx.Context
absPath, err := filepath.Abs(path)
if err != nil {
return errors.Wrapf(err, "could not resolve path '%s'", path)
}
logger.Info(cmdCtx, "opening app bundle", logger.F("path", absPath))
bundle, err := bundle.FromPath(path)
if err != nil {
return errors.Wrapf(err, "could not open path '%s' as an app bundle", path)
}
mux := chi.NewMux()
mux.Use(middleware.Logger)
bus := memory.NewBus()
db, err := sql.Open("sqlite", storageFile)
if err != nil {
return errors.Wrapf(err, "could not open database with path '%s'", storageFile)
}
documentStore := sqlite.NewDocumentStoreWithDB(db)
blobStore := sqlite.NewBlobStoreWithDB(db)
handler := appHTTP.NewHandler(
appHTTP.WithBus(bus),
appHTTP.WithServerModules(
module.ContextModuleFactory(),
module.ConsoleModuleFactory(),
module.LifecycleModuleFactory(bus),
module.NetModuleFactory(bus),
module.RPCModuleFactory(bus),
module.StoreModuleFactory(documentStore),
module.BlobModuleFactory(bus, blobStore),
),
)
if err := handler.Load(bundle); err != nil {
return errors.Wrap(err, "could not load app bundle")
}
mux.Handle("/*", handler)
logger.Info(cmdCtx, "listening", logger.F("address", address))
if err := http.ListenAndServe(address, mux); err != nil {
return errors.WithStack(err)
}
return nil
},
}
}

48
cmd/cli/command/main.go Normal file
View File

@ -0,0 +1,48 @@
package command
import (
"context"
"fmt"
"os"
"sort"
"github.com/urfave/cli/v2"
)
func Main(commands ...*cli.Command) {
ctx := context.Background()
app := &cli.App{
Name: "edge-cli",
Usage: "Arcad edge cli",
Commands: commands,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "debug",
EnvVars: []string{"DEBUG"},
Value: false,
},
},
}
app.ExitErrHandler = func(ctx *cli.Context, err error) {
if err == nil {
return
}
debug := ctx.Bool("debug")
if !debug {
fmt.Printf("[ERROR] %v\n", err)
} else {
fmt.Printf("%+v", err)
}
}
sort.Sort(cli.FlagsByName(app.Flags))
sort.Sort(cli.CommandsByName(app.Commands))
if err := app.RunContext(ctx, os.Args); err != nil {
os.Exit(1)
}
}

10
cmd/cli/main.go Normal file
View File

@ -0,0 +1,10 @@
package main
import (
"forge.cadoles.com/arcad/edge/cmd/cli/command"
"forge.cadoles.com/arcad/edge/cmd/cli/command/app"
)
func main() {
command.Main(app.Root())
}