chore: add mobile app

This commit is contained in:
2026-02-07 22:33:02 +01:00
parent db34640acc
commit e89384ad4e
29327 changed files with 3886944 additions and 12 deletions
+117
View File
@@ -0,0 +1,117 @@
import React, { createContext, useContext, useState, useEffect, type ReactNode } from 'react';
import {
getAdminToken, setAdminToken as storeAdminToken,
setAdminUsername, removeAdminUsername,
setRole as storeRole, getRole,
clearAllAuth,
} from './tokenStorage';
import { extractUsernameFromToken, extractRoleFromToken, isTokenExpired } from './jwtUtils';
export type UserRole = 'admin' | 'cabine' | 'livreur' | null;
interface AuthState {
token: string | null;
username: string | null;
role: UserRole;
isLoading: boolean;
isAuthenticated: boolean;
}
interface AuthContextType extends AuthState {
loginAdmin: (token: string, role: 'admin' | 'cabine' | 'livreur') => Promise<void>;
logout: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: ReactNode }) {
const [state, setState] = useState<AuthState>({
token: null,
username: null,
role: null,
isLoading: true,
isAuthenticated: false,
});
useEffect(() => {
const restore = async () => {
try {
const savedRole = await getRole();
if (savedRole === 'admin' || savedRole === 'cabine' || savedRole === 'livreur') {
const token = await getAdminToken();
if (token && !isTokenExpired(token)) {
const uname = extractUsernameFromToken(token);
setState({
token,
username: uname,
role: savedRole as UserRole,
isLoading: false,
isAuthenticated: true,
});
return;
}
}
await clearAllAuth();
setState({
token: null,
username: null,
role: null,
isLoading: false,
isAuthenticated: false,
});
} catch {
await clearAllAuth();
setState({
token: null,
username: null,
role: null,
isLoading: false,
isAuthenticated: false,
});
}
};
restore();
}, []);
const loginAdmin = async (token: string, role: 'admin' | 'cabine' | 'livreur') => {
const uname = extractUsernameFromToken(token);
await storeAdminToken(token);
if (uname) await setAdminUsername(uname);
await storeRole(role);
setState({
token,
username: uname,
role,
isLoading: false,
isAuthenticated: true,
});
};
const logout = async () => {
await clearAllAuth();
setState({
token: null,
username: null,
role: null,
isLoading: false,
isAuthenticated: false,
});
};
return (
<AuthContext.Provider value={{ ...state, loginAdmin, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
+36
View File
@@ -0,0 +1,36 @@
import { jwtDecode } from 'jwt-decode';
export interface JWTPayload {
client_id?: number;
user_id?: number;
username: string;
role: string;
session_id: string;
iat: number;
exp: number;
iss: string;
}
export const decodeToken = (token: string): JWTPayload | null => {
try {
return jwtDecode<JWTPayload>(token);
} catch {
return null;
}
};
export const extractUsernameFromToken = (token: string): string | null => {
const payload = decodeToken(token);
return payload?.username ?? null;
};
export const extractRoleFromToken = (token: string): string | null => {
const payload = decodeToken(token);
return payload?.role ?? null;
};
export const isTokenExpired = (token: string): boolean => {
const payload = decodeToken(token);
if (!payload) return true;
return Date.now() >= payload.exp * 1000;
};
+43
View File
@@ -0,0 +1,43 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
const TOKEN_KEY = 'token';
const ADMIN_TOKEN_KEY = 'admin_token';
const USERNAME_KEY = 'username';
const ADMIN_USERNAME_KEY = 'admin_username';
const ROLE_KEY = 'user_role';
// Client token
export const getToken = () => AsyncStorage.getItem(TOKEN_KEY);
export const setToken = (token: string) => AsyncStorage.setItem(TOKEN_KEY, token);
export const removeToken = () => AsyncStorage.removeItem(TOKEN_KEY);
// Admin/Cabine/Livreur token
export const getAdminToken = () => AsyncStorage.getItem(ADMIN_TOKEN_KEY);
export const setAdminToken = (token: string) => AsyncStorage.setItem(ADMIN_TOKEN_KEY, token);
export const removeAdminToken = () => AsyncStorage.removeItem(ADMIN_TOKEN_KEY);
// Username
export const getUsername = () => AsyncStorage.getItem(USERNAME_KEY);
export const setUsername = (username: string) => AsyncStorage.setItem(USERNAME_KEY, username);
export const removeUsername = () => AsyncStorage.removeItem(USERNAME_KEY);
// Admin username
export const getAdminUsername = () => AsyncStorage.getItem(ADMIN_USERNAME_KEY);
export const setAdminUsername = (username: string) => AsyncStorage.setItem(ADMIN_USERNAME_KEY, username);
export const removeAdminUsername = () => AsyncStorage.removeItem(ADMIN_USERNAME_KEY);
// Role
export const getRole = () => AsyncStorage.getItem(ROLE_KEY);
export const setRole = (role: string) => AsyncStorage.setItem(ROLE_KEY, role);
export const removeRole = () => AsyncStorage.removeItem(ROLE_KEY);
// Clear all auth data
export const clearAllAuth = async () => {
await AsyncStorage.multiRemove([
TOKEN_KEY,
ADMIN_TOKEN_KEY,
USERNAME_KEY,
ADMIN_USERNAME_KEY,
ROLE_KEY,
]);
};