Skip to content

Commit 0e450d3

Browse files
committed
chores: code clean up, linter fix, security improvement
1 parent 3e425e5 commit 0e450d3

169 files changed

Lines changed: 4171 additions & 3096 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Thumbs.db
55
*.log
66
tmp/
77
temp/
8+
.agent/
89

910
# IDEs
1011
.idea/

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Configuration is managed via the **Settings** page in the UI or environment vari
109109

110110
> [!TIP]
111111
> **Unlock the full potential of your Agent**<br>
112-
> Check out the **[Agent Prompting Guide](docs/agent.md)** for best practices, workflow examples, and **system prompt templates** (`CLAUDE.md`, `GEMINI.md`) to paste into your project.
112+
> Check out the **[Agent Prompting Guide](docs/agent-prompting-guide.md)** for best practices, workflow examples, and **system prompt templates** (`CLAUDE.md`, `GEMINI.md`) to paste into your project.
113113

114114
### 1. Add Data Sources
115115
Navigate to the Admin Dashboard ([http://localhost:3000](http://localhost:3000)) and click **"Add Source"**.

apps/backend/features/job/handler.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
4141
"data": jobs,
4242
"meta": map[string]int{"count": len(jobs)},
4343
}
44-
json.NewEncoder(w).Encode(resp)
44+
if err := json.NewEncoder(w).Encode(resp); err != nil {
45+
slog.ErrorContext(ctx, "failed to encode response", "error", err)
46+
}
4547
}
4648

4749
func (h *Handler) Retry(w http.ResponseWriter, r *http.Request) {
@@ -63,7 +65,9 @@ func (h *Handler) Retry(w http.ResponseWriter, r *http.Request) {
6365

6466
w.Header().Set("Content-Type", "application/json")
6567
w.WriteHeader(http.StatusOK)
66-
json.NewEncoder(w).Encode(map[string]interface{}{"data": "job retried"})
68+
if err := json.NewEncoder(w).Encode(map[string]interface{}{"data": "job retried"}); err != nil {
69+
slog.ErrorContext(ctx, "failed to encode response", "error", err)
70+
}
6771
}
6872

6973
func (h *Handler) writeError(ctx context.Context, w http.ResponseWriter, code, message string, status int) {
@@ -78,5 +82,7 @@ func (h *Handler) writeError(ctx context.Context, w http.ResponseWriter, code, m
7882
"correlationId": middleware.GetCorrelationID(ctx),
7983
}
8084

81-
json.NewEncoder(w).Encode(resp)
85+
if err := json.NewEncoder(w).Encode(resp); err != nil {
86+
slog.Error("failed to encode error response", "error", err)
87+
}
8288
}

apps/backend/features/job/handler_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,28 @@ func (m *MockRepo) Save(ctx context.Context, j *job.Job) error {
2323
args := m.Called(ctx, j)
2424
return args.Error(0)
2525
}
26+
2627
func (m *MockRepo) List(ctx context.Context) ([]job.Job, error) {
2728
args := m.Called(ctx)
2829
if args.Get(0) == nil {
2930
return nil, args.Error(1)
3031
}
3132
return args.Get(0).([]job.Job), args.Error(1)
3233
}
34+
3335
func (m *MockRepo) Get(ctx context.Context, id string) (*job.Job, error) {
3436
args := m.Called(ctx, id)
3537
if args.Get(0) == nil {
3638
return nil, args.Error(1)
3739
}
3840
return args.Get(0).(*job.Job), args.Error(1)
3941
}
42+
4043
func (m *MockRepo) Delete(ctx context.Context, id string) error {
4144
args := m.Called(ctx, id)
4245
return args.Error(0)
4346
}
47+
4448
func (m *MockRepo) Count(ctx context.Context) (int, error) {
4549
args := m.Called(ctx)
4650
return args.Int(0), args.Error(1)
@@ -111,4 +115,4 @@ func TestHandler_Retry(t *testing.T) {
111115
assert.Equal(t, http.StatusOK, w.Result().StatusCode)
112116
mockRepo.AssertExpectations(t)
113117
mockPub.AssertExpectations(t)
114-
}
118+
}

apps/backend/features/job/repo_integration_test.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ package job_test
22

33
import (
44
"context"
5+
"database/sql"
56
"encoding/json"
67
"testing"
78
"time"
8-
"database/sql"
99

10+
"github.com/google/uuid"
1011
"github.com/stretchr/testify/assert"
1112
"github.com/stretchr/testify/require"
12-
"github.com/google/uuid"
1313
"qurio/apps/backend/features/job"
1414
"qurio/apps/backend/features/source"
1515
"qurio/apps/backend/internal/testutils"
@@ -75,7 +75,7 @@ func TestJobRepo_Integration(t *testing.T) {
7575
// 5. Verify Delete
7676
err = jobRepo.Delete(ctx, j1.ID)
7777
require.NoError(t, err)
78-
78+
7979
// Verify it's gone
8080
_, err = jobRepo.Get(ctx, j1.ID)
8181
assert.Error(t, err)
@@ -96,7 +96,9 @@ func TestJobRepo_Integration(t *testing.T) {
9696
}
9797

9898
func TestJobRepo_Empty(t *testing.T) {
99-
if testing.Short() { t.Skip() }
99+
if testing.Short() {
100+
t.Skip()
101+
}
100102
s := testutils.NewIntegrationSuite(t)
101103
s.Setup()
102104
defer s.Teardown()
@@ -114,7 +116,9 @@ func TestJobRepo_Empty(t *testing.T) {
114116
}
115117

116118
func TestJobRepo_Get_NotFound(t *testing.T) {
117-
if testing.Short() { t.Skip() }
119+
if testing.Short() {
120+
t.Skip()
121+
}
118122
s := testutils.NewIntegrationSuite(t)
119123
s.Setup()
120124
defer s.Teardown()
@@ -125,4 +129,4 @@ func TestJobRepo_Get_NotFound(t *testing.T) {
125129
_, err := jobRepo.Get(ctx, uuid.NewString())
126130
assert.Error(t, err)
127131
assert.Equal(t, sql.ErrNoRows, err)
128-
}
132+
}

apps/backend/features/job/repo_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ package job_test
22

33
import (
44
"context"
5+
"encoding/json"
56
"regexp"
67
"testing"
78
"time"
8-
"encoding/json"
99

1010
"github.com/DATA-DOG/go-sqlmock"
1111
"github.com/stretchr/testify/assert"

apps/backend/features/job/service_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func TestRetry_Timeout(t *testing.T) {
5050
// though not ideal for fast unit tests.
5151
// Alternatively, I can't easily mock `time.After` without dependency injection.
5252
// I'll proceed with the 6s sleep for correctness verification as per plan "Verify test passes".
53-
53+
5454
err := service.Retry(context.Background(), "1")
5555
if err == nil {
5656
t.Fatal("Expected timeout error, got nil")
@@ -113,20 +113,20 @@ func (m *MockJobRepoForTopic) Get(ctx context.Context, id string) (*Job, error)
113113
return &Job{ID: id, Payload: m.Payload}, nil
114114
}
115115
func (m *MockJobRepoForTopic) Delete(ctx context.Context, id string) error { return nil }
116-
func (m *MockJobRepoForTopic) List(ctx context.Context) ([]Job, error) { return nil, nil }
117-
func (m *MockJobRepoForTopic) Count(ctx context.Context) (int, error) { return 0, nil }
118-
func (m *MockJobRepoForTopic) Save(ctx context.Context, job *Job) error { return nil }
116+
func (m *MockJobRepoForTopic) List(ctx context.Context) ([]Job, error) { return nil, nil }
117+
func (m *MockJobRepoForTopic) Count(ctx context.Context) (int, error) { return 0, nil }
118+
func (m *MockJobRepoForTopic) Save(ctx context.Context, job *Job) error { return nil }
119119

120120
func TestRetry_TopicSelection(t *testing.T) {
121121
pub := &MockPublisher{}
122122
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
123-
123+
124124
// We need to inject the specific repo behavior.
125125
// Since NewService takes Repository interface, we can pass our custom mock.
126126
customRepo := &MockJobRepoForTopic{
127127
Payload: []byte(`{"type": "file", "path": "/tmp/test.pdf"}`),
128128
}
129-
129+
130130
service := NewService(customRepo, pub, logger)
131131

132132
err := service.Retry(context.Background(), "1")

apps/backend/features/mcp/handler.go

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ type SourceManager interface {
2323
}
2424

2525
type Handler struct {
26-
retriever Retriever
27-
sourceMgr SourceManager
26+
retriever Retriever
27+
sourceMgr SourceManager
2828
}
2929

3030
func NewHandler(r Retriever, s SourceManager) *Handler {
@@ -127,7 +127,7 @@ func (h *Handler) processRequest(ctx context.Context, req JSONRPCRequest) *JSONR
127127
Result: ListToolsResult{
128128
Tools: []Tool{
129129
{
130-
Name: "qurio_search",
130+
Name: "qurio_search",
131131
Description: `Search & Exploration tool. Performs a hybrid search (Keyword + Vector). Use this for specific questions, finding code snippets, or exploring topics across known sources.
132132
133133
ARGUMENT GUIDE:
@@ -183,7 +183,7 @@ USAGE EXAMPLES:
183183
},
184184
},
185185
{
186-
Name: "qurio_list_sources",
186+
Name: "qurio_list_sources",
187187
Description: `Discovery tool. Lists all available documentation sets (sources) currently indexed. Use this at the start of a session to understand what documentation is available.
188188
189189
USAGE EXAMPLE:
@@ -194,7 +194,7 @@ qurio_list_sources()`,
194194
},
195195
},
196196
{
197-
Name: "qurio_list_pages",
197+
Name: "qurio_list_pages",
198198
Description: `Navigation tool. Lists all individual pages/documents within a specific source. Use this to find the exact URL of a document when a search query is too broad or to browse the table of contents.
199199
200200
USAGE EXAMPLE:
@@ -211,7 +211,7 @@ qurio_list_pages(source_id="src_stripe_api")`,
211211
},
212212
},
213213
{
214-
Name: "qurio_read_page",
214+
Name: "qurio_read_page",
215215
Description: `Deep Reading / Full Context tool. Retrieves the *entire* content of a specific page or document by its URL. Use this when a search result snippet is truncated or insufficient, or when you need to read a full guide/tutorial. Crucial: Always prefer this over guessing content if the search result is incomplete.
216216
217217
USAGE EXAMPLE:
@@ -256,7 +256,7 @@ read_page(url="https://docs.stripe.com/webhooks/signatures")`,
256256
resp := makeErrorResponse(req.ID, ErrInvalidParams, "Invalid search arguments")
257257
return &resp
258258
}
259-
259+
260260
if args.Query == "" {
261261
resp := makeErrorResponse(req.ID, ErrInvalidParams, "Query is required")
262262
return &resp
@@ -285,7 +285,7 @@ read_page(url="https://docs.stripe.com/webhooks/signatures")`,
285285
resp := makeErrorResponse(req.ID, ErrInternal, "Search failed: "+err.Error())
286286
return &resp
287287
}
288-
288+
289289
var textResult string
290290
if len(results) == 0 {
291291
textResult = "No results found."
@@ -305,21 +305,21 @@ read_page(url="https://docs.stripe.com/webhooks/signatures")`,
305305
if res.SourceID != "" {
306306
textResult += fmt.Sprintf("SourceID: %s\n", res.SourceID)
307307
}
308-
308+
309309
textResult += fmt.Sprintf("Content:\n%s\n", res.Content)
310-
310+
311311
// Optional: Show other metadata
312312
// if len(res.Metadata) > 0 {
313313
// meta, _ := json.Marshal(res.Metadata)
314314
// txtResult += fmt.Sprintf("Metadata: %s\n", string(meta))
315315
// }
316316
textResult += "\n---\n"
317317
}
318-
318+
319319
textResult += "\nUse qurio_read_page(url=\"...\") to read the full content of any result.\n"
320320
}
321321

322-
slog.Info("tool execution completed", "tool", "qurio_search", "result_count", len(results))
322+
slog.Info("tool execution completed", "tool", "qurio_search", "result_count", len(results)) // #nosec G706 -- len() result is int, not tainted
323323

324324
return &JSONRPCResponse{
325325
JSONRPC: "2.0",
@@ -364,7 +364,7 @@ read_page(url="https://docs.stripe.com/webhooks/signatures")`,
364364
Type string `json:"type"`
365365
URL string `json:"url"`
366366
}
367-
367+
368368
simpleSources := make([]SimpleSource, len(sources))
369369
for i, s := range sources {
370370
name := s.Name
@@ -413,7 +413,7 @@ read_page(url="https://docs.stripe.com/webhooks/signatures")`,
413413
resp := makeErrorResponse(req.ID, ErrInvalidParams, "Invalid arguments")
414414
return &resp
415415
}
416-
416+
417417
if args.SourceID == "" {
418418
resp := makeErrorResponse(req.ID, ErrInvalidParams, "source_id is required")
419419
return &resp
@@ -448,7 +448,7 @@ read_page(url="https://docs.stripe.com/webhooks/signatures")`,
448448
ID string `json:"id"`
449449
URL string `json:"url"`
450450
}
451-
451+
452452
simplePages := make([]SimplePage, len(pages))
453453
for i, p := range pages {
454454
simplePages[i] = SimplePage{
@@ -488,7 +488,7 @@ read_page(url="https://docs.stripe.com/webhooks/signatures")`,
488488
resp := makeErrorResponse(req.ID, ErrInvalidParams, "Invalid arguments")
489489
return &resp
490490
}
491-
491+
492492
if args.URL == "" {
493493
resp := makeErrorResponse(req.ID, ErrInvalidParams, "URL is required")
494494
return &resp
@@ -525,7 +525,7 @@ read_page(url="https://docs.stripe.com/webhooks/signatures")`,
525525
}
526526
}
527527

528-
slog.Info("tool execution completed", "tool", "qurio_read_page", "chunk_count", len(results))
528+
slog.Info("tool execution completed", "tool", "qurio_read_page", "chunk_count", len(results)) // #nosec G706 -- len() result is int, not tainted
529529

530530
return &JSONRPCResponse{
531531
JSONRPC: "2.0",
@@ -537,12 +537,12 @@ read_page(url="https://docs.stripe.com/webhooks/signatures")`,
537537
},
538538
}
539539
}
540-
540+
541541
slog.Warn("method not found", "method", params.Name)
542542
resp := makeErrorResponse(req.ID, ErrMethodNotFound, "Method not found: "+params.Name)
543543
return &resp
544544
}
545-
545+
546546
slog.Warn("unknown jsonrpc method", "method", req.Method)
547547
resp := makeErrorResponse(req.ID, ErrMethodNotFound, "Method not found")
548548
return &resp
@@ -560,7 +560,7 @@ func makeErrorResponse(id interface{}, code int, message string) JSONRPCResponse
560560
}
561561

562562
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
563-
slog.Info("mcp request received", "method", r.Method, "path", r.URL.Path)
563+
slog.Info("mcp request received", "method", r.Method, "path", r.URL.Path) // #nosec G706 -- r.URL.Path is parsed by Go's net/http, not raw user input
564564

565565
w.Header().Set("Content-Type", "application/json")
566566

@@ -597,7 +597,7 @@ func (h *Handler) writeError(w http.ResponseWriter, id interface{}, code int, me
597597
// However, the helper above `writeError` was defined at file level? No, it's a method.
598598
// Let's check if it's already defined in this file. It was in the previous version.
599599
// I will just call it.
600-
600+
601601
resp := makeErrorResponse(id, code, message)
602602
if err := json.NewEncoder(w).Encode(resp); err != nil {
603603
slog.Error("failed to write error response", "error", err)

0 commit comments

Comments
 (0)