@@ -1801,7 +2148,9 @@ export function Dashboard() {
Version:
- {selectedObservation.version || 'N/A'}
+
+ {selectedObservation.version || 'N/A'}
+
Status:
@@ -1815,15 +2164,27 @@ export function Dashboard() {
Created At:
- {selectedObservation.created_at ? new Date(selectedObservation.created_at).toLocaleString() : 'N/A'}
+
+ {selectedObservation.created_at
+ ? new Date(selectedObservation.created_at).toLocaleString()
+ : 'N/A'}
+
Updated At:
- {selectedObservation.updated_at ? new Date(selectedObservation.updated_at).toLocaleString() : 'N/A'}
+
+ {selectedObservation.updated_at
+ ? new Date(selectedObservation.updated_at).toLocaleString()
+ : 'N/A'}
+
Synced At:
- {selectedObservation.synced_at ? new Date(selectedObservation.synced_at).toLocaleString() : 'Not synced'}
+
+ {selectedObservation.synced_at
+ ? new Date(selectedObservation.synced_at).toLocaleString()
+ : 'Not synced'}
+
{selectedObservation.geolocation && (
@@ -1841,8 +2202,8 @@ export function Dashboard() {
Data:
- {typeof selectedObservation.data === 'string'
- ? selectedObservation.data
+ {typeof selectedObservation.data === 'string'
+ ? selectedObservation.data
: JSON.stringify(selectedObservation.data, null, 2)}
@@ -1864,13 +2225,18 @@ export function Dashboard() {
e.stopPropagation()}>
Version Changes
- setShowChangesModal(false)}>×
+ setShowChangesModal(false)}>
+ ×
+
{bundleChanges.compare_version_a && bundleChanges.compare_version_b && (
- Comparing: {bundleChanges.compare_version_a} → {bundleChanges.compare_version_b}
+ Comparing: {' '}
+
+ {bundleChanges.compare_version_a} → {bundleChanges.compare_version_b}
+
)}
@@ -1896,7 +2262,9 @@ export function Dashboard() {
)}
{bundleChanges.modified && bundleChanges.modified.length > 0 && (
-
✏️ Modified ({bundleChanges.modified.length})
+
+ ✏️ Modified ({bundleChanges.modified.length})
+
{bundleChanges.modified.map((file, idx) => (
{file.path}
@@ -1905,10 +2273,10 @@ export function Dashboard() {
)}
{(!bundleChanges.added || bundleChanges.added.length === 0) &&
- (!bundleChanges.removed || bundleChanges.removed.length === 0) &&
- (!bundleChanges.modified || bundleChanges.modified.length === 0) && (
-
No changes detected between versions.
- )}
+ (!bundleChanges.removed || bundleChanges.removed.length === 0) &&
+ (!bundleChanges.modified || bundleChanges.modified.length === 0) && (
+
No changes detected between versions.
+ )}
setShowChangesModal(false)}>
@@ -1919,5 +2287,5 @@ export function Dashboard() {
)}
- )
+ );
}
diff --git a/synkronus-portal/src/services/api.ts b/synkronus-portal/src/services/api.ts
index d23965ae7..585023686 100644
--- a/synkronus-portal/src/services/api.ts
+++ b/synkronus-portal/src/services/api.ts
@@ -1,78 +1,78 @@
-import type { LoginRequest, LoginResponse, RefreshRequest } from '../types/auth'
+import type { LoginRequest, LoginResponse, RefreshRequest } from '../types/auth';
// Get API base URL from environment or use default
const getApiBaseUrl = () => {
// Check if we're in development (Vite proxy) or production
if (import.meta.env.VITE_API_URL) {
- return import.meta.env.VITE_API_URL
+ return import.meta.env.VITE_API_URL;
}
// Use /api proxy in both development and production
// In development: Vite dev server proxies /api to backend
// In production: Nginx proxies /api to http://demo.synkronus.cloud
- return '/api'
-}
+ return '/api';
+};
-const API_BASE_URL = getApiBaseUrl()
+const API_BASE_URL = getApiBaseUrl();
interface ApiErrorResponse {
- error?: string
- message?: string
+ error?: string;
+ message?: string;
}
-async function request
(
- endpoint: string,
- options: RequestInit = {}
-): Promise {
- const token = localStorage.getItem('token')
-
+async function request(endpoint: string, options: RequestInit = {}): Promise {
+ const token = localStorage.getItem('token');
+
const headers: Record = {
'Content-Type': 'application/json',
- ...(options.headers as Record || {}),
- }
+ ...((options.headers as Record) || {}),
+ };
if (token) {
- headers['Authorization'] = `Bearer ${token}`
+ headers['Authorization'] = `Bearer ${token}`;
}
try {
- const url = `${API_BASE_URL}${endpoint}`
+ const url = `${API_BASE_URL}${endpoint}`;
const response = await fetch(url, {
...options,
headers,
- })
+ });
if (!response.ok) {
- let errorMessage = `HTTP error! status: ${response.status}`
-
+ let errorMessage = `HTTP error! status: ${response.status}`;
+
try {
- const errorData: ApiErrorResponse = await response.json()
+ const errorData: ApiErrorResponse = await response.json();
// API returns { error: "...", message: "..." } format
- errorMessage = errorData.message || errorData.error || errorMessage
+ errorMessage = errorData.message || errorData.error || errorMessage;
} catch {
// If response is not JSON, use status text
- errorMessage = response.statusText || errorMessage
+ errorMessage = response.statusText || errorMessage;
}
-
+
// Provide more specific error messages
if (response.status === 401) {
- errorMessage = errorMessage || 'Invalid username or password'
+ errorMessage = errorMessage || 'Invalid username or password';
} else if (response.status === 0 || response.status >= 500) {
- errorMessage = 'Server error: Please check if the API is running'
+ errorMessage = 'Server error: Please check if the API is running';
} else if (!navigator.onLine) {
- errorMessage = 'No internet connection'
+ errorMessage = 'No internet connection';
}
-
- throw new Error(errorMessage)
+
+ throw new Error(errorMessage);
}
- return response.json()
+ return response.json();
} catch (error) {
if (error instanceof Error) {
// Re-throw API errors as-is
- throw error
+ throw error;
}
// Network errors, CORS errors, etc.
- throw new Error('Network error: Unable to connect to the server. Please check if the API is running at ' + API_BASE_URL)
+ throw new Error(
+ 'Network error: Unable to connect to the server. Please check if the API is running at ' +
+ API_BASE_URL,
+ );
}
}
@@ -81,126 +81,170 @@ export const api = {
return request('/auth/login', {
method: 'POST',
body: JSON.stringify(credentials),
- })
+ });
},
async refreshToken(refreshToken: string): Promise {
- const payload: RefreshRequest = { refreshToken }
+ const payload: RefreshRequest = { refreshToken };
return request('/auth/refresh', {
method: 'POST',
body: JSON.stringify(payload),
- })
+ });
},
async get(endpoint: string): Promise {
- return request(endpoint, { method: 'GET' })
+ return request(endpoint, { method: 'GET' });
},
async post(endpoint: string, data?: unknown): Promise {
return request(endpoint, {
method: 'POST',
body: data ? JSON.stringify(data) : undefined,
- })
+ });
},
async put(endpoint: string, data?: unknown): Promise {
return request(endpoint, {
method: 'PUT',
body: data ? JSON.stringify(data) : undefined,
- })
+ });
},
async delete(endpoint: string): Promise {
- return request(endpoint, { method: 'DELETE' })
+ return request(endpoint, { method: 'DELETE' });
},
// User management endpoints
- async createUser(data: { username: string; password: string; role: string }): Promise<{ username: string; role: string; createdAt: string }> {
+ async createUser(data: {
+ username: string;
+ password: string;
+ role: string;
+ }): Promise<{ username: string; role: string; createdAt: string }> {
// Use /users (not /users/create) to match CLI and demo server
return request<{ username: string; role: string; createdAt: string }>('/users', {
method: 'POST',
body: JSON.stringify(data),
- })
+ });
},
async listUsers(): Promise> {
return request>('/users', {
method: 'GET',
- })
+ });
},
async deleteUser(username: string): Promise<{ message: string }> {
return request<{ message: string }>(`/users/delete/${username}`, {
method: 'DELETE',
- })
+ });
},
- async resetPassword(data: { username: string; newPassword: string }): Promise<{ message: string }> {
+ async resetPassword(data: {
+ username: string;
+ newPassword: string;
+ }): Promise<{ message: string }> {
return request<{ message: string }>('/users/reset-password', {
method: 'POST',
body: JSON.stringify(data),
- })
+ });
},
- async changePassword(data: { currentPassword: string; newPassword: string }): Promise<{ message: string }> {
+ async changePassword(data: {
+ currentPassword: string;
+ newPassword: string;
+ }): Promise<{ message: string }> {
return request<{ message: string }>('/users/change-password', {
method: 'POST',
body: JSON.stringify(data),
- })
+ });
},
// App bundle endpoints
async switchAppBundleVersion(version: string): Promise<{ message: string }> {
return request<{ message: string }>(`/app-bundle/switch/${version}`, {
method: 'POST',
- })
+ });
},
- async getAppBundleManifest(): Promise<{ version: string; files: Array<{ path: string; hash: string; size: number }>; hash: string }> {
- return request<{ version: string; files: Array<{ path: string; hash: string; size: number }>; hash: string }>('/app-bundle/manifest', {
+ async getAppBundleManifest(): Promise<{
+ version: string;
+ files: Array<{ path: string; hash: string; size: number }>;
+ hash: string;
+ }> {
+ return request<{
+ version: string;
+ files: Array<{ path: string; hash: string; size: number }>;
+ hash: string;
+ }>('/app-bundle/manifest', {
method: 'GET',
- })
+ });
},
- async getAppBundleChanges(current?: string, target?: string): Promise {
- const params = new URLSearchParams()
- if (current) params.append('current', current)
- if (target) params.append('target', target)
- const query = params.toString()
- return request(`/app-bundle/changes${query ? `?${query}` : ''}`, {
+ async getAppBundleChanges(
+ current?: string,
+ target?: string,
+ ): Promise<{
+ compare_version_a?: string;
+ compare_version_b?: string;
+ added?: Array<{ path: string; hash: string; size: number }>;
+ removed?: Array<{ path: string; hash: string; size: number }>;
+ modified?: Array<{
+ path: string;
+ hash_a: string;
+ hash_b: string;
+ size_a: number;
+ size_b: number;
+ }>;
+ }> {
+ const params = new URLSearchParams();
+ if (current) params.append('current', current);
+ if (target) params.append('target', target);
+ const query = params.toString();
+ return request<{
+ compare_version_a?: string;
+ compare_version_b?: string;
+ added?: Array<{ path: string; hash: string; size: number }>;
+ removed?: Array<{ path: string; hash: string; size: number }>;
+ modified?: Array<{
+ path: string;
+ hash_a: string;
+ hash_b: string;
+ size_a: number;
+ size_b: number;
+ }>;
+ }>(`/app-bundle/changes${query ? `?${query}` : ''}`, {
method: 'GET',
- })
+ });
},
async downloadAppBundleFile(filePath: string, preview?: boolean): Promise {
- const token = localStorage.getItem('token')
- const params = new URLSearchParams()
- if (preview) params.append('preview', 'true')
- const query = params.toString()
- const url = `${API_BASE_URL}/app-bundle/download/${encodeURIComponent(filePath)}${query ? `?${query}` : ''}`
-
- const headers: Record = {}
+ const token = localStorage.getItem('token');
+ const params = new URLSearchParams();
+ if (preview) params.append('preview', 'true');
+ const query = params.toString();
+ const url = `${API_BASE_URL}/app-bundle/download/${encodeURIComponent(filePath)}${query ? `?${query}` : ''}`;
+
+ const headers: Record = {};
if (token) {
- headers['Authorization'] = `Bearer ${token}`
+ headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(url, {
method: 'GET',
headers,
- })
+ });
if (!response.ok) {
- let errorMessage = `HTTP error! status: ${response.status}`
+ let errorMessage = `HTTP error! status: ${response.status}`;
try {
- const errorData: ApiErrorResponse = await response.json()
- errorMessage = errorData.message || errorData.error || errorMessage
+ const errorData: ApiErrorResponse = await response.json();
+ errorMessage = errorData.message || errorData.error || errorMessage;
} catch {
- errorMessage = response.statusText || errorMessage
+ errorMessage = response.statusText || errorMessage;
}
- throw new Error(errorMessage)
+ throw new Error(errorMessage);
}
- return response.blob()
+ return response.blob();
},
-}
-
+};
diff --git a/synkronus-portal/src/types/auth.ts b/synkronus-portal/src/types/auth.ts
index 453877c92..e77ec7831 100644
--- a/synkronus-portal/src/types/auth.ts
+++ b/synkronus-portal/src/types/auth.ts
@@ -1,27 +1,26 @@
export interface LoginRequest {
- username: string
- password: string
+ username: string;
+ password: string;
}
export interface LoginResponse {
- token: string
- refreshToken: string
- expiresAt: number
+ token: string;
+ refreshToken: string;
+ expiresAt: number;
}
export interface RefreshRequest {
- refreshToken: string
+ refreshToken: string;
}
export interface User {
- username: string
- role: string
+ username: string;
+ role: string;
}
export interface AuthState {
- user: User | null
- token: string | null
- refreshToken: string | null
- isAuthenticated: boolean
+ user: User | null;
+ token: string | null;
+ refreshToken: string | null;
+ isAuthenticated: boolean;
}
-
diff --git a/synkronus-portal/vite.config.ts b/synkronus-portal/vite.config.ts
index 1e2b4e786..f87a7fc26 100644
--- a/synkronus-portal/vite.config.ts
+++ b/synkronus-portal/vite.config.ts
@@ -52,18 +52,18 @@ export default defineConfig({
console.log(`[Vite Proxy] Rewriting ${path} -> ${rewritten}`)
return rewritten
},
- configure: (proxy, _options) => {
- proxy.on('error', (err, req, _res) => {
+ configure: (proxy) => {
+ proxy.on('error', (err, req) => {
console.error('[Vite Proxy] Error:', err)
console.error('[Vite Proxy] Request URL:', req.url)
console.error('[Vite Proxy] Target:', process.env.DOCKER_ENV === 'true' || process.env.VITE_API_URL?.includes('synkronus:')
? 'http://synkronus:8080'
: process.env.API_URL || 'http://localhost:8080')
})
- proxy.on('proxyReq', (proxyReq, req, _res) => {
+ proxy.on('proxyReq', (proxyReq, req) => {
console.log('[Vite Proxy] Proxying request:', req.method, req.url, '->', proxyReq.path)
})
- proxy.on('proxyRes', (proxyRes, req, _res) => {
+ proxy.on('proxyRes', (proxyRes, req) => {
console.log('[Vite Proxy] Response:', proxyRes.statusCode, req.url)
})
},
diff --git a/synkronus/.golangci.yml b/synkronus/.golangci.yml
index 5e265449f..cd817ac70 100644
--- a/synkronus/.golangci.yml
+++ b/synkronus/.golangci.yml
@@ -1,12 +1,11 @@
+version: "2"
+
linters:
disable-all: true
enable:
- staticcheck # Best static analysis tool
- govet # Catches common Go mistakes
- errcheck # Ensures errors are handled
- - typecheck # Type checking
- - goimports # Formatting and imports
- - gosimple # Finds code that can be simplified
- unused # Finds unused code
- gocritic # Opinionated code analysis
@@ -15,4 +14,3 @@ issues:
- path: _test\.go$ # More relaxed rules for tests
linters:
- gocritic
- - gosimple
\ No newline at end of file
diff --git a/synkronus/cmd/synkronus/main.go b/synkronus/cmd/synkronus/main.go
index 303269321..213f631cf 100644
--- a/synkronus/cmd/synkronus/main.go
+++ b/synkronus/cmd/synkronus/main.go
@@ -92,7 +92,7 @@ func main() {
log.Info("Exiting due to database initialization error")
return
}
- defer db.Close()
+ defer func() { _ = db.Close() }()
// Run database migrations
log.Info("Starting database migrations...")
diff --git a/synkronus/internal/api/api_integration_test.go b/synkronus/internal/api/api_integration_test.go
index ae81c0814..80ffc286b 100644
--- a/synkronus/internal/api/api_integration_test.go
+++ b/synkronus/internal/api/api_integration_test.go
@@ -358,15 +358,18 @@ func TestProtectedEndpoints(t *testing.T) {
req.Header.Set("Content-Type", "application/json")
if tc.withAuth {
// Determine which token to use based on test case
- tokenToUse := validToken
- if tc.invalidToken {
+ var tokenToUse string
+ switch {
+ case tc.invalidToken:
tokenToUse = invalidToken
- } else if tc.expiredToken {
+ case tc.expiredToken:
tokenToUse = expiredToken
- } else if tc.readOnlyUser {
+ case tc.readOnlyUser:
tokenToUse = readOnlyToken
- } else if tc.adminUser {
+ case tc.adminUser:
tokenToUse = adminToken
+ default:
+ tokenToUse = validToken
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tokenToUse))
}
@@ -376,7 +379,7 @@ func TestProtectedEndpoints(t *testing.T) {
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
if resp.StatusCode != tc.expectedStatus {
diff --git a/synkronus/internal/api/api_test.go b/synkronus/internal/api/api_test.go
index 2adf14c7d..ed9b563f4 100644
--- a/synkronus/internal/api/api_test.go
+++ b/synkronus/internal/api/api_test.go
@@ -61,7 +61,7 @@ func TestNewRouter(t *testing.T) {
if err != nil {
t.Fatalf("Failed to make request: %v", err)
}
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
if resp.StatusCode != http.StatusOK {
diff --git a/synkronus/internal/handlers/appbundle.go b/synkronus/internal/handlers/appbundle.go
index 1657a9af8..bff11647f 100644
--- a/synkronus/internal/handlers/appbundle.go
+++ b/synkronus/internal/handlers/appbundle.go
@@ -88,7 +88,7 @@ func (h *Handler) GetAppBundleFile(w http.ResponseWriter, r *http.Request) {
}
return
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
// Set the appropriate headers
etag := fmt.Sprintf("\"%s\"", fileInfo.Hash)
@@ -112,7 +112,7 @@ func (h *Handler) GetAppBundleFile(w http.ResponseWriter, r *http.Request) {
}
func (h *Handler) streamFile(w http.ResponseWriter, file io.ReadCloser, fileInfo *appbundle.File) {
- defer file.Close()
+ defer func() { _ = file.Close() }()
// Set content type and headers
w.Header().Set("Content-Type", fileInfo.MimeType)
diff --git a/synkronus/internal/handlers/appbundle_push.go b/synkronus/internal/handlers/appbundle_push.go
index 23e98f09f..8e6e66c6a 100644
--- a/synkronus/internal/handlers/appbundle_push.go
+++ b/synkronus/internal/handlers/appbundle_push.go
@@ -36,7 +36,7 @@ func (h *Handler) PushAppBundle(w http.ResponseWriter, r *http.Request) {
SendErrorResponse(w, http.StatusBadRequest, err, "Failed to get bundle file from form")
return
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
// Log the upload
h.log.Info("Processing app bundle upload", "filename", header.Filename, "size", header.Size, "user", user.Username)
diff --git a/synkronus/internal/handlers/appbundle_push_test.go b/synkronus/internal/handlers/appbundle_push_test.go
index 17538b427..975c1ee30 100644
--- a/synkronus/internal/handlers/appbundle_push_test.go
+++ b/synkronus/internal/handlers/appbundle_push_test.go
@@ -70,7 +70,7 @@ func TestPushAppBundle(t *testing.T) {
if err != nil {
return nil, err
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
// Copy the file content to the form
_, err = io.Copy(part, file)
diff --git a/synkronus/internal/handlers/appbundle_test.go b/synkronus/internal/handlers/appbundle_test.go
index c2b32ce9a..3762fd40b 100644
--- a/synkronus/internal/handlers/appbundle_test.go
+++ b/synkronus/internal/handlers/appbundle_test.go
@@ -25,7 +25,7 @@ func TestGetAppBundleManifest(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
assert.Equal(t, http.StatusOK, resp.StatusCode, "Expected status code %d, got %d", http.StatusOK, resp.StatusCode)
@@ -107,7 +107,7 @@ func TestGetAppBundleFile(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
assert.Equal(t, tc.expectedStatus, resp.StatusCode, "Expected status code %d, got %d", tc.expectedStatus, resp.StatusCode)
@@ -148,7 +148,7 @@ func TestGetAppBundleFileWithPreview(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Should return 200 OK
assert.Equal(t, http.StatusOK, resp.StatusCode, "Expected status code %d, got %d", http.StatusOK, resp.StatusCode)
@@ -175,7 +175,7 @@ func TestGetAppBundleFileWithPreview(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Should return 200 OK
assert.Equal(t, http.StatusOK, resp.StatusCode, "Expected status code %d, got %d", http.StatusOK, resp.StatusCode)
@@ -202,7 +202,7 @@ func TestGetAppBundleFileWithPreview(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Should still return 200 OK
assert.Equal(t, http.StatusOK, resp.StatusCode, "Expected status code %d, got %d", http.StatusOK, resp.StatusCode)
@@ -227,7 +227,7 @@ func TestGetAppBundleFileNotModified(t *testing.T) {
// Serve the first request
r.ServeHTTP(w1, req1)
resp1 := w1.Result()
- defer resp1.Body.Close()
+ defer func() { _ = resp1.Body.Close() }()
// Get the ETag from the first response
etag := resp1.Header.Get("etag")
@@ -241,7 +241,7 @@ func TestGetAppBundleFileNotModified(t *testing.T) {
// Serve the second request
r.ServeHTTP(w2, req2)
resp2 := w2.Result()
- defer resp2.Body.Close()
+ defer func() { _ = resp2.Body.Close() }()
// Check that the second response has a 304 Not Modified status
assert.Equal(t, http.StatusNotModified, resp2.StatusCode, "Expected status code %d, got %d", http.StatusNotModified, resp2.StatusCode)
@@ -258,7 +258,7 @@ func TestGetAppBundleManifestNotModified(t *testing.T) {
// Get the ETag from the first response
resp1 := w1.Result()
- defer resp1.Body.Close()
+ defer func() { _ = resp1.Body.Close() }()
etag := resp1.Header.Get("etag")
// Second request with If-None-Match header
@@ -269,7 +269,7 @@ func TestGetAppBundleManifestNotModified(t *testing.T) {
// Check response
resp2 := w2.Result()
- defer resp2.Body.Close()
+ defer func() { _ = resp2.Body.Close() }()
// Should return 304 Not Modified
assert.Equal(t, http.StatusNotModified, resp2.StatusCode, "Expected status code %d, got %d", http.StatusNotModified, resp2.StatusCode)
@@ -327,7 +327,7 @@ func TestCompareAppBundleVersions(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
assert.Equal(t, tc.expectedCode, resp.StatusCode)
diff --git a/synkronus/internal/handlers/attachment.go b/synkronus/internal/handlers/attachment.go
index de88303dc..7990fea3d 100644
--- a/synkronus/internal/handlers/attachment.go
+++ b/synkronus/internal/handlers/attachment.go
@@ -64,7 +64,7 @@ func (h *AttachmentHandler) UploadAttachment(w http.ResponseWriter, r *http.Requ
SendErrorResponse(w, http.StatusBadRequest, err, "Failed to get file from form data")
return
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
// Save the attachment
err = h.service.Save(r.Context(), attachmentID, file)
@@ -109,7 +109,7 @@ func (h *AttachmentHandler) DownloadAttachment(w http.ResponseWriter, r *http.Re
SendErrorResponse(w, http.StatusInternalServerError, err, "Failed to get attachment")
return
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
// Set headers for file download
w.Header().Set("Content-Type", "application/octet-stream")
diff --git a/synkronus/internal/handlers/attachment_test.go b/synkronus/internal/handlers/attachment_test.go
index d674d6fb0..06bbeb506 100644
--- a/synkronus/internal/handlers/attachment_test.go
+++ b/synkronus/internal/handlers/attachment_test.go
@@ -82,8 +82,8 @@ func TestAttachmentHandler_UploadAttachment(t *testing.T) {
var b bytes.Buffer
w := multipart.NewWriter(&b)
part, _ := w.CreateFormFile("file", "test.txt")
- part.Write([]byte("test content"))
- w.Close()
+ _, _ = part.Write([]byte("test content"))
+ _ = w.Close()
// Create request
req := httptest.NewRequest("PUT", "/attachments/"+tc.attachmentID, &b)
diff --git a/synkronus/internal/handlers/auth_test.go b/synkronus/internal/handlers/auth_test.go
index aead8f990..67e0e3b55 100644
--- a/synkronus/internal/handlers/auth_test.go
+++ b/synkronus/internal/handlers/auth_test.go
@@ -57,7 +57,7 @@ func TestLogin(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
if resp.StatusCode != tc.expectedStatus {
@@ -133,7 +133,7 @@ func TestRefreshToken(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
if resp.StatusCode != tc.expectedStatus {
diff --git a/synkronus/internal/handlers/dataexport.go b/synkronus/internal/handlers/dataexport.go
index 42c921b37..8518be6d2 100644
--- a/synkronus/internal/handlers/dataexport.go
+++ b/synkronus/internal/handlers/dataexport.go
@@ -23,7 +23,7 @@ func (h *Handler) ParquetExportHandler(w http.ResponseWriter, r *http.Request) {
SendErrorResponse(w, http.StatusInternalServerError, err, "Failed to export parquet data")
return
}
- defer zipReader.Close()
+ defer func() { _ = zipReader.Close() }()
// Set headers for ZIP file download
w.Header().Set("Content-Type", "application/zip")
diff --git a/synkronus/internal/handlers/health_test.go b/synkronus/internal/handlers/health_test.go
index 0c75923ea..a87309276 100644
--- a/synkronus/internal/handlers/health_test.go
+++ b/synkronus/internal/handlers/health_test.go
@@ -22,7 +22,7 @@ func TestHealthCheck(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
assert.Equal(t, http.StatusOK, resp.StatusCode, "Expected status code %d, got %d", http.StatusOK, resp.StatusCode)
diff --git a/synkronus/internal/handlers/mocks/sync_service.go b/synkronus/internal/handlers/mocks/sync_service.go
index 9b721cbd9..97b53fd78 100644
--- a/synkronus/internal/handlers/mocks/sync_service.go
+++ b/synkronus/internal/handlers/mocks/sync_service.go
@@ -75,7 +75,7 @@ func (m *MockSyncService) GetRecordsSinceVersion(ctx context.Context, sinceVersi
}
// Determine change cutoff
- var changeCutoff int64 = sinceVersion
+ var changeCutoff = sinceVersion
if len(filteredRecords) > 0 {
changeCutoff = filteredRecords[len(filteredRecords)-1].Version
}
diff --git a/synkronus/internal/handlers/sync_e2e_test.go b/synkronus/internal/handlers/sync_e2e_test.go
index f7c65af4b..ab8216651 100644
--- a/synkronus/internal/handlers/sync_e2e_test.go
+++ b/synkronus/internal/handlers/sync_e2e_test.go
@@ -57,7 +57,7 @@ func TestSyncE2E_VersionIncrement(t *testing.T) {
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
@@ -133,7 +133,7 @@ func TestSyncE2E_PartialFailure(t *testing.T) {
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
@@ -198,7 +198,7 @@ func TestSyncE2E_SchemaTypeFiltering(t *testing.T) {
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
@@ -229,7 +229,7 @@ func TestSyncE2E_SchemaTypeFiltering(t *testing.T) {
if err != nil {
t.Fatalf("Failed to send request: %v", err)
}
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
diff --git a/synkronus/internal/handlers/sync_test.go b/synkronus/internal/handlers/sync_test.go
index 9dfd858e8..d8d7237b0 100644
--- a/synkronus/internal/handlers/sync_test.go
+++ b/synkronus/internal/handlers/sync_test.go
@@ -72,7 +72,7 @@ func TestPull(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
if resp.StatusCode != tc.expectedStatus {
@@ -228,7 +228,7 @@ func TestPush(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
if resp.StatusCode != tc.expectedStatus {
diff --git a/synkronus/internal/handlers/versions_test.go b/synkronus/internal/handlers/versions_test.go
index 5dc904fbe..8681db305 100644
--- a/synkronus/internal/handlers/versions_test.go
+++ b/synkronus/internal/handlers/versions_test.go
@@ -20,7 +20,7 @@ func TestGetAPIVersions(t *testing.T) {
// Check response
resp := w.Result()
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
// Check status code
if resp.StatusCode != http.StatusOK {
diff --git a/synkronus/internal/repository/user_repository.go b/synkronus/internal/repository/user_repository.go
index 2a8a4e274..944466179 100644
--- a/synkronus/internal/repository/user_repository.go
+++ b/synkronus/internal/repository/user_repository.go
@@ -66,7 +66,7 @@ func (r *UserRepository) List(ctx context.Context) ([]models.User, error) {
if err != nil {
return nil, fmt.Errorf("failed to list users: %w", err)
}
- defer rows.Close()
+ defer func() { _ = rows.Close() }()
var users []models.User
for rows.Next() {
var user models.User
diff --git a/synkronus/pkg/appbundle/appinfo.go b/synkronus/pkg/appbundle/appinfo.go
index 8d82fa3a5..f363c783d 100644
--- a/synkronus/pkg/appbundle/appinfo.go
+++ b/synkronus/pkg/appbundle/appinfo.go
@@ -247,7 +247,7 @@ func readZipFile(file *zip.File) ([]byte, error) {
if err != nil {
return nil, err
}
- defer f.Close()
+ defer func() { _ = f.Close() }()
return io.ReadAll(f)
}
diff --git a/synkronus/pkg/appbundle/appinfo_test.go b/synkronus/pkg/appbundle/appinfo_test.go
index b667547b8..aa438fbad 100644
--- a/synkronus/pkg/appbundle/appinfo_test.go
+++ b/synkronus/pkg/appbundle/appinfo_test.go
@@ -238,7 +238,7 @@ func TestBundleChanges_FieldAddition(t *testing.T) {
bundle01Path := filepath.Join("..", "..", "testdata", "bundles", "valid_bundle01.zip")
bundle01File, err := os.Open(bundle01Path)
require.NoError(t, err, "Failed to open bundle01")
- defer bundle01File.Close()
+ defer func() { _ = bundle01File.Close() }()
_, err = service.PushBundle(context.Background(), bundle01File)
require.NoError(t, err, "Failed to push initial bundle")
@@ -271,7 +271,7 @@ func TestBundleChanges_FieldAddition(t *testing.T) {
bundle02Path := filepath.Join("..", "..", "testdata", "bundles", "valid_bundle02.zip")
bundle02File, err := os.Open(bundle02Path)
require.NoError(t, err, "Failed to open bundle02")
- defer bundle02File.Close()
+ defer func() { _ = bundle02File.Close() }()
_, err = service.PushBundle(context.Background(), bundle02File)
require.NoError(t, err, "Failed to push second bundle")
@@ -317,7 +317,7 @@ func TestBundleChanges_FieldAddition(t *testing.T) {
file, _, err := service.GetFile(context.Background(), "app/THIS_IS_VERSION_2.txt")
require.NoError(t, err, "Failed to download file")
fileContent, err := io.ReadAll(file)
- defer file.Close()
+ defer func() { _ = file.Close() }()
require.NoError(t, err, "Failed to read file content")
require.Equal(t, "Something...", string(fileContent))
@@ -408,7 +408,7 @@ func TestPushBundleGeneratesAppInfo(t *testing.T) {
// Open the test bundle
bundleFile, err := os.Open(tc.bundlePath)
require.NoError(t, err, "Failed to open test bundle")
- defer bundleFile.Close()
+ defer func() { _ = bundleFile.Close() }()
// Push the bundle
_, err = service.PushBundle(context.Background(), bundleFile)
diff --git a/synkronus/pkg/appbundle/bundle_test.go b/synkronus/pkg/appbundle/bundle_test.go
index bbf804ff9..4b41400d2 100644
--- a/synkronus/pkg/appbundle/bundle_test.go
+++ b/synkronus/pkg/appbundle/bundle_test.go
@@ -23,7 +23,7 @@ func TestBundleWithTestData(t *testing.T) {
// Open the zip file
zipFile, err := zip.OpenReader(bundlePath)
require.NoError(t, err, "Failed to open test bundle")
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
service := &Service{
bundlePath: t.TempDir(),
@@ -58,7 +58,7 @@ func TestBundleWithoutRenderers(t *testing.T) {
// Open the zip file
zipFile, err := zip.OpenReader(bundlePath)
require.NoError(t, err, "Failed to open test bundle")
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
t.Run("ValidateBundleStructure", func(t *testing.T) {
err := service.validateBundleStructure(&zipFile.Reader)
@@ -108,7 +108,7 @@ func TestInvalidBundles(t *testing.T) {
zipFile, err := zip.OpenReader(bundlePath)
require.NoError(t, err, "Failed to open test bundle")
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
err = service.validateBundleStructure(&zipFile.Reader)
if tc.expectedError != nil {
@@ -142,9 +142,9 @@ func createTestFormBundle(t *testing.T, forms map[string]map[string]any) (string
// Add required directories
for _, dir := range []string{"app/", "forms/"} {
if _, err := w.Create(dir); err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create directory %s: %w", dir, err)
}
}
@@ -152,16 +152,16 @@ func createTestFormBundle(t *testing.T, forms map[string]map[string]any) (string
// Add app/index.html
fw, err := w.Create("app/index.html")
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create app/index.html: %w", err)
}
_, err = fw.Write([]byte("Test App"))
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to write app/index.html: %w", err)
}
@@ -170,34 +170,34 @@ func createTestFormBundle(t *testing.T, forms map[string]map[string]any) (string
// Create form directory
formDir := fmt.Sprintf("forms/%s/", formName)
if _, err := w.Create(formDir); err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create form directory: %w", err)
}
// Add schema.json
schemaData, err := json.Marshal(schema)
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to marshal schema: %w", err)
}
schemaPath := fmt.Sprintf("forms/%s/schema.json", formName)
fw, err := w.Create(schemaPath)
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create schema.json: %w", err)
}
_, err = fw.Write(schemaData)
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to write schema.json: %w", err)
}
@@ -205,28 +205,28 @@ func createTestFormBundle(t *testing.T, forms map[string]map[string]any) (string
uiPath := fmt.Sprintf("forms/%s/ui.json", formName)
fw, err = w.Create(uiPath)
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create ui.json: %w", err)
}
_, err = fw.Write([]byte(`{"ui:order":[]}`))
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to write ui.json: %w", err)
}
}
if err := w.Close(); err != nil {
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to close zip writer: %w", err)
}
if err := tmpFile.Close(); err != nil {
- os.Remove(tmpFile.Name())
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to close temp file: %w", err)
}
@@ -255,7 +255,7 @@ func TestFormVersionChanges(t *testing.T) {
// Process the first version
zip1, err := zip.OpenReader(bundle1)
require.NoError(t, err)
- defer zip1.Close()
+ defer func() { _ = zip1.Close() }()
// Validate first version
err = service.validateBundleStructure(&zip1.Reader)
@@ -279,7 +279,7 @@ func TestFormVersionChanges(t *testing.T) {
// Process the second version
zip2, err := zip.OpenReader(bundle2)
require.NoError(t, err)
- defer zip2.Close()
+ defer func() { _ = zip2.Close() }()
t.Run("ValidateFieldAddition", func(t *testing.T) {
err = service.validateBundleStructure(&zip2.Reader)
@@ -302,7 +302,7 @@ func TestFormVersionChanges(t *testing.T) {
// Process the third version
zip3, err := zip.OpenReader(bundle3)
require.NoError(t, err)
- defer zip3.Close()
+ defer func() { _ = zip3.Close() }()
t.Run("ValidateFieldRemoval", func(t *testing.T) {
err = service.validateBundleStructure(&zip3.Reader)
@@ -329,7 +329,7 @@ func TestFormVersionChanges(t *testing.T) {
// Process the fourth version
zip4, err := zip.OpenReader(bundle4)
require.NoError(t, err)
- defer zip4.Close()
+ defer func() { _ = zip4.Close() }()
t.Run("ValidateNewForm", func(t *testing.T) {
err = service.validateBundleStructure(&zip4.Reader)
@@ -368,7 +368,7 @@ func TestMissingRendererReferences(t *testing.T) {
zipFile, err := zip.OpenReader(bundle)
require.NoError(t, err)
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
err = service.validateFormRendererReferences(&zipFile.Reader)
require.NoError(t, err, "Built-in renderer reference should be valid")
@@ -384,7 +384,7 @@ func TestMissingRendererReferences(t *testing.T) {
zipFile, err := zip.OpenReader(bundle)
require.NoError(t, err)
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
err = service.validateFormRendererReferences(&zipFile.Reader)
require.Error(t, err, "Should fail with missing renderer reference")
@@ -395,7 +395,7 @@ func TestMissingRendererReferences(t *testing.T) {
// Create a temporary file for the zip
tmpFile, err := os.CreateTemp("", "test-bundle-*.zip")
require.NoError(t, err, "Failed to create temp file")
- defer os.Remove(tmpFile.Name())
+ defer func() { _ = os.Remove(tmpFile.Name()) }()
w := zip.NewWriter(tmpFile)
@@ -439,7 +439,7 @@ func TestMissingRendererReferences(t *testing.T) {
// Reopen for reading
zipFile, err := zip.OpenReader(tmpFile.Name())
require.NoError(t, err, "Failed to open test bundle")
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
err = service.validateFormRendererReferences(&zipFile.Reader)
require.NoError(t, err, "Valid custom renderer reference should pass validation")
@@ -479,7 +479,7 @@ func TestCoreFieldsValidation(t *testing.T) {
// Process the first version to extract and store core field hashes
zip1, err := zip.OpenReader(bundle1)
require.NoError(t, err)
- defer zip1.Close()
+ defer func() { _ = zip1.Close() }()
// Generate app info to store core field hashes
_, err = service.generateAppInfo(&zip1.Reader, "1.0.0")
@@ -508,7 +508,7 @@ func TestCoreFieldsValidation(t *testing.T) {
// Process the second version
zip2, err := zip.OpenReader(bundle2)
require.NoError(t, err)
- defer zip2.Close()
+ defer func() { _ = zip2.Close() }()
err = service.validateBundleStructure(&zip2.Reader)
require.Error(t, err, "Should fail when modifying core field type")
@@ -539,7 +539,7 @@ func TestCoreFieldsValidation(t *testing.T) {
zipFile, err := zip.OpenReader(bundle)
require.NoError(t, err)
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
err = service.validateBundleStructure(&zipFile.Reader)
require.NoError(t, err, "Non-core form validation should pass")
diff --git a/synkronus/pkg/appbundle/service.go b/synkronus/pkg/appbundle/service.go
index d0c1f9d85..3a80d8220 100644
--- a/synkronus/pkg/appbundle/service.go
+++ b/synkronus/pkg/appbundle/service.go
@@ -153,7 +153,7 @@ func (s *Service) GetFile(ctx context.Context, path string) (io.ReadCloser, *Fil
// Get the file hash
hash, err := s.hashFile(fullPath)
if err != nil {
- file.Close()
+ _ = file.Close()
return nil, nil, fmt.Errorf("failed to hash file: %w", err)
}
@@ -211,7 +211,7 @@ func (s *Service) GetLatestVersionFile(ctx context.Context, path string) (io.Rea
// Get file hash
hash, err := s.hashFile(latestPath)
if err != nil {
- file.Close()
+ _ = file.Close()
return nil, nil, fmt.Errorf("failed to hash file: %w", err)
}
@@ -346,7 +346,7 @@ func (s *Service) hashFile(path string) (string, error) {
if err != nil {
return "", fmt.Errorf("failed to open file for hashing: %w", err)
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
diff --git a/synkronus/pkg/appbundle/test_helpers.go b/synkronus/pkg/appbundle/test_helpers.go
index c2cf3e3f9..0acb66a01 100644
--- a/synkronus/pkg/appbundle/test_helpers.go
+++ b/synkronus/pkg/appbundle/test_helpers.go
@@ -18,7 +18,7 @@ func createTestBundleFromDir(t *testing.T, srcDir string) (string, error) {
if err != nil {
t.Fatalf("Failed to create temp file: %v", err)
}
- defer tmpFile.Close()
+ defer func() { _ = tmpFile.Close() }()
// Create a new zip writer
w := zip.NewWriter(tmpFile)
@@ -152,15 +152,15 @@ func createTestBundle(t *testing.T, includeApp, includeForms, includeCells bool)
// Add app/index.html
fw, err := w.Create("app/index.html")
if err != nil {
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create app/index.html: %w", err)
}
_, err = fw.Write([]byte("Test App"))
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to write app/index.html: %w", err)
}
@@ -176,9 +176,9 @@ func createTestBundle(t *testing.T, includeApp, includeForms, includeCells bool)
for _, dir := range dirs {
if _, err := w.Create(dir); err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create directory %s: %w", dir, err)
}
}
@@ -187,31 +187,31 @@ func createTestBundle(t *testing.T, includeApp, includeForms, includeCells bool)
if includeForms {
fw, err := w.Create("forms/sample/schema.json")
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create sample form: %w", err)
}
_, err = fw.Write([]byte(`{"type":"object","properties":{"name":{"type":"string"}}}`))
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to write sample form: %w", err)
}
fw, err = w.Create("forms/sample/ui.json")
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create sample UI: %w", err)
}
_, err = fw.Write([]byte(`{"ui:order":["name"]}`))
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to write sample UI: %w", err)
}
}
@@ -220,16 +220,16 @@ func createTestBundle(t *testing.T, includeApp, includeForms, includeCells bool)
if includeCells {
fw, err := w.Create("renderers/sample/renderer.jsx")
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to create sample renderer: %w", err)
}
_, err = fw.Write([]byte("export default function SampleRenderer() { return null; }"))
if err != nil {
- w.Close()
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = w.Close()
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to write sample renderer: %w", err)
}
}
@@ -237,14 +237,14 @@ func createTestBundle(t *testing.T, includeApp, includeForms, includeCells bool)
// Close the zip writer
if err := w.Close(); err != nil {
- tmpFile.Close()
- os.Remove(tmpFile.Name())
+ _ = tmpFile.Close()
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to close zip writer: %w", err)
}
// Close the file
if err := tmpFile.Close(); err != nil {
- os.Remove(tmpFile.Name())
+ _ = os.Remove(tmpFile.Name())
return "", fmt.Errorf("failed to close temp file: %w", err)
}
diff --git a/synkronus/pkg/appbundle/validation.go b/synkronus/pkg/appbundle/validation.go
index 73b626744..e4dd04a0d 100644
--- a/synkronus/pkg/appbundle/validation.go
+++ b/synkronus/pkg/appbundle/validation.go
@@ -128,7 +128,7 @@ func (s *Service) validateFormSchema(file *zip.File) error {
if err != nil {
return fmt.Errorf("failed to open form schema: %w", err)
}
- defer f.Close()
+ defer func() { _ = f.Close() }()
// Parse the schema
var schema map[string]any
@@ -217,7 +217,9 @@ func (s *Service) validateFormRendererReferences(zipReader *zip.Reader) error {
// Parse the schema
var schema map[string]any
err = json.NewDecoder(f).Decode(&schema)
- f.Close() // Close the file immediately after reading
+ if closeErr := f.Close(); closeErr != nil {
+ return fmt.Errorf("failed to close file: %w", closeErr)
+ }
if err != nil {
return fmt.Errorf("failed to parse form schema: %w", err)
}
diff --git a/synkronus/pkg/appbundle/validation_test.go b/synkronus/pkg/appbundle/validation_test.go
index ca9cabf10..dc25fa7b4 100644
--- a/synkronus/pkg/appbundle/validation_test.go
+++ b/synkronus/pkg/appbundle/validation_test.go
@@ -93,15 +93,15 @@ func TestValidateBundleStructure(t *testing.T) {
tempFile, err := os.CreateTemp("", "test-bundle-*.zip")
require.NoError(t, err, "failed to create temp file")
- defer os.Remove(tempFile.Name())
+ defer func() { _ = os.Remove(tempFile.Name()) }()
_, err = tempFile.Write(zipData.Bytes())
require.NoError(t, err, "failed to write zip data")
- tempFile.Close()
+ _ = tempFile.Close()
zipFile, err := zip.OpenReader(tempFile.Name())
require.NoError(t, err, "failed to open zip file")
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
service := &Service{
bundlePath: filepath.Join(t.TempDir(), "bundle"),
@@ -181,15 +181,15 @@ func TestValidateFormRendererReferences(t *testing.T) {
tempFile, err := os.CreateTemp("", "test-bundle-*.zip")
require.NoError(t, err, "failed to create temp file")
- defer os.Remove(tempFile.Name())
+ defer func() { _ = os.Remove(tempFile.Name()) }()
_, err = tempFile.Write(zipData.Bytes())
require.NoError(t, err, "failed to write zip data")
- tempFile.Close()
+ _ = tempFile.Close()
zipFile, err := zip.OpenReader(tempFile.Name())
require.NoError(t, err, "failed to open zip file")
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
service := &Service{
bundlePath: filepath.Join(t.TempDir(), "bundle"),
@@ -641,16 +641,16 @@ func TestValidateFormSchema(t *testing.T) {
// Create a temporary file for the zip
tempFile, err := os.CreateTemp("", "test-bundle-*.zip")
require.NoError(t, err, "failed to create temp file")
- defer os.Remove(tempFile.Name())
+ defer func() { _ = os.Remove(tempFile.Name()) }()
_, err = tempFile.Write(zipData.Bytes())
require.NoError(t, err, "failed to write zip data")
- tempFile.Close()
+ _ = tempFile.Close()
// Open the zip file
zipFile, err := zip.OpenReader(tempFile.Name())
require.NoError(t, err, "failed to open zip file")
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
// Create a test service
service := &Service{
diff --git a/synkronus/pkg/appbundle/versioning.go b/synkronus/pkg/appbundle/versioning.go
index e26127fb8..a01ecb6b9 100644
--- a/synkronus/pkg/appbundle/versioning.go
+++ b/synkronus/pkg/appbundle/versioning.go
@@ -21,8 +21,8 @@ func (s *Service) PushBundle(ctx context.Context, zipReader io.Reader) (*Manifes
if err != nil {
return nil, fmt.Errorf("failed to create temporary file: %w", err)
}
- defer os.Remove(tempZipFile.Name())
- defer tempZipFile.Close()
+ defer func() { _ = os.Remove(tempZipFile.Name()) }()
+ defer func() { _ = tempZipFile.Close() }()
// Copy the zip content to the temporary file
if _, err := io.Copy(tempZipFile, zipReader); err != nil {
@@ -39,7 +39,7 @@ func (s *Service) PushBundle(ctx context.Context, zipReader io.Reader) (*Manifes
if err != nil {
return nil, fmt.Errorf("failed to open zip file: %w", err)
}
- defer zipFile.Close()
+ defer func() { _ = zipFile.Close() }()
// Validate the bundle structure
if err := s.validateBundleStructure(&zipFile.Reader); err != nil {
@@ -107,20 +107,25 @@ func (s *Service) PushBundle(ctx context.Context, zipReader io.Reader) (*Manifes
// Create the target file
dstFile, err := os.Create(targetPath)
if err != nil {
- srcFile.Close()
+ _ = srcFile.Close()
return nil, fmt.Errorf("failed to create file %s: %w", cleanPath, err)
}
// Copy the content
if _, err := io.Copy(dstFile, srcFile); err != nil {
- srcFile.Close()
- dstFile.Close()
+ _ = srcFile.Close()
+ _ = dstFile.Close()
return nil, fmt.Errorf("failed to copy file %s: %w", cleanPath, err)
}
// Close the files
- srcFile.Close()
- dstFile.Close()
+ if err := srcFile.Close(); err != nil {
+ _ = dstFile.Close()
+ return nil, fmt.Errorf("failed to close source file: %w", err)
+ }
+ if err := dstFile.Close(); err != nil {
+ return nil, fmt.Errorf("failed to close destination file: %w", err)
+ }
}
// Clean up old versions if needed
@@ -249,7 +254,7 @@ func (s *Service) SwitchVersion(ctx context.Context, version string) error {
// Atomic rename (works across all platforms)
if err := os.Rename(tempFile, versionFile); err != nil {
// Clean up temp file if rename fails
- os.Remove(tempFile)
+ _ = os.Remove(tempFile)
return fmt.Errorf("failed to update current version: %w", err)
}
@@ -325,14 +330,14 @@ func (s *Service) copyFile(src, dst string, mode os.FileMode) error {
if err != nil {
return fmt.Errorf("failed to open source file: %w", err)
}
- defer srcFile.Close()
+ defer func() { _ = srcFile.Close() }()
// Create the destination file
dstFile, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
if err != nil {
return fmt.Errorf("failed to create destination file: %w", err)
}
- defer dstFile.Close()
+ defer func() { _ = dstFile.Close() }()
// Copy the content
if _, err := io.Copy(dstFile, srcFile); err != nil {
diff --git a/synkronus/pkg/attachment/manifest.go b/synkronus/pkg/attachment/manifest.go
index 534a73431..3ef2dddc1 100644
--- a/synkronus/pkg/attachment/manifest.go
+++ b/synkronus/pkg/attachment/manifest.go
@@ -136,7 +136,7 @@ func (s *manifestService) GetManifest(ctx context.Context, req AttachmentManifes
if err != nil {
return nil, fmt.Errorf("failed to query attachment operations: %w", err)
}
- defer rows.Close()
+ defer func() { _ = rows.Close() }()
var operations []AttachmentOperation
var totalDownloadSize int64
@@ -169,7 +169,8 @@ func (s *manifestService) GetManifest(ctx context.Context, req AttachmentManifes
}
// Generate download URL for download operations
- if op.Operation == "create" || op.Operation == "update" {
+ switch op.Operation {
+ case "create", "update":
op.Operation = "download" // Normalize to download for client
downloadURL := s.generateDownloadURL(op.AttachmentID)
op.DownloadURL = &downloadURL
@@ -178,7 +179,7 @@ func (s *manifestService) GetManifest(ctx context.Context, req AttachmentManifes
totalDownloadSize += int64(*op.Size)
}
downloadCount++
- } else if op.Operation == "delete" {
+ case "delete":
deleteCount++
}
diff --git a/synkronus/pkg/attachment/service.go b/synkronus/pkg/attachment/service.go
index fba9534a7..f9f346cf4 100644
--- a/synkronus/pkg/attachment/service.go
+++ b/synkronus/pkg/attachment/service.go
@@ -74,7 +74,7 @@ func (s *service) Save(ctx context.Context, attachmentID string, file io.Reader)
if err != nil {
return err
}
- defer dst.Close()
+ defer func() { _ = dst.Close() }()
// Copy file content
_, err = io.Copy(dst, file)
diff --git a/synkronus/pkg/config/config.go b/synkronus/pkg/config/config.go
index 31ad3b714..d186ed320 100644
--- a/synkronus/pkg/config/config.go
+++ b/synkronus/pkg/config/config.go
@@ -60,10 +60,8 @@ func Load(log *logger.Logger) (*Config, error) {
if log != nil {
log.Info("Successfully loaded .env file", "path", cwdEnvPath)
}
- } else {
- if log != nil {
- log.Error("Error loading .env file", "path", cwdEnvPath, "error", err)
- }
+ } else if log != nil {
+ log.Error("Error loading .env file", "path", cwdEnvPath, "error", err)
}
}
@@ -86,16 +84,12 @@ func Load(log *logger.Logger) (*Config, error) {
if log != nil {
log.Info("Successfully loaded .env file", "path", envPath)
}
- } else {
- if log != nil {
- log.Error("Error loading .env file", "path", envPath, "error", err)
- }
+ } else if log != nil {
+ log.Error("Error loading .env file", "path", envPath, "error", err)
}
}
- } else {
- if log != nil {
- log.Error("Error getting executable path", "error", err)
- }
+ } else if log != nil {
+ log.Error("Error getting executable path", "error", err)
}
}
@@ -118,10 +112,8 @@ func Load(log *logger.Logger) (*Config, error) {
if log != nil {
log.Info("Successfully loaded .env file", "path", envPath)
}
- } else {
- if log != nil {
- log.Error("Error loading .env file", "path", envPath, "error", err)
- }
+ } else if log != nil {
+ log.Error("Error loading .env file", "path", envPath, "error", err)
}
}
}
diff --git a/synkronus/pkg/database/db.go b/synkronus/pkg/database/db.go
index 94e64060a..e59c67533 100644
--- a/synkronus/pkg/database/db.go
+++ b/synkronus/pkg/database/db.go
@@ -63,7 +63,7 @@ func New(config Config, log *logger.Logger) (*Database, error) {
defer cancel()
if err := db.PingContext(ctx); err != nil {
- db.Close()
+ _ = db.Close()
return nil, fmt.Errorf("failed to ping database: %w", err)
}
diff --git a/synkronus/pkg/dataexport/postgres.go b/synkronus/pkg/dataexport/postgres.go
index d850953eb..19254ff66 100644
--- a/synkronus/pkg/dataexport/postgres.go
+++ b/synkronus/pkg/dataexport/postgres.go
@@ -31,7 +31,7 @@ func (p *postgresDB) GetFormTypes(ctx context.Context) ([]string, error) {
if err != nil {
return nil, fmt.Errorf("failed to query form types: %w", err)
}
- defer rows.Close()
+ defer func() { _ = rows.Close() }()
var formTypes []string
for rows.Next() {
@@ -111,7 +111,7 @@ func (p *postgresDB) GetFormTypeSchema(ctx context.Context, formType string) (*F
if err != nil {
return nil, fmt.Errorf("failed to analyze form type schema: %w", err)
}
- defer rows.Close()
+ defer func() { _ = rows.Close() }()
var columns []FormTypeColumn
for rows.Next() {
@@ -184,7 +184,7 @@ func (p *postgresDB) GetObservationsForFormType(ctx context.Context, formType st
if err != nil {
return nil, fmt.Errorf("failed to query observations for form type %s: %w", formType, err)
}
- defer rows.Close()
+ defer func() { _ = rows.Close() }()
var observations []ObservationRow
for rows.Next() {
diff --git a/synkronus/pkg/dataexport/postgres_test.go b/synkronus/pkg/dataexport/postgres_test.go
index 3eca6df49..8bac98b26 100644
--- a/synkronus/pkg/dataexport/postgres_test.go
+++ b/synkronus/pkg/dataexport/postgres_test.go
@@ -12,7 +12,7 @@ func TestPostgresDB_GetFormTypes(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create mock database: %v", err)
}
- defer db.Close()
+ defer func() { _ = db.Close() }()
pgDB := NewPostgresDB(db)
@@ -75,7 +75,7 @@ func TestPostgresDB_GetFormTypeSchema(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create mock database: %v", err)
}
- defer db.Close()
+ defer func() { _ = db.Close() }()
pgDB := NewPostgresDB(db)
@@ -157,7 +157,7 @@ func TestPostgresDB_GetObservationsForFormType(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create mock database: %v", err)
}
- defer db.Close()
+ defer func() { _ = db.Close() }()
pgDB := NewPostgresDB(db)
diff --git a/synkronus/pkg/dataexport/service.go b/synkronus/pkg/dataexport/service.go
index 4c473d746..5d019b764 100644
--- a/synkronus/pkg/dataexport/service.go
+++ b/synkronus/pkg/dataexport/service.go
@@ -51,7 +51,7 @@ func (s *service) ExportParquetZip(ctx context.Context) (io.ReadCloser, error) {
// Process each form type
for _, formType := range formTypes {
if err := s.exportFormTypeToZip(ctx, formType, zipWriter); err != nil {
- zipWriter.Close()
+ _ = zipWriter.Close()
return nil, fmt.Errorf("failed to export form type %s: %w", formType, err)
}
}
@@ -119,7 +119,7 @@ func (s *service) writeParquetData(observations []ObservationRow, schema *FormTy
if err != nil {
return fmt.Errorf("failed to create parquet writer: %w", err)
}
- defer pqWriter.Close()
+ defer func() { _ = pqWriter.Close() }()
if err := pqWriter.Write(record); err != nil {
return fmt.Errorf("failed to write parquet record: %w", err)
diff --git a/synkronus/pkg/dataexport/service_test.go b/synkronus/pkg/dataexport/service_test.go
index 7a1e7bc89..f14514efe 100644
--- a/synkronus/pkg/dataexport/service_test.go
+++ b/synkronus/pkg/dataexport/service_test.go
@@ -164,7 +164,7 @@ func TestService_ExportParquetZip(t *testing.T) {
t.Errorf("Unexpected error: %v", err)
return
}
- defer zipReader.Close()
+ defer func() { _ = zipReader.Close() }()
// Read ZIP content
zipData, err := io.ReadAll(zipReader)
diff --git a/synkronus/pkg/logger/logger.go b/synkronus/pkg/logger/logger.go
index 9de699e9d..1e8e612fd 100644
--- a/synkronus/pkg/logger/logger.go
+++ b/synkronus/pkg/logger/logger.go
@@ -213,7 +213,6 @@ func (l *Logger) log(level Level, msg string, args ...any) {
// Get a buffer from the pool
buf := l.bufferPool.Get().(*bytes.Buffer)
buf.Reset()
- defer l.bufferPool.Put(buf)
// Encode the entry
encoder := json.NewEncoder(buf)
@@ -239,6 +238,7 @@ func (l *Logger) log(level Level, msg string, args ...any) {
if err := encoder.Encode(logData); err != nil {
fmt.Fprintf(os.Stderr, "Error marshaling log entry: %v\n", err)
l.putEntry(e)
+ l.bufferPool.Put(buf)
return
}
@@ -256,6 +256,9 @@ func (l *Logger) log(level Level, msg string, args ...any) {
// Return the entry to the pool
l.putEntry(e)
+ // Put buffer back to pool (must be done before os.Exit)
+ l.bufferPool.Put(buf)
+
// Handle fatal level
if level == LevelFatal {
os.Exit(1)
diff --git a/synkronus/pkg/logger/logger_test.go b/synkronus/pkg/logger/logger_test.go
index 2841860b4..b28ad4687 100644
--- a/synkronus/pkg/logger/logger_test.go
+++ b/synkronus/pkg/logger/logger_test.go
@@ -73,10 +73,8 @@ func TestLogger(t *testing.T) {
if logEntry["key"] != "value" {
t.Errorf("Expected key value to be 'value', got %s", logEntry["key"])
}
- } else {
- if output != "" {
- t.Errorf("Expected empty output, got %s", output)
- }
+ } else if output != "" {
+ t.Errorf("Expected empty output, got %s", output)
}
})
}
diff --git a/synkronus/pkg/sync/integration_test.go b/synkronus/pkg/sync/integration_test.go
index 64ff909b8..bbf0878b6 100644
--- a/synkronus/pkg/sync/integration_test.go
+++ b/synkronus/pkg/sync/integration_test.go
@@ -317,7 +317,7 @@ func TestDatabaseIntegration_VersionConsistency(t *testing.T) {
t.Fatalf("Failed to get initial version: %v", err)
}
- var lastVersion int64 = initialVersion
+ var lastVersion = initialVersion
const numOperations = 5
for i := 0; i < numOperations; i++ {
diff --git a/synkronus/pkg/sync/service.go b/synkronus/pkg/sync/service.go
index 5c1484189..aad86ca4c 100644
--- a/synkronus/pkg/sync/service.go
+++ b/synkronus/pkg/sync/service.go
@@ -113,15 +113,8 @@ func (s *Service) GetRecordsSinceVersion(ctx context.Context, sinceVersion int64
queryBuilder.WriteString(" ORDER BY version ASC, observation_id ASC")
// Add limit + 1 to check if there are more records
- // Calculate the correct parameter index based on whether we have schema types or not
- limitParamIndex := 1 // for sinceVersion
- if len(schemaTypes) > 0 {
- limitParamIndex = 2 // for sinceVersion and schemaTypes
- }
- if cursor != nil {
- limitParamIndex += 3 // for cursor.Version, cursor.Version, cursor.ID
- }
- limitParamIndex++ // for the limit parameter itself
+ // Use argIndex which already accounts for all previous parameters
+ limitParamIndex := argIndex + 1 // for the limit parameter itself
queryBuilder.WriteString(" LIMIT $")
queryBuilder.WriteString(strconv.Itoa(limitParamIndex))
@@ -135,7 +128,7 @@ func (s *Service) GetRecordsSinceVersion(ctx context.Context, sinceVersion int64
s.log.Error("Failed to query observations", "error", err)
return nil, fmt.Errorf("failed to query observations: %w", err)
}
- defer rows.Close()
+ defer func() { _ = rows.Close() }()
var records []Observation
for rows.Next() {
@@ -171,7 +164,7 @@ func (s *Service) GetRecordsSinceVersion(ctx context.Context, sinceVersion int64
}
// Determine change cutoff (version of the last record returned)
- var changeCutoff int64 = sinceVersion
+ var changeCutoff = sinceVersion
if len(records) > 0 {
changeCutoff = records[len(records)-1].Version
}
diff --git a/synkronus/pkg/sync/testdb_helper.go b/synkronus/pkg/sync/testdb_helper.go
index 4945da5d8..7231f9b05 100644
--- a/synkronus/pkg/sync/testdb_helper.go
+++ b/synkronus/pkg/sync/testdb_helper.go
@@ -66,13 +66,13 @@ func SetupTestDatabase(t *testing.T) (*sql.DB, func()) {
// Test connection with timeout
if err := pingWithTimeout(db, 5*time.Second); err != nil {
- db.Close()
+ _ = db.Close()
t.Skipf("Test database not available: %v\nEnsure PostgreSQL is running and accessible", err)
}
// Ensure clean test environment
if err := ensureTestSchema(db); err != nil {
- db.Close()
+ _ = db.Close()
t.Fatalf("Failed to setup test schema: %v", err)
}
@@ -81,7 +81,7 @@ func SetupTestDatabase(t *testing.T) (*sql.DB, func()) {
if err := ResetTestData(db); err != nil {
t.Errorf("Failed to reset test data: %v", err)
}
- db.Close()
+ _ = db.Close()
}
}