This commit is contained in:
2025-05-06 22:38:46 +03:30
parent b78ab7d539
commit 117edf2cf1
8 changed files with 6442 additions and 32 deletions

6366
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -47,9 +47,11 @@ export function ApiKeyItem({ apiKey, onDelete }: ApiKeyItemProps) {
} }
}; };
const displayKey = isVisible const displayKey = isVisible
? apiKey.key ? apiKey.key
: apiKey.key.substring(0, 4) + "•".repeat(apiKey.key.length - 8) + apiKey.key.substring(apiKey.key.length - 4); : (apiKey.key && apiKey.key.length >= 8)
? apiKey.key.substring(0, 4) + "•".repeat(apiKey.key.length - 8) + apiKey.key.substring(apiKey.key.length - 4)
: apiKey.key || "";
return ( return (
<Card className="w-full shadow-sm"> <Card className="w-full shadow-sm">

View File

@@ -40,9 +40,9 @@ export function Header() {
return ( return (
<header> <header>
{/* Alpha Disclaimer */} {/* Beta Disclaimer */}
<div className="bg-red-500 text-white text-center py-1 text-sm"> <div className="bg-red-500 text-white text-center py-1 text-sm">
Alpha Version Report any issues to{" "} Beta Version Report any issues to{" "}
<a href="mailto:me@mahdium.ir" className="underline"> <a href="mailto:me@mahdium.ir" className="underline">
me@mahdium.ir me@mahdium.ir
</a> </a>

View File

@@ -22,7 +22,7 @@ export function Layout() {
mahdium.ir mahdium.ir
</a> </a>
</p> </p>
<p>Version 0.0.3 - Alpha</p> <p>Version 0.1.3 - Beta</p>
</div> </div>
</footer> </footer>
<Toaster position="top-right" /> <Toaster position="top-right" />

View File

@@ -3,12 +3,12 @@ import { toast } from 'sonner';
import { getToken, clearToken } from './auth'; import { getToken, clearToken } from './auth';
// Set up API base URLs // Set up API base URLs
const AUTH_API_URL = 'https://io-a.monasefloadbalancer.ir/auth'; const USER_DATA_API_URL = 'https://io-userdata.monasefloadbalancer.ir';
const DATA_API_URL = 'https://io-e-cf.monasefloadbalancer.ir/api/user'; const DATA_API_URL = 'https://io-data.monasefloadbalancer.ir/api';
// Create axios instances // Create axios instances
const authApi = axios.create({ const userDataApi = axios.create({
baseURL: AUTH_API_URL, baseURL: USER_DATA_API_URL,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
@@ -33,6 +33,17 @@ dataApi.interceptors.request.use(
(error) => Promise.reject(error) (error) => Promise.reject(error)
); );
userDataApi.interceptors.request.use(
(config) => {
const token = getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Add response interceptor to handle unauthorized errors // Add response interceptor to handle unauthorized errors
const handleUnauthorized = (error: any) => { const handleUnauthorized = (error: any) => {
if (error.response && error.response.status === 401) { if (error.response && error.response.status === 401) {
@@ -45,12 +56,12 @@ const handleUnauthorized = (error: any) => {
}; };
dataApi.interceptors.response.use((response) => response, handleUnauthorized); dataApi.interceptors.response.use((response) => response, handleUnauthorized);
authApi.interceptors.response.use((response) => response, handleUnauthorized); userDataApi.interceptors.response.use((response) => response, handleUnauthorized);
// Auth API functions // Auth API functions
export const registerUser = async (username: string, password: string, email: string) => { export const registerUser = async (username: string, password: string, email: string) => {
try { try {
const response = await authApi.post('/register', { username, password, email }); const response = await userDataApi.post('/auth/register', { username, password, email });
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
throw error.response?.data || { success: false, message: 'Network error' }; throw error.response?.data || { success: false, message: 'Network error' };
@@ -59,7 +70,7 @@ export const registerUser = async (username: string, password: string, email: st
export const loginUser = async (username: string, password: string) => { export const loginUser = async (username: string, password: string) => {
try { try {
const response = await authApi.post('/login', { username, password }); const response = await userDataApi.post('/auth/login', { username, password });
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
throw error.response?.data || { success: false, message: 'Network error' }; throw error.response?.data || { success: false, message: 'Network error' };
@@ -69,16 +80,44 @@ export const loginUser = async (username: string, password: string) => {
// Data API functions // Data API functions
export const getAllFeeds = async () => { export const getAllFeeds = async () => {
try { try {
const response = await dataApi.get('/GetAllFeeds'); const response = await userDataApi.get('/api/getallfeeds');
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
throw error.response?.data || { success: false, message: 'Network error' }; throw error.response?.data || { success: false, message: 'Network error' };
} }
}; };
export const addNewFeed = async () => { export const updateFeed = async (feedId: string, name: string, isPublic: boolean) => {
const token = getToken();
if (!token) throw new Error('Authentication required');
const response = await userDataApi.patch('/api/updatefeed', {
feedId,
Name: name,
IsPublic: isPublic,
}, {
headers: {
Authorization: `Bearer ${token}`
}
});
return response.data;
};
export const addNewFeed = async (name: string, isPublic: boolean) => {
const token = getToken();
if (!token) throw new Error('Authentication required');
try { try {
const response = await dataApi.get('/AddNewFeed'); const response = await userDataApi.post('/api/addnewfeed', {
Name: name,
IsPublic: isPublic
}, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
throw error.response?.data || { success: false, message: 'Network error' }; throw error.response?.data || { success: false, message: 'Network error' };
@@ -87,7 +126,7 @@ export const addNewFeed = async () => {
export const deleteFeed = async (feedId: string) => { export const deleteFeed = async (feedId: string) => {
try { try {
const response = await dataApi.delete('/DeleteFeed', { const response = await userDataApi.delete('/api/deletefeed', {
data: { feedId } data: { feedId }
}); });
return response.data; return response.data;
@@ -102,7 +141,7 @@ export const getFeedDataTimeRange = async (
endTime: string endTime: string
) => { ) => {
try { try {
const response = await dataApi.get(`/GetFeedDataTimeRange/${feedId}/${startTime}/${endTime}`); const response = await dataApi.get(`/dash/getfeeddatatimerange/${feedId}/${startTime}/${endTime}`);
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
throw error.response?.data || { success: false, message: 'Network error' }; throw error.response?.data || { success: false, message: 'Network error' };
@@ -117,7 +156,7 @@ export const createApiKey = async () => {
throw new Error('Authentication required'); throw new Error('Authentication required');
} }
const response = await authApi.get('/createapikey', { const response = await userDataApi.get('/api/createapikey', {
headers: { headers: {
Authorization: `Bearer ${token}` Authorization: `Bearer ${token}`
} }
@@ -135,7 +174,7 @@ export const getUserApiKeys = async () => {
throw new Error('Authentication required'); throw new Error('Authentication required');
} }
const response = await authApi.get('/getuserapikeys', { const response = await userDataApi.get('/api/getuserapikeys', {
headers: { headers: {
Authorization: `Bearer ${token}` Authorization: `Bearer ${token}`
} }
@@ -153,7 +192,7 @@ export const deleteApiKey = async (ApiKey: string) => {
throw new Error('Authentication required'); throw new Error('Authentication required');
} }
const response = await authApi.delete('/deleteapikey', { const response = await userDataApi.delete('/api/deleteapikey', {
headers: { headers: {
Authorization: `Bearer ${token}` Authorization: `Bearer ${token}`
}, },
@@ -168,7 +207,7 @@ export const deleteApiKey = async (ApiKey: string) => {
export const verifyEmail = async (token: string) => { export const verifyEmail = async (token: string) => {
try { try {
// Call the verify email endpoint with no extra headers or data // Call the verify email endpoint with no extra headers or data
const response = await authApi.get(`/verifyemail/${token}`); const response = await userDataApi.get(`/auth/verifyemail/${token}`);
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
throw error.response?.data || { success: false, message: 'Network error' }; throw error.response?.data || { success: false, message: 'Network error' };

View File

@@ -25,7 +25,7 @@ export interface ApiKeyResponse {
apiKey: string; apiKey: string;
} }
// New ApiKey Type // New ApiKey Typez
export interface ApiKey { export interface ApiKey {
createdAt: string; createdAt: string;
key: string; key: string;

View File

@@ -36,7 +36,11 @@ export function ApiKeysPage() {
try { try {
const keys = await getUserApiKeys(); const keys = await getUserApiKeys();
setApiKeys(keys); const normalizedKeys: ApiKey[] = keys.map((item: { createdAt: any; apiKey: any; }) => ({
createdAt: item.createdAt,
key: item.apiKey,
}));
setApiKeys(normalizedKeys);
} catch (err: any) { } catch (err: any) {
setError(err.message || "Failed to fetch API keys"); setError(err.message || "Failed to fetch API keys");
} finally { } finally {

View File

@@ -56,23 +56,22 @@ export function HomePage() {
<div className="grid gap-8 md:grid-cols-3"> <div className="grid gap-8 md:grid-cols-3">
<div className="flex flex-col items-center text-center p-6 rounded-lg border bg-card shadow-sm"> <div className="flex flex-col items-center text-center p-6 rounded-lg border bg-card shadow-sm">
<LineChart className="h-12 w-12 text-primary mb-4" /> <LineChart className="h-12 w-12 text-primary mb-4" />
<h3 className="text-xl font-semibold mb-2">Real-time Visualization</h3> <h3 className="text-xl font-semibold mb-2">Time-based Visualization</h3>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Monitor your IoT device data in real-time with interactive charts and customizable Monitor your IoT device data in thoughout time with interactive charts and customizable
dashboards dashboards
</p> </p>
</div> </div>
<div className="flex flex-col items-center text-center p-6 rounded-lg border bg-card shadow-sm"> <div className="flex flex-col items-center text-center p-6 rounded-lg border bg-card shadow-sm">
<ShieldCheck className="h-12 w-12 text-primary mb-4" /> <Zap className="h-12 w-12 text-primary mb-4" />
<h3 className="text-xl font-semibold mb-2">Secure Communication</h3> <h3 className="text-xl font-semibold mb-2">Lightning-Fast Data Access</h3>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Industry-standard encryption and authentication for all your IoT communications Instantly retrieve and analyze your IoT data with high-speed querying, powered by specialized time-series databases optimized for rapid communication and real-time performance. </p>
</p>
</div> </div>
<div className="flex flex-col items-center text-center p-6 rounded-lg border bg-card shadow-sm"> <div className="flex flex-col items-center text-center p-6 rounded-lg border bg-card shadow-sm">
<Zap className="h-12 w-12 text-primary mb-4" /> <ShieldCheck className="h-12 w-12 text-primary mb-4" />
<h3 className="text-xl font-semibold mb-2">Simple Integration</h3> <h3 className="text-xl font-semibold mb-2">Simple Integration</h3>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Easy-to-use API with comprehensive documentation for quick integration with any device Easy-to-use API with comprehensive documentation for quick integration with any device