refactor: improve handling of optional CustomerID in project models and DTOs
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled

This commit is contained in:
Jean Jacques Avril 2025-04-01 15:48:43 +00:00
parent a9c7598862
commit 4b47da3673
13 changed files with 1446 additions and 28 deletions

View File

@ -12,7 +12,7 @@ type ProjectDto struct {
UpdatedAt time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"`
LastEditorID string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"`
Name string `json:"name" example:"Time Tracking App"`
CustomerID *string `json:"customerId" example:"01HGW2BBG0000000000000000"`
CustomerID *string `json:"customerId,omitempty" example:"01HGW2BBG0000000000000000"`
}
type ProjectCreateDto struct {

View File

@ -148,13 +148,17 @@ func (h *ProjectHandler) DeleteProject(c *gin.Context) {
// Helper functions for DTO conversion
func convertProjectToDTO(project *models.Project) dto.ProjectDto {
customerId := project.CustomerID.String()
var customerIdPtr *string
if project.CustomerID != nil {
customerIdStr := project.CustomerID.String()
customerIdPtr = &customerIdStr
}
return dto.ProjectDto{
ID: project.ID.String(),
CreatedAt: project.CreatedAt,
UpdatedAt: project.UpdatedAt,
Name: project.Name,
CustomerID: &customerId,
CustomerID: customerIdPtr,
}
}
@ -182,13 +186,13 @@ func convertUpdateProjectDTOToModel(dto dto.ProjectUpdateDto, id types.ULID) (mo
if dto.CustomerID.Valid {
if dto.CustomerID.Value == nil {
update.CustomerID = nil
update.CustomerID = types.Null[types.ULID]()
} else {
customerID, err := types.ULIDFromString(*dto.CustomerID.Value)
if err != nil {
return models.ProjectUpdate{}, fmt.Errorf("invalid customer ID: %w", err)
}
update.CustomerID = &customerID
update.CustomerID = types.NewNullable(customerID)
}
}

View File

@ -80,7 +80,7 @@ func MigrateDB() error {
// createConnection creates a new database connection with the given configuration
func createConnection(dbConfig config.DatabaseConfig, dbName string) (*gorm.DB, error) {
// Create DSN (Data Source Name)
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbName=%s sslmode=%s",
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
dbConfig.Host, dbConfig.Port, dbConfig.User, dbConfig.Password, dbName, dbConfig.SSLMode)
// Configure GORM logger

View File

@ -90,7 +90,7 @@ func UpdateModel(ctx context.Context, model any, updates any) error {
updateMap := make(map[string]any)
// Iterate through all fields
for i := 0; i < updateValue.NumField(); i++ {
for i := range updateValue.NumField() {
field := updateValue.Field(i)
fieldType := updateType.Field(i)

View File

@ -14,7 +14,7 @@ import (
type Project struct {
EntityBase
Name string `gorm:"column:name;not null"`
CustomerID *types.ULID `gorm:"column:customer_id;type:bytea;not null"`
CustomerID *types.ULID `gorm:"column:customer_id;type:bytea;index"`
// Relationships (for Eager Loading)
Customer *Customer `gorm:"foreignKey:CustomerID"`
@ -33,9 +33,9 @@ type ProjectCreate struct {
// ProjectUpdate contains the updatable fields of a project
type ProjectUpdate struct {
ID types.ULID `gorm:"-"` // Exclude from updates
Name *string `gorm:"column:name"`
CustomerID *types.ULID `gorm:"column:customer_id"`
ID types.ULID `gorm:"-"` // Exclude from updates
Name *string `gorm:"column:name"`
CustomerID types.Nullable[types.ULID] `gorm:"column:customer_id"`
}
// Validate checks if the Create struct contains valid data
@ -44,7 +44,7 @@ func (pc *ProjectCreate) Validate() error {
return errors.New("project name cannot be empty")
}
// Check for valid CustomerID
if pc.CustomerID.Compare(types.ULID{}) == 0 {
if pc.CustomerID != nil && pc.CustomerID.Compare(types.ULID{}) == 0 {
return errors.New("customerID cannot be empty")
}
return nil
@ -122,7 +122,7 @@ func CreateProject(ctx context.Context, create ProjectCreate) (*Project, error)
}
// Check if the customer exists
if create.CustomerID == nil {
if create.CustomerID != nil {
customer, err := GetCustomerByID(ctx, *create.CustomerID)
if err != nil {
return nil, fmt.Errorf("error checking the customer: %w", err)
@ -160,13 +160,18 @@ func UpdateProject(ctx context.Context, update ProjectUpdate) (*Project, error)
}
// If CustomerID is updated, check if the customer exists
if update.CustomerID != nil {
customer, err := GetCustomerByID(ctx, *update.CustomerID)
if err != nil {
return nil, fmt.Errorf("error checking the customer: %w", err)
}
if customer == nil {
return nil, errors.New("the specified customer does not exist")
if update.CustomerID.Valid {
if update.CustomerID.Value != nil {
customer, err := GetCustomerByID(ctx, *update.CustomerID.Value)
if err != nil {
return nil, fmt.Errorf("error checking the customer: %w", err)
}
if customer == nil {
return nil, errors.New("the specified customer does not exist")
}
} else {
// If CustomerID is nil, set it to nil in the project
project.CustomerID = nil
}
}

View File

@ -187,7 +187,7 @@ func (uc *UserCreate) Validate() error {
}
}
if uc.CompanyID.Compare(types.ULID{}) == 0 {
if uc.CompanyID != nil && uc.CompanyID.Compare(types.ULID{}) == 0 {
return errors.New("companyID cannot be empty")
}
@ -363,13 +363,15 @@ func CreateUser(ctx context.Context, create UserCreate) (*User, error) {
return errors.New("email is already in use")
}
// Check if company exists
var companyCount int64
if err := tx.Model(&Company{}).Where("id = ?", create.CompanyID).Count(&companyCount).Error; err != nil {
return fmt.Errorf("error checking company: %w", err)
}
if companyCount == 0 {
return errors.New("the specified company does not exist")
if create.CompanyID != nil {
// Check if company exists
var companyCount int64
if err := tx.Model(&Company{}).Where("id = ?", create.CompanyID).Count(&companyCount).Error; err != nil {
return fmt.Errorf("error checking company: %w", err)
}
if companyCount == 0 {
return errors.New("the specified company does not exist")
}
}
// Hash password with unique salt

View File

@ -0,0 +1,168 @@
{
"info": {
"_postman_id": "YOUR_POSTMAN_ID",
"name": "Activity API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "GET /api/activities",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/activities",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"activities"
]
}
},
"response": []
},
{
"name": "GET /api/activities/:id",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/activities/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"activities",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "POST /api/activities",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"name\": \"\",\n\t\"billingRate\": 0\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/activities",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"activities"
]
}
},
"response": []
},
{
"name": "PUT /api/activities/:id",
"request": {
"method": "PUT",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"name\": \"\",\n\t\"billingRate\": 0\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/activities/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"activities",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "DELETE /api/activities/:id",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/activities/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"activities",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
}
]
}

View File

@ -0,0 +1,168 @@
{
"info": {
"_postman_id": "YOUR_POSTMAN_ID",
"name": "Company API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "GET /api/companies",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/companies",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"companies"
]
}
},
"response": []
},
{
"name": "GET /api/companies/:id",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/companies/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"companies",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "POST /api/companies",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"name\": \"\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/companies",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"companies"
]
}
},
"response": []
},
{
"name": "PUT /api/companies/:id",
"request": {
"method": "PUT",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"name\": \"\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/companies/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"companies",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "DELETE /api/companies/:id",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/companies/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"companies",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
}
]
}

View File

@ -0,0 +1,200 @@
{
"info": {
"_postman_id": "YOUR_POSTMAN_ID",
"name": "Customer API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "GET /api/customers",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/customers",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"customers"
]
}
},
"response": []
},
{
"name": "GET /api/customers/:id",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/customers/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"customers",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "GET /api/customers/company/:companyId",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/customers/company/:companyId",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"customers",
"company",
":companyId"
],
"variable": [
{
"key": "companyId",
"value": ""
}
]
}
},
"response": []
},
{
"name": "POST /api/customers",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"name\": \"\",\n\t\"companyId\": \"\",\n\t\"ownerUserID\": \"\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/customers",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"customers"
]
}
},
"response": []
},
{
"name": "PUT /api/customers/:id",
"request": {
"method": "PUT",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"name\": \"\",\n\t\"companyId\": \"\",\n\t\"ownerUserID\": \"\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/customers/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"customers",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "DELETE /api/customers/:id",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/customers/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"customers",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
}
]
}

View File

@ -0,0 +1,225 @@
{
"info": {
"_postman_id": "YOUR_POSTMAN_ID",
"name": "Project API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "GET /api/projects",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/projects",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"projects"
]
}
},
"response": []
},
{
"name": "GET /api/projects/with-customers",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/projects/with-customers",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"projects",
"with-customers"
]
}
},
"response": []
},
{
"name": "GET /api/projects/:id",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/projects/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"projects",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "GET /api/projects/customer/:customerId",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/projects/customer/:customerId",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"projects",
"customer",
":customerId"
],
"variable": [
{
"key": "customerId",
"value": ""
}
]
}
},
"response": []
},
{
"name": "POST /api/projects",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"name\": \"\",\n\t\"customerId\": \"\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/projects",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"projects"
]
}
},
"response": []
},
{
"name": "PUT /api/projects/:id",
"request": {
"method": "PUT",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"name\": \"\",\n\t\"customerId\": \"\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/projects/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"projects",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "DELETE /api/projects/:id",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/projects/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"projects",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
}
]
}

View File

@ -0,0 +1,292 @@
{
"info": {
"_postman_id": "YOUR_POSTMAN_ID",
"name": "TimeEntry API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "GET /api/time-entries",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/time-entries",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"time-entries"
]
}
},
"response": []
},
{
"name": "GET /api/time-entries/me",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/time-entries/me",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"time-entries",
"me"
]
}
},
"response": []
},
{
"name": "GET /api/time-entries/range",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/time-entries/range?start=2023-01-01T00:00:00Z&end=2023-01-02T00:00:00Z",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"time-entries",
"range"
],
"query": [
{
"key": "start",
"value": "2023-01-01T00:00:00Z"
},
{
"key": "end",
"value": "2023-01-02T00:00:00Z"
}
]
}
},
"response": []
},
{
"name": "GET /api/time-entries/:id",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/time-entries/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"time-entries",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "GET /api/time-entries/user/:userId",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/time-entries/user/:userId",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"time-entries",
"user",
":userId"
],
"variable": [
{
"key": "userId",
"value": ""
}
]
}
},
"response": []
},
{
"name": "GET /api/time-entries/project/:projectId",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/time-entries/project/:projectId",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"time-entries",
"project",
":projectId"
],
"variable": [
{
"key": "projectId",
"value": ""
}
]
}
},
"response": []
},
{
"name": "POST /api/time-entries",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"userID\": \"\",\n \"projectID\": \"\",\n \"activityID\": \"\",\n \"start\": \"2023-01-01T00:00:00Z\",\n \"end\": \"2023-01-01T01:00:00Z\",\n \"description\": \"\",\n \"billable\": true\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/time-entries",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"time-entries"
]
}
},
"response": []
},
{
"name": "PUT /api/time-entries/:id",
"request": {
"method": "PUT",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"userID\": \"\",\n \"projectID\": \"\",\n \"activityID\": \"\",\n \"start\": \"2023-01-01T00:00:00Z\",\n \"end\": \"2023-01-01T01:00:00Z\",\n \"description\": \"\",\n \"billable\": true\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/time-entries/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"time-entries",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
},
{
"name": "DELETE /api/time-entries/:id",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/time-entries/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"time-entries",
":id"
],
"variable": [
{
"key": "id",
"value": ""
}
]
}
},
"response": []
}
]
}

View File

@ -0,0 +1,241 @@
{
"info": {
"_postman_id": "YOUR_POSTMAN_ID",
"name": "User API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Auth",
"item": [
{
"name": "POST /api/auth/login",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n\t\"email\": \"\",\n\t\"password\": \"\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/auth/login",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"auth",
"login"
]
}
},
"response": []
},
{
"name": "POST /api/auth/register",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n\t\"email\": \"\",\n\t\"password\": \"\",\n\t\"role\": \"user\",\n\t\"companyID\": \"\",\n\t\"hourlyRate\": 0\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/auth/register",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"auth",
"register"
]
}
},
"response": []
},
{
"name": "GET /api/auth/me",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/auth/me",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"auth",
"me"
]
}
},
"response": []
}
]
},
{
"name": "Users",
"item": [
{
"name": "GET /api/users",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/users",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"users"
]
}
},
"response": []
},
{
"name": "GET /api/users/:id",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/users/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"users",
":id"
]
}
},
"response": []
},
{
"name": "POST /api/users",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"email\": \"\",\n\t\"password\": \"\",\n\t\"role\": \"user\",\n\t\"companyID\": \"\",\n\t\"hourlyRate\": 0\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/users",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"users"
]
}
},
"response": []
},
{
"name": "PUT /api/users/:id",
"request": {
"method": "PUT",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"email\": \"\",\n\t\"password\": \"\",\n\t\"role\": \"user\",\n\t\"companyID\": \"\",\n\t\"hourlyRate\": 0\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{API_URL}}/api/users/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"users",
":id"
]
}
},
"response": []
},
{
"name": "DELETE /api/users/:id",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{JWT_TOKEN}}",
"type": "text"
}
],
"url": {
"raw": "{{API_URL}}/api/users/:id",
"host": [
"{{API_URL}}"
],
"path": [
"api",
"users",
":id"
]
}
},
"response": []
}
]
}
]
}

113
flake.nix Normal file
View File

@ -0,0 +1,113 @@
{
description = "Development environment for Go and Next.js (TypeScript)";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
config = {
allowUnfree = true;
};
};
# Go development tools
goPackages = with pkgs; [
go
gopls
golangci-lint
delve
go-outline
gotools
go-mockgen
gomodifytags
impl
gotests
];
# TypeScript/Next.js development tools
nodePackages = with pkgs; [
nodejs_20
nodePackages.typescript
nodePackages.typescript-language-server
nodePackages.yarn
nodePackages.pnpm
nodePackages.npm
nodePackages.prettier
nodePackages.eslint
nodePackages.next
];
# General development tools
commonPackages = with pkgs; [
git
gh
nixpkgs-fmt
pre-commit
ripgrep
jq
curl
coreutils
gnumake
];
# VSCode with extensions
vscodeWithExtensions = pkgs.vscode-with-extensions.override {
vscodeExtensions = with pkgs.vscode-extensions; [
golang.go # Go support
esbenp.prettier-vscode # Prettier
dbaeumer.vscode-eslint # ESLint
ms-vscode.vscode-typescript-tslint-plugin # TypeScript
bradlc.vscode-tailwindcss # Tailwind CSS support
jnoortheen.nix-ide # Nix support
] ++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
{
name = "nextjs";
publisher = "pulkitgangwar";
version = "1.0.6";
sha256 = "sha256-L6ZgqNkM0qzSiTKiGfgQB9m3U0HmwLA3NZ9nrslQjeg=";
}
];
};
in
{
devShells.default = pkgs.mkShell {
buildInputs = goPackages ++ nodePackages ++ commonPackages ++ [ vscodeWithExtensions ];
shellHook = ''
echo "🚀 Welcome to the Go and Next.js (TypeScript) development environment!"
echo "📦 Available tools:"
echo " Go: $(go version)"
echo " Node: $(node --version)"
echo " TypeScript: $(tsc --version)"
echo " Next.js: $(npx next --version)"
echo ""
echo "🔧 Use 'code .' to open VSCode with the appropriate extensions"
echo "🔄 Run 'nix flake update' to update dependencies"
'';
# Environment variables
GOROOT = "${pkgs.go}/share/go";
GOPATH = "$(pwd)/.go";
GO111MODULE = "on";
# NodeJS setup
NODE_OPTIONS = "--max-old-space-size=4096";
};
# Optional: Add custom packages if needed
packages = {
# Example of a custom package or script if needed
# my-tool = ...
};
# Default package if someone runs `nix build`
defaultPackage = self.devShells.${system}.default;
});
}