Subject: [PATCH links] Fix bug when creating second (or more) organizations. It was two fold.

From: Peter Sanchez <[email-masked]>
Date: Thu, 2 Oct 2025 07:20:51 -0600

1. An issue when an image upload failed was causing a nil pointer
   reference
2. A default public visibility was missing using the newer PostgreSQL
   enum values.

Changelog-fixed: Traceback caused by 2 edge cases when adding a second+
  organization.
---
 api/graph/schema.resolvers.go    | 22 ++++++++++++----------
 cmd/admin/commands.go            | 11 ++++++-----
 helpers.go                       | 20 ++++++++++++++++++++
 migrations/test_migration.up.sql |  6 +++---
 4 files changed, 41 insertions(+), 18 deletions(-)

diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go
index e05241f..579d3fc 100644
--- a/api/graph/schema.resolvers.go
+++ b/api/graph/schema.resolvers.go
@@ -168,11 +168,12 @@ func (r *mutationResolver) AddOrganization(ctx context.Context, input model.Orga
 	}
 
 	org := &models.Organization{
-		OwnerID:  int(user.ID),
-		OrgType:  models.OrgTypeNormal,
-		Name:     input.Name,
-		Slug:     slug,
-		IsActive: true,
+		OwnerID:    int(user.ID),
+		OrgType:    models.OrgTypeNormal,
+		Name:       input.Name,
+		Slug:       slug,
+		IsActive:   true,
+		Visibility: models.VisibilityPublic,
 		Settings: models.OrganizationSettings{
 			DefaultPerm: models.OrgLinkVisibilityPublic,
 			Billing: models.BillingSettings{
@@ -1795,11 +1796,12 @@ func (r *mutationResolver) CompleteRegister(ctx context.Context, input *model.Co
 	}
 
 	userOrg := &models.Organization{
-		OwnerID:  int(user.ID),
-		OrgType:  models.OrgTypeUser,
-		Name:     user.Name,
-		Slug:     slug,
-		IsActive: true,
+		OwnerID:    int(user.ID),
+		OrgType:    models.OrgTypeUser,
+		Name:       user.Name,
+		Slug:       slug,
+		IsActive:   true,
+		Visibility: models.VisibilityPublic,
 		Settings: models.OrganizationSettings{
 			DefaultPerm: models.OrgLinkVisibilityPublic,
 			Billing: models.BillingSettings{
diff --git a/cmd/admin/commands.go b/cmd/admin/commands.go
index 674679a..11d870f 100644
--- a/cmd/admin/commands.go
+++ b/cmd/admin/commands.go
@@ -118,11 +118,12 @@ func userSetup(ctx context.Context) error {
 
 	slug := links.Slugify(username)
 	org := &models.Organization{
-		OwnerID:  int(user.ID),
-		OrgType:  models.OrgTypeUser,
-		Name:     user.Name,
-		Slug:     slug,
-		IsActive: true,
+		OwnerID:    int(user.ID),
+		OrgType:    models.OrgTypeUser,
+		Name:       user.Name,
+		Slug:       slug,
+		IsActive:   true,
+		Visibility: models.VisibilityPublic,
 	}
 	return org.Store(ctx)
 }
diff --git a/helpers.go b/helpers.go
index 82f5143..df0d56f 100644
--- a/helpers.go
+++ b/helpers.go
@@ -121,11 +121,22 @@ func ParseInputErrors(c echo.Context, graphError *gqlclient.Error, fMap gobwebs.
 // to be stored or used in another process
 func ProcessImage(ctx context.Context, image *graphql.Upload) (io.ReadSeeker, string, error) {
 	validator := valid.New(ctx)
+	if image == nil || image.File == nil {
+		validator.Error("No image file provided").
+			WithField("image").
+			WithCode(valid.ErrValidationCode)
+	}
+	if !validator.Ok() {
+		return nil, "", nil
+	}
+
 	ext := strings.ToLower(filepath.Ext(image.Filename))
 	if ext != JPG && ext != JPEG && ext != PNG {
 		validator.Error("This file extension is not supported").
 			WithField("image").
 			WithCode(valid.ErrValidationCode)
+	}
+	if !validator.Ok() {
 		return nil, "", nil
 	}
 
@@ -139,8 +150,11 @@ func ProcessImage(ctx context.Context, image *graphql.Upload) (io.ReadSeeker, st
 		validator.Error("The file submitted is not a valid JPG or PNG file").
 			WithField("image").
 			WithCode(valid.ErrValidationCode)
+	}
+	if !validator.Ok() {
 		return nil, "", nil
 	}
+
 	image.File.Seek(0, io.SeekStart)
 	return image.File, ext, nil
 }
@@ -148,10 +162,16 @@ func ProcessImage(ctx context.Context, image *graphql.Upload) (io.ReadSeeker, st
 // StoreImage save an image into the storage and return its path
 // to be saved into the model.
 func StoreImage(ctx context.Context, image *graphql.Upload) (string, error) {
+	if image == nil || image.File == nil {
+		return "", nil
+	}
 	imageReader, ext, err := ProcessImage(ctx, image)
 	if err != nil {
 		return "", err
 	}
+	if imageReader == nil {
+		return "", nil
+	}
 	path := fmt.Sprintf("media/org_images/%s/%s%s",
 		ksuid.New().String(), ksuid.New().String(), ext)
 
diff --git a/migrations/test_migration.up.sql b/migrations/test_migration.up.sql
index 13f75f0..3e167d9 100644
--- a/migrations/test_migration.up.sql
+++ b/migrations/test_migration.up.sql
@@ -2,10 +2,10 @@ INSERT INTO users (full_name, password, email, is_verified) VALUES ('user', 'qwe
 INSERT INTO users (full_name, password, email, is_verified) VALUES ('test_api_user', 'qwerty', [email-masked]', true);
 INSERT INTO users (full_name, password, email, is_verified, is_superuser) VALUES ('superuser', 'qwerty', [email-masked]', true, true);
 
-INSERT INTO organizations (owner_id, name, slug, settings) VALUES (1, 'personal org', 'personal-org', '{"billing": {"status": "FREE"}, "default_perm": "PUBLIC"}');
-INSERT INTO organizations (owner_id, name, slug, org_type, settings) VALUES (1, 'business org', 'business_org', 'NORMAL', '{"billing": {"status": "FREE"}, "default_perm": "PUBLIC"}');
+INSERT INTO organizations (owner_id, name, slug, visibility, settings) VALUES (1, 'personal org', 'personal-org', 'PUBLIC', '{"billing": {"status": "FREE"}, "default_perm": "PUBLIC"}');
+INSERT INTO organizations (owner_id, name, slug, org_type, visibility, settings) VALUES (1, 'business org', 'business_org', 'NORMAL', 'PUBLIC', '{"billing": {"status": "FREE"}, "default_perm": "PUBLIC"}');
 
-INSERT INTO organizations (owner_id, name, slug, settings) VALUES (2, 'api test org', 'api-test-org', '{"billing": {"status": "FREE"}, "default_perm": "PUBLIC"}');
+INSERT INTO organizations (owner_id, name, slug, visibility, settings) VALUES (2, 'api test org', 'api-test-org', 'PUBLIC', '{"billing": {"status": "FREE"}, "default_perm": "PUBLIC"}');
 
 INSERT INTO base_urls (url, hash) VALUES ('http://base.com', 'abcdefg');
 
-- 
2.49.1