feat: redesign project sharing & initial registration

redesigned the project sharing popup to be a multi select dropdown
that populates the options by using the input as a fuzzy search filter
on the current users & invited users.

users can now also be directly invited by email from the project share
window. if invited this way, then the user will receive an email
that sends them to a registration page, then a confirmation page.

the initial registration was always redone so that it uses a similar
system to the above in that it now will accept the first registered
user if there are no other accounts (besides 'system').
This commit is contained in:
Jordan Knott
2020-10-20 18:52:09 -05:00
parent 6c7203a4aa
commit 7b6624ecc3
75 changed files with 5041 additions and 859 deletions

View File

@ -1,89 +1,21 @@
package logger
import (
"fmt"
"net/http"
"time"
"context"
"github.com/go-chi/chi/middleware"
"github.com/sirupsen/logrus"
"github.com/google/uuid"
"github.com/jordanknott/taskcafe/internal/utils"
log "github.com/sirupsen/logrus"
)
// NewStructuredLogger creates a new logger for chi router
func NewStructuredLogger(logger *logrus.Logger) func(next http.Handler) http.Handler {
return middleware.RequestLogger(&StructuredLogger{logger})
}
// StructuredLogger is a logger for chi router
type StructuredLogger struct {
Logger *logrus.Logger
}
// NewLogEntry creates a new log entry for the given HTTP request
func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
entry := &StructuredLoggerEntry{Logger: logrus.NewEntry(l.Logger)}
logFields := logrus.Fields{}
if reqID := middleware.GetReqID(r.Context()); reqID != "" {
logFields["req_id"] = reqID
// New returns a log entry with the reqID and userID fields populated if they exist
func New(ctx context.Context) *log.Entry {
entry := log.NewEntry(log.StandardLogger())
if reqID, ok := ctx.Value(utils.ReqIDKey).(uuid.UUID); ok {
entry = entry.WithField("reqID", reqID)
}
scheme := "http"
if r.TLS != nil {
scheme = "https"
if userID, ok := ctx.Value(utils.UserIDKey).(uuid.UUID); ok {
entry = entry.WithField("userID", userID)
}
logFields["http_scheme"] = scheme
logFields["http_proto"] = r.Proto
logFields["http_method"] = r.Method
logFields["remote_addr"] = r.RemoteAddr
logFields["user_agent"] = r.UserAgent()
logFields["uri"] = fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI)
entry.Logger = entry.Logger.WithFields(logFields)
return entry
}
// StructuredLoggerEntry is a log entry will all relevant information about a specific http request
type StructuredLoggerEntry struct {
Logger logrus.FieldLogger
}
// Write logs information about http request response body
func (l *StructuredLoggerEntry) Write(status, bytes int, elapsed time.Duration) {
l.Logger = l.Logger.WithFields(logrus.Fields{
"resp_status": status, "resp_bytes_length": bytes,
"resp_elapsed_ms": float64(elapsed.Nanoseconds()) / 1000000.0,
})
l.Logger.Debugln("request complete")
}
// Panic logs if the request panics
func (l *StructuredLoggerEntry) Panic(v interface{}, stack []byte) {
l.Logger = l.Logger.WithFields(logrus.Fields{
"stack": string(stack),
"panic": fmt.Sprintf("%+v", v),
})
}
// GetLogEntry helper function for getting log entry for request
func GetLogEntry(r *http.Request) logrus.FieldLogger {
entry := middleware.GetLogEntry(r).(*StructuredLoggerEntry)
return entry.Logger
}
// LogEntrySetField sets a key's value
func LogEntrySetField(r *http.Request, key string, value interface{}) {
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
entry.Logger = entry.Logger.WithField(key, value)
}
}
// LogEntrySetFields sets the log entry's fields
func LogEntrySetFields(r *http.Request, fields map[string]interface{}) {
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
entry.Logger = entry.Logger.WithFields(fields)
}
}

View File

@ -0,0 +1,89 @@
package logger
import (
"fmt"
"net/http"
"time"
"github.com/go-chi/chi/middleware"
"github.com/sirupsen/logrus"
)
// NewStructuredLogger creates a new logger for chi router
func NewStructuredLogger(logger *logrus.Logger) func(next http.Handler) http.Handler {
return middleware.RequestLogger(&StructuredLogger{logger})
}
// StructuredLogger is a logger for chi router
type StructuredLogger struct {
Logger *logrus.Logger
}
// NewLogEntry creates a new log entry for the given HTTP request
func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
entry := &StructuredLoggerEntry{Logger: logrus.NewEntry(l.Logger)}
logFields := logrus.Fields{}
if reqID := middleware.GetReqID(r.Context()); reqID != "" {
logFields["req_id"] = reqID
}
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
logFields["http_scheme"] = scheme
logFields["http_proto"] = r.Proto
logFields["http_method"] = r.Method
logFields["remote_addr"] = r.RemoteAddr
logFields["user_agent"] = r.UserAgent()
logFields["uri"] = fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI)
entry.Logger = entry.Logger.WithFields(logFields)
return entry
}
// StructuredLoggerEntry is a log entry will all relevant information about a specific http request
type StructuredLoggerEntry struct {
Logger logrus.FieldLogger
}
// Write logs information about http request response body
func (l *StructuredLoggerEntry) Write(status, bytes int, elapsed time.Duration) {
l.Logger = l.Logger.WithFields(logrus.Fields{
"resp_status": status, "resp_bytes_length": bytes,
"resp_elapsed_ms": float64(elapsed.Nanoseconds()) / 1000000.0,
})
l.Logger.Debugln("request complete")
}
// Panic logs if the request panics
func (l *StructuredLoggerEntry) Panic(v interface{}, stack []byte) {
l.Logger = l.Logger.WithFields(logrus.Fields{
"stack": string(stack),
"panic": fmt.Sprintf("%+v", v),
})
}
// GetLogEntry helper function for getting log entry for request
func GetLogEntry(r *http.Request) logrus.FieldLogger {
entry := middleware.GetLogEntry(r).(*StructuredLoggerEntry)
return entry.Logger
}
// LogEntrySetField sets a key's value
func LogEntrySetField(r *http.Request, key string, value interface{}) {
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
entry.Logger = entry.Logger.WithField(key, value)
}
}
// LogEntrySetFields sets the log entry's fields
func LogEntrySetFields(r *http.Request, fields map[string]interface{}) {
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
entry.Logger = entry.Logger.WithFields(fields)
}
}