Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
VERSION ?= dev
LDFLAGS := -ldflags "-X github.com/cordon-co/cordon-cli/cli/cmd.Version=$(VERSION)"
BUILD := build
OPENAPI_SPEC ?= ../openapi/cordon-v1.openapi.yaml
OPENAPI_CONFIG := cli/internal/apicontract/oapi-codegen.yaml
OPENAPI_OUTPUT := cli/internal/apicontract/gen_types.go

.PHONY: build build-all clean fmt vet \
build-darwin-arm64 build-darwin-amd64 \
build-linux-amd64 build-linux-arm64
build-linux-amd64 build-linux-arm64 \
openapi-generate openapi-check

## build: compile for the current platform
build:
Expand Down Expand Up @@ -36,3 +40,15 @@ vet:
## clean: remove build artifacts
clean:
rm -rf $(BUILD)

## openapi-generate: generate API contract types from OpenAPI spec
openapi-generate:
@test -f "$(OPENAPI_SPEC)" || (echo "missing OpenAPI spec at $(OPENAPI_SPEC)"; exit 1)
go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.4.1 \
-config $(OPENAPI_CONFIG) \
$(OPENAPI_SPEC)

## openapi-check: verify generated contract types are up to date
openapi-check: openapi-generate
@git diff --exit-code -- $(OPENAPI_OUTPUT) >/dev/null || \
(echo "OpenAPI generated file is out of date: $(OPENAPI_OUTPUT)"; git --no-pager diff -- $(OPENAPI_OUTPUT); exit 1)
35 changes: 11 additions & 24 deletions cli/cmd/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/cordon-co/cordon-cli/cli/internal/api"
"github.com/cordon-co/cordon-cli/cli/internal/apicontract"
"github.com/cordon-co/cordon-cli/cli/internal/flags"
"github.com/spf13/cobra"
)
Expand All @@ -21,27 +22,8 @@ var loginCmd = &cobra.Command{
RunE: RunLogin,
}

// deviceResponse is the response from POST /api/v1/auth/device.
type deviceResponse struct {
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"`
VerificationURI string `json:"verification_uri"`
ExpiresIn int `json:"expires_in"`
Interval int `json:"interval"`
}

// tokenResponse is the success response from POST /api/v1/auth/token.
type tokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
User api.User `json:"user"`
}

// tokenErrorResponse is the error response from POST /api/v1/auth/token.
type tokenErrorResponse struct {
Error string `json:"error"`
}
type deviceResponse = apicontract.DeviceCodeResponse
type tokenResponse = apicontract.TokenResponse

type loginResult struct {
User api.User `json:"user"`
Expand Down Expand Up @@ -78,11 +60,11 @@ func RunLogin(cmd *cobra.Command, args []string) error {

// Step 2: Display code and open browser.
if !flags.JSON {
fmt.Fprintf(cmd.OutOrStdout(), "\nOpen this URL in your browser: %s\n", device.VerificationURI)
fmt.Fprintf(cmd.OutOrStdout(), "\nOpen this URL in your browser: %s\n", device.VerificationUri)
fmt.Fprintf(cmd.OutOrStdout(), "Enter code: %s\n\n", device.UserCode)
fmt.Fprintln(cmd.OutOrStdout(), "Waiting for authorization...")
}
openBrowser(device.VerificationURI)
openBrowser(device.VerificationUri)

// Step 3: Poll for token.
interval := time.Duration(device.Interval) * time.Second
Expand Down Expand Up @@ -119,9 +101,14 @@ func RunLogin(cmd *cobra.Command, args []string) error {

// Success — save credentials.
now := time.Now().UTC()
user := api.User{
ID: token.User.Id,
Username: token.User.Username,
DisplayName: token.User.DisplayName,
}
creds := &api.Credentials{
AccessToken: token.AccessToken,
User: token.User,
User: user,
IssuedAt: now,
ExpiresAt: now.Add(time.Duration(token.ExpiresIn) * time.Second),
}
Expand Down
37 changes: 23 additions & 14 deletions cli/cmd/auth/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/cordon-co/cordon-cli/cli/internal/api"
"github.com/cordon-co/cordon-cli/cli/internal/apicontract"
"github.com/cordon-co/cordon-cli/cli/internal/flags"
"github.com/spf13/cobra"
)
Expand All @@ -19,12 +20,6 @@ var statusCmd = &cobra.Command{
RunE: runStatus,
}

// meResponse is the response from GET /api/v1/auth/me.
type meResponse struct {
User api.User `json:"user"`
Perimeters []perimeter `json:"perimeters"`
}

type perimeter struct {
ID string `json:"id"`
Name string `json:"name"`
Expand Down Expand Up @@ -55,7 +50,7 @@ func runStatus(cmd *cobra.Command, args []string) error {

// Verify token with server.
client := api.NewClientWithToken(creds.AccessToken)
var me meResponse
var me apicontract.MeResponse
_, err = client.GetJSON("/api/v1/auth/me", &me)
if err != nil {
if errors.Is(err, api.ErrUnauthorized) {
Expand All @@ -72,26 +67,40 @@ func runStatus(cmd *cobra.Command, args []string) error {
return fmt.Errorf("auth status: verify token: %w", err)
}

user := api.User{
ID: me.User.Id,
Username: me.User.Username,
DisplayName: me.User.DisplayName,
}
perimeters := make([]perimeter, 0, len(me.Perimeters))
for _, p := range me.Perimeters {
perimeters = append(perimeters, perimeter{
ID: p.Id,
Name: p.Name,
Role: string(p.Role),
})
}

if flags.JSON {
out, _ := json.MarshalIndent(statusResult{
Authenticated: true,
User: &me.User,
Perimeters: me.Perimeters,
User: &user,
Perimeters: perimeters,
ExpiresAt: &creds.ExpiresAt,
}, "", " ")
fmt.Println(string(out))
return nil
}

fmt.Fprintf(cmd.OutOrStdout(), "Logged in as %s", me.User.Username)
if me.User.DisplayName != "" && me.User.DisplayName != me.User.Username {
fmt.Fprintf(cmd.OutOrStdout(), " (%s)", me.User.DisplayName)
fmt.Fprintf(cmd.OutOrStdout(), "Logged in as %s", user.Username)
if user.DisplayName != "" && user.DisplayName != user.Username {
fmt.Fprintf(cmd.OutOrStdout(), " (%s)", user.DisplayName)
}
fmt.Fprintln(cmd.OutOrStdout())

if len(me.Perimeters) > 0 {
if len(perimeters) > 0 {
fmt.Fprintln(cmd.OutOrStdout(), "\nPerimeters:")
for _, p := range me.Perimeters {
for _, p := range perimeters {
fmt.Fprintf(cmd.OutOrStdout(), " %s (%s)\n", p.Name, p.Role)
}
}
Expand Down
Loading
Loading