Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
bad3812
add storage summary page
matthewpeterkort Jun 25, 2026
6a0acd1
add path fields to storage creation page
matthewpeterkort Jun 26, 2026
211a196
add lfs tag to get requests since it no longer is a default in gecko
matthewpeterkort Jun 26, 2026
0538c98
Merge branch 'feature/cbds' into feature/storage-summary
matthewpeterkort Jun 26, 2026
d4548bd
update storage page audit to be better ux for user
matthewpeterkort Jul 1, 2026
e016ac4
fix build error
matthewpeterkort Jul 1, 2026
a28985c
fix build error
matthewpeterkort Jul 1, 2026
b4b2c88
fix build error
matthewpeterkort Jul 1, 2026
e14da4e
update expired jwt to cause session timeout
matthewpeterkort Jul 2, 2026
fe0a04e
remove hardcoded audit states
matthewpeterkort Jul 3, 2026
8c9a8d1
update audit page
matthewpeterkort Jul 3, 2026
c78ae7c
update frontend to work correctly with gecko to diagnose issues and r…
matthewpeterkort Jul 3, 2026
0d2b7b3
make storage tab legible
matthewpeterkort Jul 4, 2026
fd2e258
cosmetic upgrades to storage page
matthewpeterkort Jul 4, 2026
a1b2bc3
use new gecko file api
matthewpeterkort Jul 4, 2026
82c0797
fix audit no populating download statistics
matthewpeterkort Jul 4, 2026
c6b96be
fix small cosmetic issues
matthewpeterkort Jul 5, 2026
b19eaac
cleanup audit report
matthewpeterkort Jul 7, 2026
d1ec1bc
update frontend
matthewpeterkort Jul 8, 2026
c0f1cd5
cleanup cosmetics of frontend
matthewpeterkort Jul 8, 2026
debe4ea
fix duplicate session login when logged out
matthewpeterkort Jul 10, 2026
f10bc59
add support for deleting syfon records that yield bucket probe errors…
matthewpeterkort Jul 10, 2026
fb8daf8
improve tree render times
matthewpeterkort Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 99 additions & 6 deletions packages/core/src/features/gecko/geckoApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CoreState } from '../../reducers';
import { resourcePathFromProjectID } from '../submission/authMappingUtils';
import { gen3Api } from '../gen3';
import { selectCSRFToken } from '../user/userSliceRTK';
import { handleUnauthorizedStatus } from '../user/unauthorized';
import { getCookie } from 'cookies-next';

export interface GeckoProjectRecord {
Expand Down Expand Up @@ -272,6 +273,18 @@ export interface GeckoGitTreeResponse {
readonly project_id: string;
readonly ref: string;
readonly path: string;
readonly entry_count: number;
readonly truncated?: boolean;
readonly entries: Array<GeckoGitTreeEntry>;
}

export interface GeckoGitManifestResponse {
readonly project_id: string;
readonly ref: string;
readonly path: string;
readonly entry_count: number;
readonly has_more: boolean;
readonly next_cursor?: string;
readonly entries: Array<GeckoGitTreeEntry>;
}

Expand Down Expand Up @@ -426,6 +439,7 @@ const blobToDataURL = async (blob: Blob): Promise<string> =>
});

const responseErrorMessage = async (response: Response): Promise<string> => {
handleUnauthorizedStatus(response.status);
const text = await response.text();
return (
geckoErrorMessage(text) ||
Expand Down Expand Up @@ -995,6 +1009,11 @@ export const geckoApi = geckoTaggedApi.injectEndpoints({
project: string;
path?: string;
ref?: string;
include_size?: boolean;
include_last_modified?: boolean;
include_lfs_pointer?: boolean;
view?: 'manifest';
limit?: number;
}
>({
query: ({
Expand All @@ -1004,19 +1023,41 @@ export const geckoApi = geckoTaggedApi.injectEndpoints({
project,
path,
ref,
include_size,
include_last_modified,
include_lfs_pointer,
view,
limit,
}) => {
const normalizedPath = path?.trim().replace(/^\/+|\/+$/g, '');
const params = new URLSearchParams();
const queryParams = new URLSearchParams();
if (ref) {
params.set('ref', ref);
queryParams.set('ref', ref);
}
if (includeLFSPointer) {
params.set('include_lfs_pointer', 'true');
if (typeof include_size === 'boolean') {
queryParams.set('include_size', String(include_size));
}
if (typeof include_last_modified === 'boolean') {
queryParams.set('include_last_modified', String(include_last_modified));
}
if (includeLastModified) {
params.set('include_last_modified', 'true');
queryParams.set('include_last_modified', 'true');
}
if (typeof include_lfs_pointer === 'boolean') {
queryParams.set('include_lfs_pointer', String(include_lfs_pointer));
}
if (includeLFSPointer) {
queryParams.set('include_lfs_pointer', 'true');
}
const query = params.toString() ? `?${params.toString()}` : '';
if (view) {
queryParams.set('view', view);
}
if (typeof limit === 'number') {
queryParams.set('limit', String(limit));
}
const query = queryParams.toString()
? `?${queryParams.toString()}`
: '';
const suffix = normalizedPath
? `/tree/${normalizedPath
.split('/')
Expand Down Expand Up @@ -1072,6 +1113,58 @@ export const geckoApi = geckoTaggedApi.injectEndpoints({
credentials: 'include',
}),
}),
getGeckoGitProjectManifest: builder.query<
GeckoGitManifestResponse,
{
organization: string;
project: string;
path?: string;
ref?: string;
cursor?: string;
files_only?: boolean;
limit?: number;
}
>({
query: ({
organization,
project,
path,
ref,
cursor,
files_only,
limit,
}) => {
const normalizedPath = path?.trim().replace(/^\/+|\/+$/g, '');
const queryParams = new URLSearchParams();
if (ref) {
queryParams.set('ref', ref);
}
if (cursor) {
queryParams.set('cursor', cursor);
}
if (typeof files_only === 'boolean') {
queryParams.set('files_only', String(files_only));
}
if (typeof limit === 'number') {
queryParams.set('limit', String(limit));
}
const query = queryParams.toString()
? `?${queryParams.toString()}`
: '';
const suffix = normalizedPath
? `/manifest/${normalizedPath
.split('/')
.map((segment) => encodeURIComponent(segment))
.join('/')}${query}`
: `/manifest${query}`;

return {
url: buildGitProjectApiPath(organization, project, suffix),
method: 'GET',
credentials: 'include',
};
},
}),
getGeckoGitUploadSession: builder.query<
GeckoGitUploadSessionResponse,
{
Expand Down
54 changes: 35 additions & 19 deletions packages/core/src/features/gen3/gen3Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { fetchBaseQuery, createApi } from '@reduxjs/toolkit/query/react';
import { GEN3_API } from '../../constants';
import { CoreState } from '../../reducers';
import { selectCSRFToken } from '../user/userSliceRTK';
import { handleUnauthorizedStatus } from '../user/unauthorized';
import { getCookie } from 'cookies-next';

/**
Expand All @@ -12,27 +13,42 @@ import { getCookie } from 'cookies-next';
* @param endpoints - Base API endpoints that should exist in every slice
* @returns: The generated base API
*/
const rawBaseQuery = fetchBaseQuery({
baseUrl: `${GEN3_API}`,
prepareHeaders: (headers, { getState }) => {
const csrfToken = selectCSRFToken(getState() as CoreState);
headers.set('Content-Type', 'application/json');
if (process.env.NODE_ENV === 'development') {
// NOTE: This cookie can only be accessed from the client side
// in development mode. Otherwise, the cookie is set as httpOnly
const accessToken = getCookie('credentials_token');
if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`);
}
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);

return headers;
},
validateStatus: (response) => {
return response.status >= 200 && response.status < 300;
},
});

export const gen3Api = createApi({
reducerPath: 'gen3Services',
baseQuery: fetchBaseQuery({
baseUrl: `${GEN3_API}`,
prepareHeaders: (headers, { getState }) => {
const csrfToken = selectCSRFToken(getState() as CoreState);
headers.set('Content-Type', 'application/json');
if (process.env.NODE_ENV === 'development') {
// NOTE: This cookie can only be accessed from the client side
// in development mode. Otherwise, the cookie is set as httpOnly
const accessToken = getCookie('credentials_token');
if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`);
}
if (csrfToken) headers.set('X-CSRF-Token', csrfToken);

return headers;
},
validateStatus: (response) => {
return response.status >= 200 && response.status < 300;
},
}),
baseQuery: async (args, api, extraOptions) => {
const result = await rawBaseQuery(args, api, extraOptions);
if ('error' in result) {
handleUnauthorizedStatus(
typeof result.error === 'object' &&
result.error !== null &&
'status' in result.error &&
typeof result.error.status === 'number'
? result.error.status
: undefined,
);
}
return result;
},
endpoints: () => ({}),
});

Expand Down
39 changes: 5 additions & 34 deletions packages/core/src/features/gen3Apps/Gen3App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,7 @@ import {
ReactReduxContextValue,
TypedUseSelectorHook,
} from 'react-redux';
import {
FLUSH,
PAUSE,
PERSIST,
PURGE,
REGISTER,
REHYDRATE,
} from 'redux-persist';

import { registerGen3App } from './gen3AppRegistry';
import { DataStatus } from '../../dataAccess';
import { CookiesProvider } from 'react-cookie';
Expand Down Expand Up @@ -140,34 +133,12 @@ export const createAppStore = (
middleware: (getDefaultMiddleware) =>
middleware
? getDefaultMiddleware({
serializableCheck: {
ignoredActions: [
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
],
},
immutableCheck: {
warnAfter: 128,
},
serializableCheck: false,
immutableCheck: false,
}).concat(middleware)
: getDefaultMiddleware({
serializableCheck: {
ignoredActions: [
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
],
},
immutableCheck: {
warnAfter: 128,
},
serializableCheck: false,
immutableCheck: false,
}),
});
type AppState = ReturnType<typeof reducers>;
Expand Down
68 changes: 37 additions & 31 deletions packages/core/src/features/gen3Apps/Gen3AppRTKQ.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,12 @@ import {
fetchBaseQuery,
} from '@reduxjs/toolkit/query/react';
import { configureStore } from '@reduxjs/toolkit';
import {
FLUSH,
PAUSE,
PERSIST,
PURGE,
REGISTER,
REHYDRATE,
} from 'redux-persist';

import { GEN3_API } from '../../constants';
import { getCookie } from 'cookies-next';
import { v5 as uuidv5 } from 'uuid';
import { GEN3_APP_NAMESPACE } from './constants';
import { handleUnauthorizedStatus } from '../user/unauthorized';

export const createAppApiForRTKQ = (
reducerPath: string,
Expand Down Expand Up @@ -55,25 +49,41 @@ export const createAppApiForRTKQ = (
}),
);

const defaultBaseQuery = fetchBaseQuery({
baseUrl: `${GEN3_API}`,
prepareHeaders: (headers) => {
headers.set('Content-Type', 'application/json');
if (process.env.NODE_ENV === 'development') {
// NOTE: This cookie can only be accessed from the client side
// in development mode. Otherwise, the cookie is set as httpOnly
const accessToken = getCookie('credentials_token');
if (accessToken)
headers.set('Authorization', `Bearer ${accessToken}`);
}

return headers;
},
});

const delegatedBaseQuery = baseQuery ?? defaultBaseQuery;
const resolvedBaseQuery: BaseQueryFn = async (args, api, extraOptions) => {
const result = await delegatedBaseQuery(args, api, extraOptions);
if ('error' in result) {
handleUnauthorizedStatus(
typeof result.error === 'object' &&
result.error !== null &&
'status' in result.error &&
typeof result.error.status === 'number'
? result.error.status
: undefined,
);
}
return result;
};

const appRTKQApi = appCreateApi({
reducerPath: reducerPath,
baseQuery:
baseQuery ??
fetchBaseQuery({
baseUrl: `${GEN3_API}`,
prepareHeaders: (headers) => {
headers.set('Content-Type', 'application/json');
if (process.env.NODE_ENV === 'development') {
// NOTE: This cookie can only be accessed from the client side
// in development mode. Otherwise, the cookie is set as httpOnly
const accessToken = getCookie('credentials_token');
if (accessToken)
headers.set('Authorization', `Bearer ${accessToken}`);
}

return headers;
},
}),
baseQuery: resolvedBaseQuery,
endpoints: () => ({}),
});

Expand All @@ -88,12 +98,8 @@ export const createAppApiForRTKQ = (
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
immutableCheck: {
warnAfter: 128,
},
serializableCheck: false,
immutableCheck: false,
}).concat(appMiddleware),
});

Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/features/grip/gripApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { GEN3_GRIP_API } from '../../constants';
import { getCookie } from 'cookies-next';
import { selectCSRFToken } from '../user';
import { CoreState } from '../../reducers';
import { handleUnauthorizedStatus } from '../user/unauthorized';

export interface gripApiResponse<H = JSONObject> {
readonly data: H;
Expand Down Expand Up @@ -79,6 +80,14 @@ export const gripApi = createApi({
const results = await gripApiFetch(request, headers);
return { data: results };
} catch (e) {
if (
e &&
typeof e === 'object' &&
'status' in e &&
typeof (e as { status?: unknown }).status === 'number'
) {
handleUnauthorizedStatus((e as { status: number }).status);
}
return { error: e };
}
},
Expand Down
Loading
Loading