diff --git a/backend/internal/channel/handler.go b/backend/internal/channel/handler.go index 65e5d07..2e743d5 100644 --- a/backend/internal/channel/handler.go +++ b/backend/internal/channel/handler.go @@ -2,6 +2,7 @@ package channel import ( "context" + "errors" "fmt" "log/slog" @@ -21,16 +22,20 @@ func NewHandler(store *Store) *ChannelHandler { } func (handler *ChannelHandler) CreateChannel(ctx context.Context, req *radiov1.CreateChannelRequest) (*radiov1.CreateChannelResponse, error) { - - _, ok := user.FromContext(ctx) + user, ok := user.FromContext(ctx) if !ok { slog.Error("User missing from context in protected route") return nil, connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("User not authenticated")) } - // Use the store to create channel - // Note: We ignore the request frequency for this dummy implementation as per original code - c, _ := handler.store.CreateChannel("97.1", "") + c, err := handler.store.CreateChannel(ctx, user.ID, req.Frequency, "insane Description") + if err != nil { + if errors.Is(err, ErrFrequencyExists) { + return nil, connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("Frequency already exists in database")) + } else { + return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("Couldnt insert channel into database")) + } + } channelProto := &radiov1.Channel{Id: c.ID, Frequency: c.Frequency, Description: c.Description} return &radiov1.CreateChannelResponse{Channel: channelProto}, nil diff --git a/backend/internal/channel/model.go b/backend/internal/channel/model.go index 43feb7f..614d203 100644 --- a/backend/internal/channel/model.go +++ b/backend/internal/channel/model.go @@ -1,7 +1,16 @@ package channel +import ( + "time" + + "github.com/google/uuid" +) + type Channel struct { ID string + OwnerID uuid.UUID Frequency string Description string + CreatedAt time.Time + ModifiedAt time.Time } diff --git a/backend/internal/channel/store.go b/backend/internal/channel/store.go index 6b63238..40ed9a6 100644 --- a/backend/internal/channel/store.go +++ b/backend/internal/channel/store.go @@ -1,6 +1,15 @@ package channel -import "database/sql" +import ( + "context" + "database/sql" + "errors" + "fmt" + "log/slog" + + "github.com/google/uuid" + "github.com/lib/pq" +) type Store struct { db *sql.DB @@ -10,12 +19,33 @@ func NewStore(db *sql.DB) *Store { return &Store{db: db} } -// CreateChannel is a placeholder for DB logic -func (s *Store) CreateChannel(frequency, description string) (*Channel, error) { - // TODO: Implement actual DB insert - return &Channel{ - ID: "1", - Frequency: "97.1", - Description: "", - }, nil +var ErrFrequencyExists = errors.New("a channel with this frequency already exists") + +func (s *Store) CreateChannel(ctx context.Context, owner_id uuid.UUID, frequency, description string) (*Channel, error) { + var c Channel + query := ` + INSERT INTO channels (owner_id, frequency, description) + VALUES ($1, $2, $3) + RETURNING id, owner_id, frequency, description, created_at, modified_at + ` + + err := s.db.QueryRowContext(ctx, query, owner_id, frequency, description).Scan( + &c.ID, &c.OwnerID, &c.Frequency, &c.Description, &c.CreatedAt, &c.ModifiedAt, + ) + + if err != nil { + var pqErr *pq.Error + if errors.As(err, &pqErr) { + if pqErr.Code == "23505" && pqErr.Constraint == "channels_frequency_key" { + slog.Info("Channel with frequency already exists", "frequency", frequency) + return nil, ErrFrequencyExists + } + } + + slog.Error("Error while inserting channel into database", "error", err) + return nil, fmt.Errorf("failed to insert channel into db: %w", err) + } + slog.Info("Insert channel into database", "frequency", c.Frequency) + + return &c, nil } diff --git a/backend/migrations/20260115181948_add_channel.sql b/backend/migrations/20260115181948_add_channel.sql new file mode 100644 index 0000000..36f771a --- /dev/null +++ b/backend/migrations/20260115181948_add_channel.sql @@ -0,0 +1,23 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE channels ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + owner_id UUID, + + CONSTRAINT fk_owner + FOREIGN KEY(owner_id) + REFERENCES users(id) + ON DELETE SET NULL, + + frequency TEXT UNIQUE, + description TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + modified_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE channels; +-- +goose StatementEnd