Compare commits

...

6 Commits

Author SHA1 Message Date
Kyle Mendell
9834a08843 release: 2.6.1 2026-04-21 12:10:30 -05:00
Marc
4f40352497 chore(translations): Add catalan language (#1436)
Co-authored-by: Marc <marc@radiovoltrega.com>
Co-authored-by: Kyle Mendell <kmendell@ofkm.us>
2026-04-21 12:07:50 -05:00
Kyle Mendell
975d3c79c6 fix: restore login screen background from not showing up 2026-04-21 11:55:52 -05:00
Alessandro (Ale) Segala
2f0338211d chore: update golangci-lint (#1440) 2026-04-21 11:53:06 -05:00
Elias Schneider
9c1a8b3c87 chore(translations): update translations via Crowdin (#1437) 2026-04-20 00:15:45 -05:00
Kyle Mendell
ce4b89da65 chore: ignore webauthn type for swagger generation 2026-04-19 16:48:27 -05:00
25 changed files with 589 additions and 550 deletions

View File

@@ -34,7 +34,7 @@ jobs:
- name: Run Golangci-lint
uses: golangci/golangci-lint-action@v9.0.0
with:
version: v2.9.0
version: v2.11.4
args: --build-tags=exclude_frontend
working-directory: backend
only-new-issues: ${{ github.event_name == 'pull_request' }}

View File

@@ -1 +1 @@
2.6.0
2.6.1

View File

@@ -1,3 +1,17 @@
## v2.6.1
### Bug Fixes
- restore login screen background from not showing up ([975d3c7](https://github.com/pocket-id/pocket-id/commit/975d3c79c6a882291c69b31d25bfcd8b7896528c) by @kmendell)
### Other
- ignore webauthn type for swagger generation ([ce4b89d](https://github.com/pocket-id/pocket-id/commit/ce4b89da650f025747fd0dd45eab5cebe29f5a93) by @kmendell)
- update golangci-lint ([#1440](https://github.com/pocket-id/pocket-id/pull/1440) by @ItalyPaleAle)
- Add catalan language ([#1436](https://github.com/pocket-id/pocket-id/pull/1436) by @mcasellas)
**Full Changelog**: https://github.com/pocket-id/pocket-id/compare/v2.6.0...v2.6.1
## v2.6.0
### Bug Fixes

View File

@@ -15,6 +15,7 @@ require (
github.com/dunglas/go-urlpattern v0.0.0-20241020164140-716dfa1c80b1
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6
github.com/emersion/go-smtp v0.24.0
github.com/fsnotify/fsnotify v1.8.0
github.com/gin-contrib/slog v1.2.1
github.com/gin-gonic/gin v1.12.0
github.com/glebarez/go-sqlite v1.22.0
@@ -86,7 +87,6 @@ require (
github.com/disintegration/gift v1.2.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gin-contrib/sse v1.1.1 // indirect

View File

@@ -264,7 +264,10 @@ func runServer(ctx context.Context, config *serverConfig) error {
notifySystemdReady()
<-ctx.Done()
return shutdownServer(ctx, config.server)
// We do not pass the context because it's already been canceled
//nolint:contextcheck
return shutdownServer(config.server)
}
func startCertWatcher(ctx context.Context, certProvider *tlsCertProvider) (*fsnotify.Watcher, error) {
@@ -321,9 +324,10 @@ func notifySystemdReady() {
}
}
func shutdownServer(ctx context.Context, srv *http.Server) error {
shutdownCtx, shutdownCancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
shutdownErr := srv.Shutdown(shutdownCtx)
func shutdownServer(srv *http.Server) error {
// Note we use the background context here as ctx has been canceled already
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
shutdownErr := srv.Shutdown(shutdownCtx) //nolint:contextcheck
shutdownCancel()
if shutdownErr != nil {
// Log the error only (could be context canceled)

View File

@@ -347,6 +347,7 @@ func resolveFileBasedEnvVariable(field reflect.Value, fieldType reflect.StructFi
return nil
}
// #nosec G703 - Path is passed by the admin
fileContent, err := os.ReadFile(envVarFileValue)
if err != nil {
return fmt.Errorf("failed to read file for env var %s: %w", envVarFileName, err)

View File

@@ -272,7 +272,7 @@ func TestPrepareEnvConfig_FileBasedAndToLower(t *testing.T) {
require.NoError(t, err)
dbConnFile := tempDir + "/db_connection.txt"
dbConnContent := "postgres://user:pass@localhost/testdb"
dbConnContent := "postgres://user:pass@localhost/testdb" // #nosec G101 - test credential
err = os.WriteFile(dbConnFile, []byte(dbConnContent), 0600)
require.NoError(t, err)

View File

@@ -10,7 +10,7 @@ type WebauthnCredentialDto struct {
Name string `json:"name"`
CredentialID string `json:"credentialID"`
AttestationType string `json:"attestationType"`
Transport []protocol.AuthenticatorTransport `json:"transport"`
Transport []protocol.AuthenticatorTransport `json:"transport" swaggertype:"array,string"`
BackupEligible bool `json:"backupEligible"`
BackupState bool `json:"backupState"`

View File

@@ -60,7 +60,7 @@ func TestWithApiKeyAuthDisabled(t *testing.T) {
})
t.Run("rejects API key auth when API key auth is disabled", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/protected", nil)
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/protected", nil)
req.Header.Set("X-API-Key", apiKeyToken)
recorder := httptest.NewRecorder()
@@ -75,7 +75,7 @@ func TestWithApiKeyAuthDisabled(t *testing.T) {
})
t.Run("allows JWT auth when API key auth is disabled", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/api/protected", nil)
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/protected", nil)
req.Header.Set("Authorization", "Bearer "+jwtToken)
recorder := httptest.NewRecorder()

View File

@@ -18,7 +18,7 @@ func TestCacheControlMiddlewareSetsDefault(t *testing.T) {
c.Status(http.StatusOK)
})
req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody)
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/test", http.NoBody)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
@@ -36,7 +36,7 @@ func TestCacheControlMiddlewarePreservesExistingHeader(t *testing.T) {
c.Status(http.StatusOK)
})
req := httptest.NewRequest(http.MethodGet, "/custom", http.NoBody)
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/custom", http.NoBody)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)

View File

@@ -265,9 +265,9 @@ func (s *AppConfigService) UpdateAppConfigValues(ctx context.Context, keysAndVal
// We update the in-memory data (in the cfg struct) and collect values to update in the database
// (Note the += 2, as we are iterating through key-value pairs)
dbUpdate := make([]model.AppConfigVariable, 0, len(keysAndValues)/2)
for i := 0; i < len(keysAndValues); i += 2 {
key := keysAndValues[i]
value := keysAndValues[i+1]
for i := 1; i < len(keysAndValues); i += 2 {
key := keysAndValues[i-1]
value := keysAndValues[i]
// Ensure that the field is valid
// We do this by grabbing the default value
@@ -408,6 +408,7 @@ func (s *AppConfigService) loadDbConfigFromEnv(ctx context.Context, tx *gorm.DB)
if attrs == "sensitive" {
fileName := os.Getenv(envVarName + "_FILE")
if fileName != "" {
// #nosec G703 - Value is provided by admin
b, err := os.ReadFile(fileName)
if err != nil {
return nil, fmt.Errorf("failed to read secret '%s' from file '%s': %w", envVarName, fileName, err)

View File

@@ -104,7 +104,7 @@ func newFileHeader(t *testing.T, filename string, content []byte) *multipart.Fil
require.NoError(t, writer.Close())
req := httptest.NewRequest(http.MethodPost, "/", body)
req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/", body)
req.Header.Set("Content-Type", writer.FormDataContentType())
_, fileHeader, err := req.FormFile("file")

View File

@@ -92,7 +92,7 @@ func (s *AuditLogService) CreateNewSignInWithEmail(ctx context.Context, ipAddres
// If the user hasn't logged in from the same device before and email notifications are enabled, send an email
if s.appConfigService.GetDbConfig().EmailLoginNotificationEnabled.IsTrue() && count <= 1 {
// We use a background context here as this is running in a goroutine
// #nosec G118 - We use a background context here as this is running in a goroutine
//nolint:contextcheck
go func() {
span := trace.SpanFromContext(ctx)

View File

@@ -97,7 +97,7 @@ func (s *OneTimeAccessService) requestOneTimeAccessEmailInternal(ctx context.Con
return nil, err
}
// We use a background context here as this is running in a goroutine
// #nosec G118 - We use a background context here as this is running in a goroutine
//nolint:contextcheck
go func() {
span := trace.SpanFromContext(ctx)

View File

@@ -136,7 +136,8 @@ func (s *UserService) GetProfilePicture(ctx context.Context, userID string) (io.
// Save the default picture for future use (in a goroutine to avoid blocking)
defaultPictureBytes := defaultPicture.Bytes()
//nolint:contextcheck
//#nosec G118 - We use a background context as this is running in background
// nolint:contextcheck
go func() {
// Use bytes.NewReader because we need an io.ReadSeeker
rErr := s.fileStorage.Save(context.Background(), defaultPicturePath, bytes.NewReader(defaultPictureBytes))

View File

@@ -35,7 +35,7 @@ func TestValidateCallbackURLPattern(t *testing.T) {
},
{
name: "wildcard userinfo",
pattern: "https://user:*@example.com/callback",
pattern: "https://user:*@example.com/callback", // #nosec G101 - Test credential
shouldError: false,
},
{

View File

@@ -64,6 +64,7 @@ func TestBearerAuth(t *testing.T) {
}
}
// #nosec G101 - Test credentials
func TestOAuthClientBasicAuth(t *testing.T) {
tests := []struct {
name string

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "pocket-id-frontend",
"version": "2.6.0",
"version": "2.6.1",
"private": true,
"type": "module",
"scripts": {

View File

@@ -37,7 +37,7 @@
isInitialLoad = !e?.from?.url;
});
const isDesktop = new MediaQuery('min-width: 1024px');
const isDesktop = new MediaQuery('(min-width: 1024px)');
let alternativeSignInButton = $state({
href: '/login/alternative',
label: m.alternative_sign_in_methods()
@@ -61,7 +61,12 @@
{#if backgroundImageExists === undefined}
<div class="bg-background h-screen"></div>
{:else if isDesktop.current}
<div in:fade={{ duration: 150 }} class="h-screen items-center overflow-hidden text-center">
<div
in:fade={{ duration: 150 }}
class="relative flex h-screen w-full items-center overflow-hidden text-center {backgroundImageExists
? 'justify-start'
: 'justify-center'}"
>
<div
class="relative z-10 flex h-full p-16 {cn(
showAlternativeSignInMethodButton && 'pb-0',
@@ -88,7 +93,7 @@
{#if backgroundImageExists}
<!-- Background image -->
<div
class="absolute top-0 right-0 left-500px bottom-0 z-0 overflow-hidden rounded-[40px] m-6"
class="absolute top-0 right-0 bottom-0 left-[650px] z-0 m-6 overflow-hidden rounded-[40px] 2xl:left-[800px]"
>
<img
src={cachedBackgroundImage.getUrl()}

View File

@@ -20,8 +20,16 @@
const oidService = new OidcService();
let { data }: PageProps = $props();
let { client, scope, callbackURL, nonce, codeChallenge, codeChallengeMethod, authorizeState, prompt } =
data;
let {
client,
scope,
callbackURL,
nonce,
codeChallenge,
codeChallengeMethod,
authorizeState,
prompt
} = data;
let isLoading = $state(false);
let success = $state(false);
@@ -71,7 +79,7 @@
if (!authorizationConfirmed) {
authorizationRequired = await oidService.isAuthorizationRequired(client!.id, scope);
// If prompt=consent, always show consent UI
if (hasPromptConsent) {
authorizationRequired = true;

View File

@@ -70,10 +70,10 @@
const backgroundImagePromise =
backgroundImage === null
? appConfigService.deleteBackgroundImage()
: backgroundImage
? appConfigService.updateBackgroundImage(backgroundImage)
: Promise.resolve();
? appConfigService.deleteBackgroundImage()
: backgroundImage
? appConfigService.updateBackgroundImage(backgroundImage)
: Promise.resolve();
await Promise.all([
lightLogoPromise,

View File

@@ -33,7 +33,7 @@
data-testid={`callback-url-${i + 1}`}
type="text"
inputmode="url"
autocomplete="url"
autocomplete="url"
bind:value={callbackURLs[i]}
/>
<Button

View File

@@ -139,7 +139,11 @@
</Item.Media>
<Item.Content class="min-w-52">
<Item.Title class="text-xl font-semibold">{m.passkeys()}</Item.Title>
<Item.Description>{passkeys.length > 0 ? m.manage_this_users_passkeys() : m.user_has_no_passkeys_yet()}</Item.Description>
<Item.Description
>{passkeys.length > 0
? m.manage_this_users_passkeys()
: m.user_has_no_passkeys_yet()}</Item.Description
>
</Item.Content>
</Item.Root>
{#if passkeys.length > 0}

View File

@@ -21,7 +21,7 @@ export default defineConfig((mode) => {
disable: mode.isPreview,
algorithm: 'gzip',
ext: '.gz',
filter: /\.(js|mjs|json|css)$/i
filter: /\.(js|mjs|json|css)$/i
}),
// Create brotli-compressed files
@@ -29,7 +29,7 @@ export default defineConfig((mode) => {
disable: mode.isPreview,
algorithm: 'brotliCompress',
ext: '.br',
filter: /\.(js|mjs|json|css)$/i
filter: /\.(js|mjs|json|css)$/i
})
],