Skip to content

Commit af95417

Browse files
authored
Pmiddleton/fix project delete (#38)
## Summary <!-- What does this PR do? Why? --> ## Changes <!-- Bullet list of changes --> ## Test plan <!-- How was this tested? --> - [ ] `make test` passes - [ ] `cd web && npx tsc --noEmit` passes
1 parent 92d8b49 commit af95417

37 files changed

Lines changed: 694 additions & 128 deletions

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ jobs:
2626
- name: Install dependencies
2727
run: go mod download
2828

29+
- name: Create frontend stub for embed
30+
run: mkdir -p web/dist && touch web/dist/.gitkeep
31+
32+
- name: Lint
33+
uses: golangci/golangci-lint-action@v7
34+
with:
35+
version: v2.10
36+
2937
# Build internal packages and cmd/server (excludes Wails app which needs web/dist)
3038
- name: Build
3139
run: |

.golangci.yml

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
version: "2"
2+
3+
run:
4+
timeout: 5m
5+
6+
linters:
7+
enable:
8+
- errcheck
9+
- govet
10+
- staticcheck
11+
- unused
12+
- ineffassign
13+
- gocritic
14+
- misspell
15+
- bodyclose
16+
- durationcheck
17+
- errname
18+
- nilerr
19+
- sqlclosecheck
20+
- unconvert
21+
- unparam
22+
- wastedassign
23+
24+
settings:
25+
errcheck:
26+
check-type-assertions: false
27+
check-blank: false
28+
exclude-functions:
29+
# Network cleanup — errors not actionable
30+
- (net.Conn).Close
31+
- (net.Conn).SetDeadline
32+
- (net.Conn).SetReadDeadline
33+
- (*net.UDPConn).Close
34+
- (*net.UDPConn).SetDeadline
35+
- (*net.UDPConn).SetReadDeadline
36+
- (*net.UDPConn).WriteToUDP
37+
- (*database/sql.DB).Close
38+
- (*os.File).Close
39+
- (io.Closer).Close
40+
- (*io.PipeWriter).Close
41+
- (*mime/multipart.Writer).Close
42+
- (*net/http.Response).Body.Close
43+
- os.Remove
44+
- os.MkdirAll
45+
- encoding/json.Unmarshal
46+
- (*encoding/json.Encoder).Encode
47+
- (*encoding/json.Decoder).Decode
48+
gocritic:
49+
enabled-tags:
50+
- diagnostic
51+
- performance
52+
disabled-checks:
53+
- hugeParam
54+
- rangeValCopy
55+
- ifElseChain
56+
- filepathJoin
57+
- exitAfterDefer
58+
misspell:
59+
locale: US
60+
61+
exclusions:
62+
paths:
63+
- web
64+
- site
65+
- build
66+
- migrations
67+
rules:
68+
# Test files: relax errcheck and unparam
69+
- path: _test\.go
70+
linters:
71+
- unparam
72+
- errcheck
73+
- gocritic
74+
# Defer cleanup — error is not actionable
75+
- linters:
76+
- errcheck
77+
source: "defer "
78+
# Deprecated websocket library — tracked separately
79+
- linters:
80+
- staticcheck
81+
text: "SA1019:"
82+
# IPv6 hostport in printer discovery
83+
- linters:
84+
- govet
85+
text: "hostport"
86+
# "cancelled" is used in JSON struct tags and API responses
87+
- linters:
88+
- misspell
89+
text: "(?i)cancelled"
90+
# Best-effort event logging / audit trail
91+
- linters:
92+
- errcheck
93+
source: "AddEvent|AppendEvent"
94+
# Best-effort cleanup of OAuth state
95+
- linters:
96+
- errcheck
97+
source: "DeleteOAuthState|UpdateLastSync|UpdateWebhookEventProcessed"
98+
# Background printer connections (fire-and-forget goroutines)
99+
- linters:
100+
- errcheck
101+
source: "go s\\.(manager|printerMgr)\\.Connect"
102+
# Migration ALTER TABLE — intentionally ignores "column already exists"
103+
- linters:
104+
- errcheck
105+
source: "// Ignore error if"

Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: help dev build run test clean frontend backend stop start restart show-version bump-patch bump-minor bump-major release site site-build
1+
.PHONY: help dev build run test clean frontend backend stop start restart show-version bump-patch bump-minor bump-major release site site-build lint lint-go lint-web
22

33
# Version
44
VERSION := $(shell cat VERSION | tr -d 'v\n')
@@ -23,6 +23,9 @@ help:
2323
@echo " make frontend - Run React frontend only"
2424
@echo " make build - Build production binaries"
2525
@echo " make test - Run tests"
26+
@echo " make lint - Run all linters"
27+
@echo " make lint-go - Run Go linter (golangci-lint)"
28+
@echo " make lint-web - Run frontend linter (ESLint)"
2629
@echo " make clean - Clean build artifacts"
2730
@echo ""
2831
@echo "Site:"
@@ -68,6 +71,15 @@ test-coverage:
6871
go test -coverprofile=coverage.out ./...
6972
go tool cover -html=coverage.out
7073

74+
# Linting
75+
lint: lint-go lint-web
76+
77+
lint-go:
78+
golangci-lint run ./...
79+
80+
lint-web:
81+
cd web && npm run lint
82+
7183
# Cleanup
7284
clean:
7385
rm -rf bin/

internal/api/dispatch_handler.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,3 @@ func (h *DispatchHandler) UpdatePrinterSettings(w http.ResponseWriter, r *http.R
162162

163163
respondJSON(w, http.StatusOK, settings)
164164
}
165-
166-
// PrintJobHandler extension for priority update
167-
type printJobPriorityRequest struct {
168-
Priority int `json:"priority"`
169-
}

internal/api/handlers.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ func (h *DesignHandler) Download(w http.ResponseWriter, r *http.Request) {
519519

520520
w.Header().Set("Content-Disposition", "attachment; filename="+design.FileName)
521521
w.Header().Set("Content-Type", "application/octet-stream")
522-
io.Copy(w, reader)
522+
io.Copy(w, reader) //nolint:errcheck // best-effort streaming to HTTP client
523523
}
524524

525525
// OpenExternal opens a design file in an external application.
@@ -918,6 +918,22 @@ func (h *SpoolHandler) Get(w http.ResponseWriter, r *http.Request) {
918918
respondJSON(w, http.StatusOK, spool)
919919
}
920920

921+
// Delete deletes a spool by ID.
922+
func (h *SpoolHandler) Delete(w http.ResponseWriter, r *http.Request) {
923+
id, err := parseUUID(r, "id")
924+
if err != nil {
925+
respondError(w, http.StatusBadRequest, "invalid spool ID")
926+
return
927+
}
928+
929+
if err := h.service.Delete(r.Context(), id); err != nil {
930+
respondError(w, http.StatusInternalServerError, err.Error())
931+
return
932+
}
933+
934+
w.WriteHeader(http.StatusNoContent)
935+
}
936+
921937
// PrintJobHandler handles print job endpoints.
922938
type PrintJobHandler struct {
923939
service *service.PrintJobService
@@ -1396,7 +1412,7 @@ func (h *FileHandler) Get(w http.ResponseWriter, r *http.Request) {
13961412

13971413
w.Header().Set("Content-Disposition", "attachment; filename="+file.OriginalName)
13981414
w.Header().Set("Content-Type", file.ContentType)
1399-
io.Copy(w, reader)
1415+
io.Copy(w, reader) //nolint:errcheck // best-effort streaming to HTTP client
14001416
}
14011417

14021418
// ExpenseHandler handles expense endpoints.

internal/api/router.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ func NewRouter(services *service.Services, hub *realtime.Hub) http.Handler {
196196
r.Get("/", spoolHandler.List)
197197
r.Post("/", spoolHandler.Create)
198198
r.Get("/{id}", spoolHandler.Get)
199+
r.Delete("/{id}", spoolHandler.Delete)
199200
})
200201

201202
// Print Jobs

internal/api/templates_test.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,6 @@ func (m *MockTemplateService) CreateProjectFromTemplate(ctx context.Context, tem
109109
return project, []model.PrintJob{}, nil
110110
}
111111

112-
// Test helper to create a template handler with mock service
113-
func setupTemplateHandler() (*TemplateHandler, *MockTemplateService) {
114-
mock := NewMockTemplateService()
115-
// We need to wrap the mock in a real service struct for the handler
116-
// For now, we'll test via HTTP handlers directly
117-
return nil, mock
118-
}
119-
120112
func TestTemplateHandler_Create(t *testing.T) {
121113
// Create a test template
122114
template := model.Template{

internal/database/schema.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ CREATE INDEX IF NOT EXISTS idx_projects_sku ON projects(sku);
115115
-- Tasks table (Work Instances - created when processing orders)
116116
CREATE TABLE IF NOT EXISTS tasks (
117117
id TEXT PRIMARY KEY,
118-
project_id TEXT NOT NULL REFERENCES projects(id),
118+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
119119
order_id TEXT REFERENCES orders(id),
120120
order_item_id TEXT REFERENCES order_items(id),
121121
name TEXT NOT NULL,
@@ -816,7 +816,7 @@ CREATE TABLE IF NOT EXISTS quotes (
816816
CREATE INDEX IF NOT EXISTS idx_quotes_status ON quotes(status);
817817
CREATE INDEX IF NOT EXISTS idx_quotes_customer ON quotes(customer_id);
818818
CREATE INDEX IF NOT EXISTS idx_quotes_quote_number ON quotes(quote_number);
819-
CREATE UNIQUE INDEX IF NOT EXISTS idx_quotes_share_token ON quotes(share_token);
819+
-- idx_quotes_share_token is created in sqlite.go after ALTER TABLE adds the column for existing databases
820820

821821
CREATE TABLE IF NOT EXISTS quote_options (
822822
id TEXT PRIMARY KEY,

internal/database/sqlite.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func RunMigrations(db *sql.DB) error {
132132
}
133133

134134
// Create indexes that may not exist
135-
db.Exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_quotes_share_token ON quotes(share_token)`)
135+
db.Exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_quotes_share_token ON quotes(share_token)`) //nolint:errcheck // best-effort index creation
136136

137137
return nil
138138
}

internal/printer/bambu.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ func (c *BambuClient) requestPushAll() {
262262
Command: "pushall",
263263
},
264264
}
265-
c.sendCommand(cmd)
265+
c.sendCommand(cmd) //nolint:errcheck // fire-and-forget status refresh
266266
}
267267

268268
// sendCommand sends a command to the printer via MQTT.
@@ -415,7 +415,7 @@ func (c *BambuClient) uploadFile(localPath string, remoteName string) (string, e
415415
remoteDir := "/cache"
416416
if err := conn.ChangeDir(remoteDir); err != nil {
417417
remoteDir = "/"
418-
conn.ChangeDir(remoteDir)
418+
conn.ChangeDir(remoteDir) //nolint:errcheck // fallback to root dir
419419
}
420420

421421
// Upload file

0 commit comments

Comments
 (0)