Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
696d56a
Layout for application created, documentation added.
mroki58 Jun 16, 2025
1a45249
First idea for running test container. Created image with dockerfile…
mroki58 Jun 17, 2025
38f90ed
Session data taken correctly but it rerenders page again which throws…
mroki58 Jun 18, 2025
6b93174
Merge branch 'dev' into feature/TKN/OGUI-1711/implement-overall-layout
mroki58 Aug 20, 2025
6faffd1
- Added eslint configuration with established assumptions
mroki58 Aug 21, 2025
8231106
Merge remote-tracking branch 'origin/dev' into feature/TKN/OGUI-1724/…
mroki58 Aug 22, 2025
3374b5b
- Moved test files for UI to webapp folder
mroki58 Aug 22, 2025
8643941
Merge branch 'feature/TKN/OGUI-1711/implement-overall-layout' into fe…
mroki58 Aug 22, 2025
f0700da
- Proposed session checking logic with context api and hooks.
mroki58 Aug 24, 2025
5311ebf
- Proposed Tokenization project github workflow for UI tests ( eslint…
mroki58 Aug 25, 2025
494c626
Fixing components and changing routes for better using of react-route…
mroki58 Sep 3, 2025
5232c19
Double import fix
mroki58 Sep 3, 2025
ef7ecfb
Test commit for checking pushing with cuurent tokenization.yml
mroki58 Sep 3, 2025
7ae3345
npm ci => npm i because of some errors in lock file
mroki58 Sep 3, 2025
b75be95
- dependencies for building application taken from dev to normal depe…
mroki58 Sep 3, 2025
8fe9693
Actions for lint-checking in backend (waiting for guys for their chan…
mroki58 Sep 3, 2025
c2f5922
Merge branch 'feature/TKN/OGUI-1724/prepare-testing-env' into feature…
mroki58 Sep 3, 2025
05f7465
Typo correct
mroki58 Sep 3, 2025
7505c32
Updating .dockerignore
mroki58 Sep 3, 2025
d473dcf
Adjust test file
mroki58 Sep 3, 2025
4de90cd
Merge branch 'feature/TKN/OGUI-1725/session-checking' into feature/TK…
mroki58 Sep 3, 2025
a2a4648
Fixed type error in sidebar.tsx - changed wrong Props type
mroki58 Sep 19, 2025
2d69a8d
Part of token creation workflow working - selects for services and HT…
mroki58 Oct 1, 2025
7376100
Changes in create.tsx for http methods
mroki58 Oct 2, 2025
15bf271
Adjusted eslint configuration file for linting and fixing mocha tests…
mroki58 Oct 7, 2025
e7cfafa
Merge branch 'dev' into feature/TKN/OGUI-1724/prepare-testing-env
mroki58 Oct 7, 2025
948a4a0
Merge branch 'feature/TKN/OGUI-1724/prepare-testing-env' into feature…
mroki58 Oct 7, 2025
0114076
Added ui-tests for creating tokens
mroki58 Oct 7, 2025
17480d5
Use of promise.all in beforeEach test and adding jsdocs
mroki58 Oct 10, 2025
2b36cbf
package-lock fix
mroki58 Oct 10, 2025
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
48 changes: 48 additions & 0 deletions .github/workflows/tokenization.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Tokenization
on:
pull_request:
paths:
- 'Tokenization/**/*'
- '.github/workflows/tokenization.yml'
push:
branches:
- 'main'
- 'dev'

jobs:
lint-check-backend:
name: Check eslint rules for backend on ubuntu-latest
runs-on: ubuntu-latest
timeout-minutes: 6
steps:
- uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: '22.x'
- run: (cd Tokenization/backend; npm i)
lint-check-webapp:
name: Check eslint rules for webapp on ubuntu-latest
runs-on: ubuntu-latest
timeout-minutes: 6
steps:
- uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: '22.x'
- run: (cd Tokenization/webapp; npm i )
- run: (cd Tokenization/webapp; npm run typecheck)
- run: (cd Tokenization/webapp; npm run lint)

ui-test:
name: UI-tests for webapp application
runs-on: ubuntu-latest
timeout-minutes: 6
steps:
- uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: '22.x'
- run: (cd Tokenization/webapp; npm run docker:test)
2 changes: 2 additions & 0 deletions Tokenization/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.react-router/
53 changes: 53 additions & 0 deletions Tokenization/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# ---- Base ----
FROM node:22-alpine AS base
WORKDIR /var/workspace

# ---- Dependencies (for production) ----
FROM base AS dependencies
COPY webapp/package*.json ./
RUN npm ci --only=production && npm cache clean --force

# ---- Dev Dependencies (for tests) ----
FROM base AS dev-dependencies

# Installs packages required for Puppeteer
RUN apk add --no-cache \
chromium \
freetype \
freetype-dev \
harfbuzz \
ca-certificates

ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser

COPY webapp .

RUN npm --silent install

# ---- Build ----
FROM base AS build
COPY webapp/package*.json ./
RUN npm i --only=production && npm cache clean --force
COPY webapp .
RUN npm run build

# ---- Test ----
FROM dev-dependencies AS test
CMD ["npm", "run", "test"]

# ---- Coverage ----
FROM dev-dependencies AS coverage
CMD ["npm", "run", "coverage"]

# ---- Production ----
FROM nginx:alpine AS production
COPY --from=build /var/workspace/build/client /usr/share/nginx/html
COPY docker/provisioning/nginx/conf.d/production.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

FROM nginx:1.27 AS reverse-proxy
COPY ./docker/provisioning/nginx/conf.d/default.conf /etc/nginx/conf.d
EXPOSE 8080

48 changes: 48 additions & 0 deletions Tokenization/docker-compose.test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
services:
install-backend:
image: node:22-alpine
working_dir: /var/workspace
volumes:
- ./backend:/var/workspace
command: ["npm", "install", "--no-save", "--silent"]

backend:
image: node:22-alpine
working_dir: /var/workspace
volumes:
- ./backend:/var/workspace
command: ["npm", "run", "dev"]
healthcheck:
interval: 1s
test: ["CMD-SHELL",
"node", "-c",
"node --input-type=module -e \"process.exit((await fetch('http://backend:8080/api/healthcheck')).ok === true ? 0 : 1)\""]
depends_on:
install-backend:
condition: service_completed_successfully


prod-container:
build:
context: .
dockerfile: Dockerfile
target: production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/"]
interval: 5s
timeout: 3s
retries: 5
depends_on:
backend:
condition: service_healthy

ui-tests:
build:
context: .
dockerfile: Dockerfile
target: test
volumes:
- ./webapp/tests:/var/workspace/tests
depends_on:
prod-container:
condition: service_healthy
5 changes: 2 additions & 3 deletions Tokenization/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@ services:
condition: service_completed_successfully

reverse-proxy:
image: nginx:1.27
volumes:
- ./docker/provisioning/nginx/conf.d/:/etc/nginx/conf.d/
build:
target: reverse-proxy
ports:
- "8080:8080"
depends_on:
Expand Down
26 changes: 26 additions & 0 deletions Tokenization/docker/provisioning/nginx/conf.d/production.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
events {}

http {
include mime.types;
default_type application/octet-stream;
sendfile on;

server {
listen 80;
listen [::]:80;

server_name localhost;

root /usr/share/nginx/html;
index index.html;

location /api {
proxy_pass http://backend:8080;
}

location / {
try_files $uri /index.html;
}

}
}
2 changes: 1 addition & 1 deletion Tokenization/webapp/app/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@

.scale25 {
transform: scale(2.5);
}
}
97 changes: 97 additions & 0 deletions Tokenization/webapp/app/contexts/sessionContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* @license
* Copyright 2019-2020 CERN and copyright holders of ALICE O2.
* See http://alice-o2.web.cern.ch/copyright for details of the copyright holders.
* All rights not expressly granted are reserved.
*
* This software is distributed under the terms of the GNU General Public
* License v3 (GPL Version 3), copied verbatim in the file "COPYING".
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/
import React, { createContext, useState, useEffect, useMemo, useCallback } from 'react';
import { useLocation, useNavigate } from 'react-router';

interface Session {
personid: string | null;
name: string | null;
token: string | null;
username: string | null;
access: string[] | null;
}

type SessionKey = keyof Session;

const defaultSession = {
personid: null,
name: null,
token: null,
username: null,
access: null,
};

// List ["personid", "name", "token", ...]
const sessionKeys = Object.keys(defaultSession) as SessionKey[];

interface SessionContextType {
session: Session;
hasAccess: (role: string) => boolean;
}

/**
* React context for managing user session state.
* Provides session data and access control functionality.
*/
export const SessionContext = createContext<SessionContextType>({
session: defaultSession,
hasAccess: () => false,
});

/**
* Session provider component that manages user session state.
*
* Automatically extracts session data from URL parameters on mount and
* provides session context to child components.
*
* @param children - React components that need access to session context
*
* @example
* ```tsx
* <SessionProvider>
* <App />
* </SessionProvider>
* ```
*/
export const SessionProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [session, setSession] = useState<Session>(defaultSession);
const location = useLocation();
const navigate = useNavigate();

useEffect(() => {
let sessionLoad: Session = { ...defaultSession };
const params = new URLSearchParams(location.search);
for (const sessionKey of sessionKeys) {
const value = params.get(sessionKey);
if (value && sessionKey === 'access') {
sessionLoad = { ...sessionLoad, [sessionKey]: value.split(',') };
} else if (value) {
sessionLoad = { ...sessionLoad, [sessionKey]: value };
}
}
setSession(sessionLoad);
navigate(location.pathname, { replace: true });
// It should run only once when we start page
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const hasAccess = useCallback(
(role: string) => session.access?.includes(role) ?? false,
[session],
);

const value = useMemo(() => ({ session, hasAccess }), [session, hasAccess]);

return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;
};
69 changes: 69 additions & 0 deletions Tokenization/webapp/app/hooks/session.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @license
* Copyright 2019-2020 CERN and copyright holders of ALICE O2.
* See http://alice-o2.web.cern.ch/copyright for details of the copyright holders.
* All rights not expressly granted are reserved.
*
* This software is distributed under the terms of the GNU General Public
* License v3 (GPL Version 3), copied verbatim in the file "COPYING".
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

import { useContext } from 'react';

import { SessionContext } from '../contexts/sessionContext';

/**
* Custom hook to access the current user session data.
*
* @returns {Session} The current session object containing:
* - personid: User's person ID
* - name: User's display name
* - token: Authentication token
* - username: User's username
* - access: Array of user's access roles
*
* @throws {Error} If the hook is used outside of SessionProvider
*
* @example
* ```tsx
* const session = useSession();
* console.log(`Welcome, ${session.name}!`);
* ```
*/
export function useSession() {
const obj = useContext(SessionContext);
if (!obj) {
throw new Error('Session wasnt created');
}
return obj.session;
}

/**
* Custom hook to check if the current user has access to a specific role.
*
* @param {string} role - The role to check access for
* @returns {boolean} True if the user has the specified role, false otherwise
*
* @throws {Error} If the hook is used outside of SessionProvider
*
* @example
* ```tsx
* const hasAdminAccess = useAuth('admin');
* const canEditTokens = useAuth('token-editor');
*
* if (hasAdminAccess) {
* // Render admin-only content
* }
* ```
*/
export function useAuth(role: string) {
const obj = useContext(SessionContext);
if (!obj) {
throw new Error('Session wasnt created');
}
return obj.hasAccess(role);
}
Loading