Skip to content

Commit

Permalink
using saved data for api calls, but wrong metadata on page
Browse files Browse the repository at this point in the history
  • Loading branch information
slackspace-io committed Mar 10, 2024
1 parent 5bc32f6 commit 1330441
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 46 deletions.
1 change: 1 addition & 0 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/robfig/cron/v3 v3.0.1
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/oauth2 v0.18.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
Expand Down
21 changes: 15 additions & 6 deletions backend/internal/app/api.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
package app

import (
"context"
"encoding/json"
"net/http"
)

type SystemConfig struct {
Schedule string `yaml:"schedule"`
}

func (app *Application) RegisterHandlers() {
http.HandleFunc("/api/images", app.handleListImages)
}

func (app *Application) handleListImages(w http.ResponseWriter, r *http.Request) {
images, err := app.Kubernetes.ListContainerImages("default") // Assume using "default" namespace
if err != nil {
http.Error(w, "Failed to get images", http.StatusInternalServerError)
return
refresh := r.URL.Query().Get("refresh")
if refresh == "true" {
// Trigger data refresh logic here
} else {
// Use saved data to respond to the request
savedData, err := app.DataService.GetData(context.Background())
if err != nil {
http.Error(w, "Failed to get saved data", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(savedData)
}

json.NewEncoder(w).Encode(images)
}

117 changes: 78 additions & 39 deletions backend/internal/app/app.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
package app

import (
"encoding/json"
"context"
"log"
"net/http"
"slackwatch/backend/internal/kubernetes"
"slackwatch/backend/internal/notifications"
"slackwatch/backend/internal/repochecker"
"slackwatch/backend/internal/service"
"slackwatch/backend/internal/storage"
"slackwatch/backend/pkg/config"

"github.com/robfig/cron/v3"
)

type Application struct {
Kubernetes *kubernetes.Client
Notifications *notifications.Manager
RepoChecker *repochecker.Checker
Scheduler *cron.Cron
DataService *service.DataService
System config.SystemConfig `yaml:"system"`
}

func Initialize() (*Application, error) {
Expand All @@ -22,69 +29,101 @@ func Initialize() (*Application, error) {
log.Printf("Failed to load configuration: %v", err)
return nil, err
}

// Initialize services and storage
fileStorage := &storage.FileStorage{FilePath: "data.json"}
dataService := &service.DataService{Storage: fileStorage}

// Initialize other components as before
repoChecker := repochecker.NewChecker(*cfg)
k8sClient, err := kubernetes.NewClient(&cfg.Kubernetes, repoChecker)
if err != nil {
log.Printf("Failed to initialize Kubernetes client: %v", err)
return nil, err
}

notificationManager := notifications.NewManager()

app := &Application{
Kubernetes: k8sClient,
Notifications: notificationManager,
RepoChecker: repoChecker,
DataService: dataService,
System: cfg.System,
}

app.setupRoutes()

app.scheduleTasks()

return app, nil
}

func enableCors(w *http.ResponseWriter) {
(*w).Header().Set("Access-Control-Allow-Origin", "*") // Allow any domain, adjust as necessary for security
func enableCors(w http.ResponseWriter) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
}

func (app *Application) setupRoutes() {
http.HandleFunc("/api/pods", func(w http.ResponseWriter, r *http.Request) {
enableCors(&w) // Enable CORS for this endpoint

// Debug log for entering the API endpoint
log.Println("Accessed /api/pods endpoint")

// First, find containers with the specific annotation
containers, err := app.Kubernetes.FindContainersWithAnnotation("", "diun.enable", "true")
if err != nil {
log.Printf("Error finding containers with annotation: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// Debug log for found containers
log.Printf("Found containers: %+v", containers)

// Then, pass the result directly to CheckForImageUpdates
updates, err := app.Kubernetes.CheckForImageUpdates(containers)
if err != nil {
log.Printf("Error checking for image updates: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// Debug log for image updates
log.Printf("Image updates: %+v", updates)

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(updates)
})
// Setup routes for data service with CORS enabled
http.HandleFunc("/api/images", app.enableCorsMiddleware(app.DataService.HandleGetData))
http.HandleFunc("/api/refresh", app.enableCorsMiddleware(app.handleRefreshData))

// Setup other routes as before
// Example: http.HandleFunc("/api/pods", app.handlePods)
}

// enableCorsMiddleware wraps an http.HandlerFunc with CORS headers
func (app *Application) enableCorsMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
enableCors(w)
next.ServeHTTP(w, r)
}
}

func (app *Application) handleRefreshData(w http.ResponseWriter, r *http.Request) {
// Logic to refresh data manually
log.Println("Manually refreshing data...")
app.runScheduledTask() // Directly invoke the task logic
}

func (app *Application) scheduleTasks() {
app.Scheduler = cron.New()
_, err := app.Scheduler.AddFunc(app.System.Schedule, app.runScheduledTask)
if err != nil {
log.Fatalf("Failed to schedule tasks: %v", err)
}
app.Scheduler.Start()
}

// runScheduledTask encapsulates the logic to be executed on schedule.
func (app *Application) runScheduledTask() {
log.Println("Scheduled task running...")
containers, err := app.Kubernetes.FindContainersWithAnnotation("", "slackwatch.enable", "true")
if err != nil {
log.Printf("Error finding containers: %v", err)
return
}
updates, err := app.Kubernetes.CheckForImageUpdates(containers)
if err != nil {
log.Printf("Error checking for image updates: %v", err)
return
}
// Save updates data locally
log.Printf("Saving updates data: %v", updates)
err = app.DataService.SaveData(context.Background(), updates)
if err != nil {
log.Printf("Error saving updates data: %v", err)
}
log.Printf("Data saved successfully")
}

func (app *Application) Run() error {
log.Println("Starting Slackwatch")
log.Println("Starting the application on port :8080")
// Start HTTP server
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
return nil
}

// Include other methods (e.g., scheduleKubernetesTasks) as they were previously defined
6 changes: 6 additions & 0 deletions backend/internal/model/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package model

type Data struct {
ID string `json:"id"`
Value string `json:"value"`
}
86 changes: 86 additions & 0 deletions backend/internal/service/dataservice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package service

import (
"context"
"encoding/json"
"log"
"net/http"
"os"
"slackwatch/backend/internal/model"
"slackwatch/backend/internal/storage"
"strings"
)

type DataService struct {
Storage storage.Storage
}

func (ds *DataService) SaveData(ctx context.Context, data interface{}) error {
file, err := os.OpenFile("data.json", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer file.Close()

encoder := json.NewEncoder(file)
return encoder.Encode(data)
}

func (ds *DataService) GetData(ctx context.Context) ([]map[string]string, error) {
file, err := os.Open("data.json")
if err != nil {
return nil, err
}
defer file.Close()

var data []map[string]string
decoder := json.NewDecoder(file)
err = decoder.Decode(&data)
if err != nil {
return nil, err
}

return data, nil
}

// HandleSaveData handles the HTTP request for saving data
func (ds *DataService) HandleSaveData(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return
}

var data model.Data
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

if err := ds.SaveData(context.Background(), data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusCreated)
}

// HandleGetData handles the HTTP request for retrieving data by ID
func (ds *DataService) HandleGetData(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Only GET method is allowed", http.StatusMethodNotAllowed)
return
}

id := strings.TrimPrefix(r.URL.Path, "/api/data/")
log.Printf("Getting data for ID: %s", id)
data, err := ds.GetData(context.Background())
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}

if err := json.NewEncoder(w).Encode(data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
9 changes: 9 additions & 0 deletions backend/internal/storage/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package storage

import "context"
import "slackwatch/backend/internal/model"

type Storage interface {
Save(ctx context.Context, data model.Data) error
Get(ctx context.Context, id string) (model.Data, error)
}
29 changes: 29 additions & 0 deletions backend/internal/storage/filestorage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package storage

import (
"context"
"encoding/json"
"os"
"slackwatch/backend/internal/model"
)

type FileStorage struct {
FilePath string
}

func (fs *FileStorage) Save(ctx context.Context, data model.Data) error {
file, err := os.OpenFile(fs.FilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()

encoder := json.NewEncoder(file)
return encoder.Encode(data)
}

func (fs *FileStorage) Get(ctx context.Context, id string) (model.Data, error) {
// Implementation for retrieving data by ID from file
// This is a simplified example. A real implementation might need to read the entire file and decode each entry to find the matching ID.
return model.Data{}, nil
}
5 changes: 5 additions & 0 deletions backend/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
type Config struct {
Kubernetes KubernetesConfig `yaml:"kubernetes"`
Repositories []Repository `yaml:"repositories"`
System SystemConfig `yaml:"system"`
}

type Repository struct {
Expand All @@ -28,6 +29,10 @@ type KubernetesConfig struct {
} `yaml:"outOfClusterConfig"`
}

type SystemConfig struct {
Schedule string `yaml:"schedule"`
}

// LoadConfig reads and parses the configuration file
func LoadConfig(configPath string) (*Config, error) {
log.Printf("Starting to load configuration from %s", configPath)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/routes/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export async function load({
}: {
fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
}) {
const response = await fetch(`${config!.baseURL}api/pods`);
const response = await fetch(`${config!.baseURL}api/images`);
const podsInfo = await response.json(); // Parse the response as JSON
return { podsInfo };
}

0 comments on commit 1330441

Please sign in to comment.