Received: from mail.netlandish.com (mail.netlandish.com [174.136.98.166]) by code.netlandish.com (Postfix) with ESMTP id A5CD2210 for <~netlandish/links-dev@lists.code.netlandish.com>; Thu, 02 Oct 2025 13:19:47 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=209.85.128.182; helo=mail-yw1-f182.google.com; envelope-from=peter@netlandish.com; receiver= Authentication-Results: mail.netlandish.com; dkim=pass (1024-bit key; unprotected) header.d=netlandish.com header.i=@netlandish.com header.b=mz+p5lyb Received: from mail-yw1-f182.google.com (mail-yw1-f182.google.com [209.85.128.182]) by mail.netlandish.com (Postfix) with ESMTP id E3FB41D81A1 for <~netlandish/links-dev@lists.code.netlandish.com>; Thu, 02 Oct 2025 13:20:57 +0000 (UTC) Received: by mail-yw1-f182.google.com with SMTP id 00721157ae682-71d603a9cfaso11573217b3.1 for <~netlandish/links-dev@lists.code.netlandish.com>; Thu, 02 Oct 2025 06:20:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=netlandish.com; s=google; t=1759411257; x=1760016057; darn=lists.code.netlandish.com; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=tTBAB7yIdL40j8x4NG5hb3b/3Puez667VEwinyYsw9c=; b=mz+p5lybxeEbwo2pQyvk/I0Jp8Ehn/wDCeKVLwWJReWQkR/ZKFZr9g2RHqBouiIL+K EGtF3L8jjIdIpDVjjIbQATVcXefyT5Ttpmc1Ss9R+9NQbuMo+3y4W+di0MAqqthnMs21 5X/WtA06ASLj23ErfcqTTGkWYGQyAgU6dj6eE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759411257; x=1760016057; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=tTBAB7yIdL40j8x4NG5hb3b/3Puez667VEwinyYsw9c=; b=ZODttrdidSGSZYPJwkCoI7csWqbFpa9m75uqXcucw6wytHWyXF9SySpE5qfWAgK3kq mBn7gwVafTpRWGX6Icmsc6PpTRTjxNFbHKOaGKvhStMzRHIug10s3dglRl11XHLn+05M Vsl2pi7Pv9Zoe22PnMcltEGWlngDeoYlAEcsQtPfjnmRBMbRi+y/VxYMzSCPwjIN7Iu5 apZ442bqCOZEDHZRCJlnP41x3i8JnKyKGoG3CeMOIktkV+U//b73Ww1IQdR5hc+oCFjx u8GMAVuUUGP1/mscxDpjN0HKEsBp0Me81Cl8i2L5UaxnjKBBrsMmx+xfcO7SF805qHgv PdOw== X-Gm-Message-State: AOJu0Yxo0JksFqCH42xwp8bHMJ6DsRHycNG1bsuCBdlxVWqwG9zDWU2U XzsrXKnZDJb/8XJ62mSCOf+zz9bhkh4zXAwXCsOV/nsIsBD3goo2Un653R78hoHyJp8qwIkCiyg IIQ/fTnI= X-Gm-Gg: ASbGncsFToslUgl5repmtvZv6Adjte3uBn5pAjdWWorAoig+wNqLV8qDMDlunBay9JR KWgEbC7PenTH9zrCTxl0+aG9EhVEd8BC9ILk1YgQCF3Vx9ldiPkOr5QiiGGOryNz6+YtAIzNuHE cdSqH5sWdGNaLFzAu+3IC+0Zszew5pxtMZVuYuY4JW0KMgRMmUVal+ReI3qaM0YlKue30x7BWss p9u0rUAkXko/RKjwUyW24jeS0E/qmfKP7CX/1uNIPzuWXegiy+gAzOHgWibv1F4k/cPsui3Juab YWeSyMpmD8ttBxByWKKTsRxim52C63+YMIuA/Q56RfavGTUW3ImcJ/9NxGt5q+pBc7vgYy0vcXB 4YoyNhIIlMavZufpUKhimEFvPJlvpkbwxRdczCCXylhh+N3k= X-Google-Smtp-Source: AGHT+IGEQoUMkyhvNj5MlYkhNtScn5JgHk+XhIAj6PiOzWaPVaead3cnsGU10W2qDTRlQ4huIisWDA== X-Received: by 2002:a53:bd4e:0:b0:636:17d6:a30 with SMTP id 956f58d0204a3-63b6fee1a60mr7346600d50.15.1759411256307; Thu, 02 Oct 2025 06:20:56 -0700 (PDT) Received: from localhost ([2803:2d60:1107:87f:1ed8:9eff:1f1c:56e1]) by smtp.gmail.com with UTF8SMTPSA id 956f58d0204a3-63b8457148esm740265d50.3.2025.10.02.06.20.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 02 Oct 2025 06:20:55 -0700 (PDT) From: Peter Sanchez To: ~netlandish/links-dev@lists.code.netlandish.com Cc: Peter Sanchez Subject: [PATCH links] Fix bug when creating second (or more) organizations. It was two fold. Date: Thu, 2 Oct 2025 07:20:51 -0600 Message-ID: <20251002132053.26484-1-peter@netlandish.com> X-Mailer: git-send-email 2.49.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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', 'test@api.com', true); INSERT INTO users (full_name, password, email, is_verified, is_superuser) VALUES ('superuser', 'qwerty', 'superuser@api.com', 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