Added some files
This commit is contained in:
@@ -0,0 +1,94 @@
|
|||||||
|
/* Pure CSS styles for Music Search */
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-container {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search_term {
|
||||||
|
width: 60vw;
|
||||||
|
max-width: 600px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid #9ca3af;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: #e5e7eb;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search_term:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: #f97316;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear:hover {
|
||||||
|
background-color: #ea580c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#games-container {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Game result cards */
|
||||||
|
.bg-green-100 {
|
||||||
|
background-color: #dcfce7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-4 {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-md {
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-lg {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-6 {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
#search_term {
|
||||||
|
width: 80vw;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/labstack/echo/v5"
|
||||||
|
"github.com/labstack/echo/v5/middleware"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestLogger is an Echo middleware that logs HTTP requests using Zap
|
||||||
|
func RequestLogger() echo.MiddlewareFunc {
|
||||||
|
return middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
|
||||||
|
LogStatus: true,
|
||||||
|
LogURI: true,
|
||||||
|
LogMethod: true,
|
||||||
|
HandleError: true,
|
||||||
|
LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error {
|
||||||
|
logger := GetLogger()
|
||||||
|
|
||||||
|
fields := []zap.Field{
|
||||||
|
zap.String("method", v.Method),
|
||||||
|
zap.String("uri", v.URI),
|
||||||
|
zap.Int("status", v.Status),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Error != nil {
|
||||||
|
fields = append(fields, zap.String("error", v.Error.Error()))
|
||||||
|
logger.Error("Request error", fields...)
|
||||||
|
} else {
|
||||||
|
logger.Info("Request completed", fields...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorHandler is a custom error handler that logs errors
|
||||||
|
func ErrorHandler(err error, c *echo.Context) {
|
||||||
|
logger := GetLogger()
|
||||||
|
logger.Error("Error occurred",
|
||||||
|
zap.String("method", c.Request().Method),
|
||||||
|
zap.String("path", c.Request().URL.Path),
|
||||||
|
zap.String("error", err.Error()),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Logger is the global logger instance
|
||||||
|
Logger *zap.Logger
|
||||||
|
|
||||||
|
// SugaredLogger is the global sugared logger instance
|
||||||
|
SugaredLogger *zap.SugaredLogger
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init initializes the logger with the specified level and config
|
||||||
|
func Init(level string, jsonOutput bool) {
|
||||||
|
var config zap.Config
|
||||||
|
|
||||||
|
// Set the log level
|
||||||
|
logLevel := zap.NewAtomicLevel()
|
||||||
|
err := logLevel.UnmarshalText([]byte(level))
|
||||||
|
if err != nil {
|
||||||
|
logLevel.SetLevel(zap.InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if jsonOutput {
|
||||||
|
// JSON output for Grafana Loki
|
||||||
|
config = zap.Config{
|
||||||
|
Level: logLevel,
|
||||||
|
Development: false,
|
||||||
|
Sampling: nil,
|
||||||
|
Encoding: "json",
|
||||||
|
EncoderConfig: zapcore.EncoderConfig{
|
||||||
|
MessageKey: "msg",
|
||||||
|
LevelKey: "level",
|
||||||
|
TimeKey: "time",
|
||||||
|
NameKey: "logger",
|
||||||
|
CallerKey: "caller",
|
||||||
|
FunctionKey: zapcore.OmitKey,
|
||||||
|
StacktraceKey: "stacktrace",
|
||||||
|
SkipLineEnding: false,
|
||||||
|
LineEnding: zapcore.DefaultLineEnding,
|
||||||
|
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||||
|
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||||
|
EncodeDuration: zapcore.StringDurationEncoder,
|
||||||
|
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||||
|
},
|
||||||
|
OutputPaths: []string{"stdout"},
|
||||||
|
ErrorOutputPaths: []string{"stderr"},
|
||||||
|
InitialFields: map[string]interface{}{"service": "music-server"},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Human-readable output for development
|
||||||
|
config = zap.Config{
|
||||||
|
Level: logLevel,
|
||||||
|
Development: true,
|
||||||
|
Sampling: nil,
|
||||||
|
Encoding: "console",
|
||||||
|
EncoderConfig: zapcore.EncoderConfig{
|
||||||
|
MessageKey: "msg",
|
||||||
|
LevelKey: "level",
|
||||||
|
TimeKey: "time",
|
||||||
|
NameKey: "logger",
|
||||||
|
CallerKey: "caller",
|
||||||
|
FunctionKey: zapcore.OmitKey,
|
||||||
|
StacktraceKey: "stacktrace",
|
||||||
|
SkipLineEnding: false,
|
||||||
|
LineEnding: zapcore.DefaultLineEnding,
|
||||||
|
EncodeLevel: zapcore.CapitalLevelEncoder,
|
||||||
|
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||||
|
EncodeDuration: zapcore.StringDurationEncoder,
|
||||||
|
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||||
|
},
|
||||||
|
OutputPaths: []string{"stdout"},
|
||||||
|
ErrorOutputPaths: []string{"stderr"},
|
||||||
|
InitialFields: map[string]interface{}{"service": "music-server"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger, err := config.Build()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger = logger
|
||||||
|
SugaredLogger = logger.Sugar()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogger returns the global logger
|
||||||
|
func GetLogger() *zap.Logger {
|
||||||
|
if Logger == nil {
|
||||||
|
Init("info", false)
|
||||||
|
}
|
||||||
|
return Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSugaredLogger returns the global sugared logger
|
||||||
|
func GetSugaredLogger() *zap.SugaredLogger {
|
||||||
|
if SugaredLogger == nil {
|
||||||
|
Init("info", false)
|
||||||
|
}
|
||||||
|
return SugaredLogger
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user