-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update all non-major dependencies (#23)
* Create simple CLI * Create goreleaser config * Set max editor width * Update all non-major dependencies --------- Co-authored-by: Aaron Kirkbride <aaron@aaronkirkbride.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
- Loading branch information
1 parent
2af430d
commit eef3dc5
Showing
14 changed files
with
590 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,4 @@ tsconfig.tsbuildinfo | |
.DS_Store | ||
.env | ||
generated/ | ||
*.gen.go |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json | ||
# vim: set ts=2 sw=2 tw=0 fo=cnqoj | ||
|
||
version: 2 | ||
|
||
before: | ||
hooks: | ||
- go mod tidy | ||
- go generate ./... | ||
|
||
builds: | ||
- env: | ||
- CGO_ENABLED=0 | ||
goos: | ||
- linux | ||
- windows | ||
- darwin | ||
|
||
archives: | ||
- format: tar.gz | ||
# this name template makes the OS and Arch compatible with the results of `uname`. | ||
name_template: >- | ||
{{ .ProjectName }}_ | ||
{{- title .Os }}_ | ||
{{- if eq .Arch "amd64" }}x86_64 | ||
{{- else if eq .Arch "386" }}i386 | ||
{{- else }}{{ .Arch }}{{ end }} | ||
{{- if .Arm }}v{{ .Arm }}{{ end }} | ||
# use zip for windows archives | ||
format_overrides: | ||
- goos: windows | ||
format: zip | ||
|
||
changelog: | ||
sort: asc | ||
filters: | ||
exclude: | ||
- '^docs:' | ||
- '^test:' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Collabify CLI | ||
|
||
## Usage | ||
|
||
`collabify <filepath>` | ||
|
||
# Future ideas | ||
|
||
- Create from a template: e.g. `collabify -t templates/kickoff.md 2024-01-01-project-kickoff.md` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package: api | ||
output: collabify.gen.go | ||
generate: | ||
models: true | ||
chi-server: true | ||
embedded-spec: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package api | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"log" | ||
"net/http" | ||
"os" | ||
) | ||
|
||
type Collabify struct { | ||
fileId string | ||
filepath string | ||
} | ||
|
||
// Ensure that we've conformed to the `ServerInterface` with a compile-time check | ||
var _ ServerInterface = (*Collabify)(nil) | ||
|
||
func NewCollabify(fileId string, filepath string) *Collabify { | ||
return &Collabify{ | ||
fileId: fileId, | ||
filepath: filepath, | ||
} | ||
} | ||
|
||
func (c Collabify) GetFile(w http.ResponseWriter, r *http.Request, fileId string) { | ||
if fileId != c.fileId { | ||
http.Error(w, "File ID not found", http.StatusNotFound) | ||
return | ||
} | ||
|
||
content, err := os.ReadFile(c.filepath) | ||
if err != nil { | ||
http.Error(w, "File not found", http.StatusNotFound) | ||
return | ||
} | ||
|
||
w.WriteHeader(http.StatusOK) | ||
w.Header().Set("Content-Type", "text/markdown") | ||
w.Write(content) | ||
} | ||
|
||
func (c Collabify) UpdateFile(w http.ResponseWriter, r *http.Request, fileId string) { | ||
if fileId != c.fileId { | ||
http.Error(w, "Route not found", http.StatusNotFound) | ||
return | ||
} | ||
|
||
content, err := io.ReadAll(r.Body) | ||
if err != nil { | ||
http.Error(w, "Error reading request", http.StatusBadRequest) | ||
return | ||
} | ||
defer r.Body.Close() | ||
|
||
err = os.WriteFile(c.filepath, content, 0644) | ||
if err != nil { | ||
http.Error(w, "Failed to write to file", http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
log.Println("File updated") | ||
|
||
w.WriteHeader(http.StatusOK) | ||
} | ||
|
||
type SessionData struct { | ||
URL string `json:"url"` | ||
JoinURL string `json:"joinUrl"` | ||
} | ||
|
||
func (c Collabify) PostSession(w http.ResponseWriter, r *http.Request) { | ||
var session SessionData | ||
|
||
if err := json.NewDecoder(r.Body).Decode(&session); err != nil { | ||
http.Error(w, "Invalid input", http.StatusBadRequest) | ||
return | ||
} | ||
|
||
fmt.Printf("\n------------------------\n") | ||
fmt.Printf("Your session URL: %s\n", session.URL) | ||
fmt.Printf("Join URL to share: %s\n", session.JoinURL) | ||
fmt.Printf("------------------------\n\n") | ||
|
||
w.WriteHeader(http.StatusOK) | ||
} | ||
|
||
func (c Collabify) Stop(w http.ResponseWriter, r *http.Request) { | ||
log.Println("Session ended via web") | ||
|
||
w.WriteHeader(http.StatusAccepted) | ||
|
||
go func() { | ||
os.Exit(0) | ||
}() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package api | ||
|
||
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml ../../schema/api.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
"crypto/sha256" | ||
"crypto/subtle" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/getkin/kin-openapi/openapi3" | ||
"github.com/getkin/kin-openapi/openapi3filter" | ||
middleware "github.com/oapi-codegen/nethttp-middleware" | ||
"github.com/rs/cors" | ||
) | ||
|
||
func CorsMiddleware(allowedOrigins []string) func(next http.Handler) http.Handler { | ||
return cors.New(cors.Options{ | ||
AllowedOrigins: allowedOrigins, | ||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, | ||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"}, | ||
AllowCredentials: true, | ||
}).Handler | ||
} | ||
|
||
func OpenApiRequestValidatorMiddleware(swagger *openapi3.T, authToken string) func(next http.Handler) http.Handler { | ||
return middleware.OapiRequestValidatorWithOptions(swagger, &middleware.Options{ | ||
Options: openapi3filter.Options{ | ||
AuthenticationFunc: authenticate(authToken), | ||
}, | ||
SilenceServersWarning: true, | ||
}) | ||
} | ||
|
||
func authenticate(authToken string) openapi3filter.AuthenticationFunc { | ||
expectedAuthHeader := "Bearer " + authToken | ||
expectedHash := sha256.Sum256([]byte(expectedAuthHeader)) | ||
|
||
return func(ctx context.Context, input *openapi3filter.AuthenticationInput) error { | ||
if input.SecuritySchemeName != "bearerAuth" { | ||
return fmt.Errorf("security scheme %s != 'bearerAuth'", input.SecuritySchemeName) | ||
} | ||
|
||
providedAuthHeader := input.RequestValidationInput.Request.Header.Get("Authorization") | ||
providedHash := sha256.Sum256([]byte(providedAuthHeader)) | ||
|
||
if subtle.ConstantTimeCompare(expectedHash[:], providedHash[:]) != 1 { | ||
return errors.New("invalid token") | ||
} | ||
|
||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package api | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/go-chi/chi/v5" | ||
) | ||
|
||
func CreateRouter(filename string, fileId string, authToken string, allowedOrigins []string) *chi.Mux { | ||
swagger, err := GetSwagger() | ||
if err != nil { | ||
fmt.Println("Error loading swagger spec:", err) | ||
os.Exit(1) | ||
} | ||
|
||
r := chi.NewRouter() | ||
|
||
r.Use(CorsMiddleware(allowedOrigins)) | ||
r.Use(OpenApiRequestValidatorMiddleware(swagger, authToken)) | ||
|
||
collabify := NewCollabify(fileId, filename) | ||
r.Route("/v1", func(r chi.Router) { | ||
r.Mount("/", HandlerFromMux(collabify, chi.NewMux())) | ||
}) | ||
|
||
return r | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net" | ||
"net/http" | ||
"os" | ||
"time" | ||
|
||
"collabify/api" | ||
|
||
"github.com/getkin/kin-openapi/openapi3filter" | ||
"github.com/pkg/browser" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
const defaultAppUrl = "https://collabify.it" // Base URL for the app | ||
const defaultListenHost = "localhost" // `localhost` is recommended for most use cases | ||
const defaultListenPort = "0" // 0 uses the next available port | ||
|
||
func init() { | ||
openapi3filter.RegisterBodyDecoder("text/markdown", openapi3filter.FileBodyDecoder) | ||
} | ||
|
||
func main() { | ||
appUrl := getEnv("COLLABIFY_APP_URL", defaultAppUrl) | ||
listenHost := getEnv("COLLABIFY_LISTEN_HOST", defaultListenHost) | ||
listenPort := getEnv("COLLABIFY_LISTEN_PORT", defaultListenPort) | ||
allowedOrigins := []string{appUrl} | ||
|
||
app := &cli.App{ | ||
Name: "collabify", | ||
Usage: "serve a markdown file over HTTP", | ||
Action: func(c *cli.Context) error { | ||
filename := c.Args().Get(0) | ||
if filename == "" { | ||
return cli.Exit("You must provide a filename", 1) | ||
} | ||
if _, err := os.Stat(filename); err != nil { | ||
return err | ||
} | ||
|
||
// Generate a random fileId and authToken | ||
fileId, err := generateRandomUrlSafeString(8) | ||
if err != nil { | ||
return err | ||
} | ||
authToken, err := generateRandomUrlSafeString(16) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
listener, err := net.Listen("tcp", ":"+listenPort) | ||
if err != nil { | ||
fmt.Println("Failed to listen:", err) | ||
} | ||
defer listener.Close() | ||
|
||
// This may be different from listenPort if listenPort | ||
// was set to 0 (i.e. use the next available port) | ||
port := listener.Addr().(*net.TCPAddr).Port | ||
|
||
r := api.CreateRouter(filename, fileId, authToken, allowedOrigins) | ||
|
||
// Start the HTTP server | ||
serverStarted := make(chan error) | ||
go func() { | ||
err := http.Serve(listener, r) | ||
if err != nil { | ||
serverStarted <- err | ||
return | ||
} | ||
}() | ||
|
||
select { | ||
case err := <-serverStarted: | ||
fmt.Println("Server failed to start:", err) | ||
|
||
case <-time.After(100 * time.Millisecond): | ||
localUrl := fmt.Sprintf("http://%s:%d", listenHost, port) | ||
newSessionUrl, err := buildNewSessionUrl(appUrl, localUrl, fileId, authToken) | ||
if err != nil { | ||
log.Fatal("Failed to build new URL:", err) | ||
} | ||
|
||
fmt.Println("Opening", newSessionUrl) | ||
browser.OpenURL(newSessionUrl) | ||
} | ||
|
||
log.Fatal(http.Serve(listener, r)) | ||
return nil | ||
}, | ||
} | ||
|
||
err := app.Run(os.Args) | ||
if err != nil { | ||
fmt.Println(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
module collabify | ||
|
||
go 1.22.4 | ||
|
||
require ( | ||
github.com/getkin/kin-openapi v0.124.0 | ||
github.com/go-chi/chi/v5 v5.0.12 | ||
github.com/oapi-codegen/nethttp-middleware v1.0.2 | ||
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0 | ||
github.com/oapi-codegen/runtime v1.1.1 | ||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c | ||
github.com/rs/cors v1.11.0 | ||
github.com/urfave/cli/v2 v2.27.2 | ||
) | ||
|
||
require ( | ||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect | ||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect | ||
github.com/go-openapi/jsonpointer v0.20.2 // indirect | ||
github.com/go-openapi/swag v0.22.8 // indirect | ||
github.com/google/uuid v1.5.0 // indirect | ||
github.com/gorilla/mux v1.8.1 // indirect | ||
github.com/invopop/yaml v0.2.0 // indirect | ||
github.com/josharian/intern v1.0.0 // indirect | ||
github.com/mailru/easyjson v0.7.7 // indirect | ||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect | ||
github.com/perimeterx/marshmallow v1.1.5 // indirect | ||
github.com/russross/blackfriday/v2 v2.1.0 // indirect | ||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect | ||
golang.org/x/mod v0.17.0 // indirect | ||
golang.org/x/sys v0.20.0 // indirect | ||
golang.org/x/text v0.15.0 // indirect | ||
golang.org/x/tools v0.21.0 // indirect | ||
gopkg.in/yaml.v2 v2.4.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
Oops, something went wrong.