Skip to content

Commit

Permalink
feat(server): create a minimal server
Browse files Browse the repository at this point in the history
Signed-off-by: Tristan Partin <tristan@partin.io>
  • Loading branch information
tristan957 committed Aug 30, 2024
1 parent aec52de commit 5440cbe
Show file tree
Hide file tree
Showing 19 changed files with 648 additions and 0 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/build-server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# SPDX-FileCopyrightText: 2024 Tristan Partin <tristan@partin.io>

name: Build Server

on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- "**.go"

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true

jobs:
build-server:
runs-on: ubuntu-latest
container:
image: docker.io/fedora:40

steps:
- name: Checkout splitsies
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
check-latest: true
go-version: "1.22"

- name: Install just
run: |
dnf install --assumeyes --setopt=install_weak_deps=False just
- name: Build the Server
run: |
just debug
32 changes: 32 additions & 0 deletions .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# SPDX-FileCopyrightText: 2024 Tristan Partin <tristan@partin.io>

name: golangci-lint

on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- "**.go"

permissions:
contents: read

jobs:
golangci-lint:
runs-on: ubuntu-latest

steps:
- name: Checkout gosplitsies
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.22"

- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: "v1.60"
79 changes: 79 additions & 0 deletions .github/workflows/vacuum.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# SPDX-FileCopyrightText: 2024 Tristan Partin <tristan@partin.io>

name: vacuum

on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- "openapi.yaml"
- "vacuum.conf.yaml"

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true

jobs:
go-cache:
runs-on: ubuntu-latest
outputs:
GOCACHE: ${{ steps.gocache.outputs.GOCACHE }}
GOMODCACHE: ${{ steps.gomodcache.outputs.GOMODCACHE }}

steps:
- name: Setup Go
uses: actions/setup-go@v5
with:
cache: false
check-latest: true
go-version: "1.22"

- name: Get the GOCACHE Environment Variable
id: gocache
run: |
echo "GOCACHE=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
- name: Get the GOMODCACHE Environment Variable
id: gomodcache
run: |
echo "GOMODCACHE=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"
vacuum:
runs-on: ubuntu-latest
needs:
- go-cache
env:
VACUUM_VERSION: "v0.12.1"

steps:
- name: Checkout splitsies
uses: actions/checkout@v4

- name: Cache Go Modules
uses: actions/cache@v4
with:
path: |
${{ needs.go-cache.outputs.GOCACHE }}
${{ needs.go-cache.outputs.GOMODCACHE }}
key: ${{ env.VACUUM_VERSION }}

- name: Setup Go
uses: actions/setup-go@v5
with:
cache: false
check-latest: true
go-version: "1.22"

- name: Install vacuum
run: |
go install "github.com/daveshanley/vacuum@$VACUUM_VERSION"
- name: Lint the OpenAPI Description
run: |
vacuum lint --no-style openapi.yaml
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ go.work.sum
.env

!.vscode/

gsplit
gsplit.debug
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ SPDX-FileCopyrightText: 2024 Tristan Partin <tristan@partin.io>

# gosplitsies

## server

### Building

```shell
# Release
just build
# Debug
just debug
```

## ui

### Setup
Expand Down
3 changes: 3 additions & 0 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ SPDX-PackageName = "splitsies"
[[annotations]]
path = [
".vscode/copyright.code-snippets",
"go.mod",
"go.sum",
"ui/package.json",
"ui/package-lock.json",
"VERSION",
]
precedence = "override"
SPDX-FileCopyrightText = "2024 Joseph Martinsen <joseph@martinsen.com>"
Expand Down
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.0.0
118 changes: 118 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later
*
* SPDX-FileCopyrightText: 2024 Tristan Partin <tristan@partin.io>
*/

package cmd

import (
"fmt"
"os"
"strings"

"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

var debug string
var configFile string
var zapConfig zap.Config

var rootCmd = &cobra.Command{
Use: "gsplit",
Short: "GoSplitsies is a pay splitting application",
}

func init() {
cobra.OnInitialize(initConfig)

rootCmd.AddCommand(versionCmd)
rootCmd.AddCommand(serverCmd)

rootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "Path to a config file")
rootCmd.PersistentFlags().StringP("log-filepath", "l", "", "Path to log to if log-location is file")
rootCmd.PersistentFlags().String("log-location", "stderr", "Location to send logs to")

serverCmd.Flags().Uint16P("port", "p", 5431, "Port to start the server on")

flags := map[string]*pflag.Flag{
"log.filepath": rootCmd.PersistentFlags().Lookup("log-filepath"),
"log.location": rootCmd.PersistentFlags().Lookup("log-location"),

"server.port": serverCmd.Flags().Lookup("port"),
}
for key, flag := range flags {
if err := viper.BindPFlag(key, flag); err != nil {
fmt.Fprintf(os.Stderr, "failed to bind flags for configuration purposes: %s\n", err)
os.Exit(1)
}
}
}

func initConfig() {
if configFile != "" {
viper.SetConfigFile(configFile)
} else {
viper.SetConfigName("gosplitsies")
viper.SetConfigType("yaml")

// TODO: Windows and macOS?
viper.AddConfigPath("/etc")
}

viper.SetEnvPrefix("gsplit")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
viper.AutomaticEnv()

if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok && configFile != "" {
fmt.Fprintf(os.Stderr, "failed to read in config: %s\n", err)
os.Exit(1)
}
}

if debug == "true" {
zapConfig = zap.NewDevelopmentConfig()
zapConfig.Level.SetLevel(zapcore.DebugLevel)
} else {
zapConfig = zap.NewProductionConfig()
zapConfig.Level.SetLevel(zapcore.InfoLevel)
}

logLocation := viper.GetString("log.location")
if logLocation == "file" {
logFilepath := viper.GetString("log.filepath")
if logFilepath == "" {
fmt.Fprintln(os.Stderr, "no log filepath provided")
os.Exit(1)
}

zapConfig.OutputPaths = []string{logFilepath}
} else if logLocation == "stdout" {
zapConfig.OutputPaths = []string{"stdout"}
} else if logLocation == "stderr" {
// Nothing to do since this is the default
} else if logLocation == "syslog" { //nolint:staticcheck
// TODO: Fail this if macOS or Windows
// TODO: Also, actually implement this
}

logger, err := zapConfig.Build()
if err != nil {
fmt.Fprintln(os.Stderr, "failed to initialize logger")
}
//nolint:errcheck
defer logger.Sync()

zap.ReplaceGlobals(logger)
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
zap.S().Error("%s", err)
os.Exit(1)
}
}
36 changes: 36 additions & 0 deletions cmd/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later
*
* SPDX-FileCopyrightText: 2024 Tristan Partin <tristan@partin.io>
*/

package cmd

import (
"fmt"
"net/http"
"os"

"github.com/Return0Software/gosplitsies/middleware"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.uber.org/zap"
)

var serverPort uint16

var serverCmd = &cobra.Command{
Use: "server",
Short: "Start the server",
Run: func(cmd *cobra.Command, args []string) {
mux := http.NewServeMux()

mux.HandleFunc("/logs/level", zapConfig.Level.ServeHTTP)

port := viper.GetUint16("server.port")
zap.S().Infof("Starting server on port %d", port)
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), middleware.NewLogger(mux)); err != nil {
zap.S().Fatalf("failed to start the server on port %d: %s", serverPort, err)
os.Exit(1)
}
},
}
22 changes: 22 additions & 0 deletions cmd/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later
*
* SPDX-FileCopyrightText: 2024 Tristan Partin <tristan@partin.io>
*/

package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var version string

var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(version)
},
}
12 changes: 12 additions & 0 deletions docs/gosplitsies.example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# SPDX-FileCopyrightText: 2024 Tristan Partin <tristan@partin.io>

log:
# Path to log to if log-location is file
filename: ""
# Location to send logs to (file, stderr, stdout, syslog)
location: "stderr"
server:
# Port to start the server on
port: 5431
Loading

0 comments on commit 5440cbe

Please sign in to comment.