@maka/maka-cli 5.1.44 → 5.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -1
- package/bundle/typescript/package.json +1 -1
- package/bundle/typescript/src/commands/ai/mcp.sub.cmd.js +135 -7
- package/bundle/typescript/src/commands/ai/mcp.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/commands/create.js +3 -3
- package/bundle/typescript/src/commands/create.js.map +1 -1
- package/bundle/typescript/src/commands/run/run.command.js +0 -5
- package/bundle/typescript/src/commands/run/run.command.js.map +1 -1
- package/bundle/typescript/src/generators/kits/auth/_index.d.ts +1 -0
- package/bundle/typescript/src/generators/kits/auth/_index.js +1 -0
- package/bundle/typescript/src/generators/kits/auth/_index.js.map +1 -1
- package/bundle/typescript/src/generators/kits/auth/auth0-meteor.auth.gen.d.ts +1 -0
- package/bundle/typescript/src/generators/kits/auth/auth0-meteor.auth.gen.js +704 -0
- package/bundle/typescript/src/generators/kits/auth/auth0-meteor.auth.gen.js.map +1 -0
- package/bundle/typescript/src/generators/kits/auth/basic-meteor.auth.gen.js +2 -2
- package/bundle/typescript/src/generators/kits/auth/basic-meteor.auth.gen.js.map +1 -1
- package/bundle/typescript/src/generators/kits/mui/_index.d.ts +1 -0
- package/bundle/typescript/src/generators/kits/mui/_index.js +1 -0
- package/bundle/typescript/src/generators/kits/mui/_index.js.map +1 -1
- package/bundle/typescript/src/generators/kits/mui/auth0.mui.gen.d.ts +2 -0
- package/bundle/typescript/src/generators/kits/mui/auth0.mui.gen.js +158 -0
- package/bundle/typescript/src/generators/kits/mui/auth0.mui.gen.js.map +1 -0
- package/bundle/typescript/src/generators/publish.js +2 -6
- package/bundle/typescript/src/generators/publish.js.map +1 -1
- package/bundle/typescript/src/templates/react/kit/auth/service/auth0-client.js.jsx +141 -0
- package/bundle/typescript/src/templates/react/kit/auth/service/auth0-client.js.tsx +141 -0
- package/bundle/typescript/src/templates/react/kit/auth/service/auth0-config.js.jsx +46 -0
- package/bundle/typescript/src/templates/react/kit/auth/service/auth0-config.js.tsx +54 -0
- package/bundle/typescript/src/templates/react/kit/auth/service/auth0-methods.js.jsx +152 -0
- package/bundle/typescript/src/templates/react/kit/auth/service/auth0-methods.js.tsx +152 -0
- package/bundle/typescript/src/templates/react/kit/auth/service/auth0-validator.js.jsx +143 -0
- package/bundle/typescript/src/templates/react/kit/auth/service/auth0-validator.js.tsx +158 -0
- package/bundle/typescript/src/templates/react/kit/mui/auth0/auth-callback.js.jsx +71 -0
- package/bundle/typescript/src/templates/react/kit/mui/auth0/auth-callback.js.tsx +71 -0
- package/bundle/typescript/src/templates/react/kit/mui/auth0/auth0-account-settings.js.jsx +123 -0
- package/bundle/typescript/src/templates/react/kit/mui/auth0/auth0-account-settings.js.tsx +123 -0
- package/bundle/typescript/src/templates/react/kit/mui/auth0/auth0-login-button.js.jsx +56 -0
- package/bundle/typescript/src/templates/react/kit/mui/auth0/auth0-login-button.js.tsx +74 -0
- package/bundle/typescript/src/tools/scaffold/scaffold.class.d.ts +2 -1
- package/bundle/typescript/src/tools/scaffold/scaffold.class.js +12 -6
- package/bundle/typescript/src/tools/scaffold/scaffold.class.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
import { Box, CircularProgress, Typography, Alert } from '@mui/material';
|
|
4
|
+
import AuthService from '<%= authServicePath %>';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Auth0 Callback Component
|
|
8
|
+
*
|
|
9
|
+
* This component handles the OAuth callback from Auth0.
|
|
10
|
+
* It exchanges the authorization code for tokens and logs the user into Meteor.
|
|
11
|
+
*/
|
|
12
|
+
export const AuthCallback = () => {
|
|
13
|
+
const navigate = useNavigate();
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const handleCallback = async () => {
|
|
18
|
+
try {
|
|
19
|
+
// Handle the Auth0 callback and login to Meteor
|
|
20
|
+
await AuthService.handleAuth0Callback();
|
|
21
|
+
|
|
22
|
+
// Redirect to home page after successful login
|
|
23
|
+
navigate('/', { replace: true });
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.error('Auth0 callback error:', err);
|
|
26
|
+
setError(err instanceof Error ? err.message : 'Authentication failed');
|
|
27
|
+
|
|
28
|
+
// Redirect to login page after a delay
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
navigate('/login', { replace: true });
|
|
31
|
+
}, 3000);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
handleCallback();
|
|
36
|
+
}, [navigate]);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Box
|
|
40
|
+
sx={{
|
|
41
|
+
display: 'flex',
|
|
42
|
+
flexDirection: 'column',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
justifyContent: 'center',
|
|
45
|
+
minHeight: '100vh',
|
|
46
|
+
gap: 2,
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
49
|
+
{error ? (
|
|
50
|
+
<>
|
|
51
|
+
<Alert severity="error" sx={{ maxWidth: 600 }}>
|
|
52
|
+
{error}
|
|
53
|
+
</Alert>
|
|
54
|
+
<Typography variant="body2" color="text.secondary">
|
|
55
|
+
Redirecting to login page...
|
|
56
|
+
</Typography>
|
|
57
|
+
</>
|
|
58
|
+
) : (
|
|
59
|
+
<>
|
|
60
|
+
<CircularProgress size={60} />
|
|
61
|
+
<Typography variant="h6" sx={{ mt: 2 }}>
|
|
62
|
+
Completing authentication...
|
|
63
|
+
</Typography>
|
|
64
|
+
<Typography variant="body2" color="text.secondary">
|
|
65
|
+
Please wait while we log you in.
|
|
66
|
+
</Typography>
|
|
67
|
+
</>
|
|
68
|
+
)}
|
|
69
|
+
</Box>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
import { Box, CircularProgress, Typography, Alert } from '@mui/material';
|
|
4
|
+
import AuthService from '<%= authServicePath %>';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Auth0 Callback Component
|
|
8
|
+
*
|
|
9
|
+
* This component handles the OAuth callback from Auth0.
|
|
10
|
+
* It exchanges the authorization code for tokens and logs the user into Meteor.
|
|
11
|
+
*/
|
|
12
|
+
export const AuthCallback = () => {
|
|
13
|
+
const navigate = useNavigate();
|
|
14
|
+
const [error, setError] = useState<string | null>(null);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const handleCallback = async () => {
|
|
18
|
+
try {
|
|
19
|
+
// Handle the Auth0 callback and login to Meteor
|
|
20
|
+
await AuthService.handleAuth0Callback();
|
|
21
|
+
|
|
22
|
+
// Redirect to home page after successful login
|
|
23
|
+
navigate('/', { replace: true });
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.error('Auth0 callback error:', err);
|
|
26
|
+
setError(err instanceof Error ? err.message : 'Authentication failed');
|
|
27
|
+
|
|
28
|
+
// Redirect to login page after a delay
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
navigate('/login', { replace: true });
|
|
31
|
+
}, 3000);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
handleCallback();
|
|
36
|
+
}, [navigate]);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Box
|
|
40
|
+
sx={{
|
|
41
|
+
display: 'flex',
|
|
42
|
+
flexDirection: 'column',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
justifyContent: 'center',
|
|
45
|
+
minHeight: '100vh',
|
|
46
|
+
gap: 2,
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
49
|
+
{error ? (
|
|
50
|
+
<>
|
|
51
|
+
<Alert severity="error" sx={{ maxWidth: 600 }}>
|
|
52
|
+
{error}
|
|
53
|
+
</Alert>
|
|
54
|
+
<Typography variant="body2" color="text.secondary">
|
|
55
|
+
Redirecting to login page...
|
|
56
|
+
</Typography>
|
|
57
|
+
</>
|
|
58
|
+
) : (
|
|
59
|
+
<>
|
|
60
|
+
<CircularProgress size={60} />
|
|
61
|
+
<Typography variant="h6" sx={{ mt: 2 }}>
|
|
62
|
+
Completing authentication...
|
|
63
|
+
</Typography>
|
|
64
|
+
<Typography variant="body2" color="text.secondary">
|
|
65
|
+
Please wait while we log you in.
|
|
66
|
+
</Typography>
|
|
67
|
+
</>
|
|
68
|
+
)}
|
|
69
|
+
</Box>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React, { useContext, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
Card,
|
|
5
|
+
CardContent,
|
|
6
|
+
Typography,
|
|
7
|
+
Button,
|
|
8
|
+
Alert,
|
|
9
|
+
CircularProgress,
|
|
10
|
+
} from '@mui/material';
|
|
11
|
+
import { Meteor } from 'meteor/meteor';
|
|
12
|
+
import { AuthContext } from '<%= authContextPath %>';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Auth0 Account Settings Component
|
|
16
|
+
*
|
|
17
|
+
* Displays Auth0 account status and allows linking/unlinking
|
|
18
|
+
* Auth0 account to/from the current Meteor user.
|
|
19
|
+
*/
|
|
20
|
+
export const Auth0AccountSettings = () => {
|
|
21
|
+
const auth = useContext(AuthContext);
|
|
22
|
+
const [loading, setLoading] = useState(false);
|
|
23
|
+
const [message, setMessage] = useState(null);
|
|
24
|
+
|
|
25
|
+
if (!auth?.currentUser) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const user = Meteor.user();
|
|
30
|
+
const hasAuth0Account = Boolean(user?.services?.auth0);
|
|
31
|
+
|
|
32
|
+
const handleLinkAccount = async () => {
|
|
33
|
+
setLoading(true);
|
|
34
|
+
setMessage(null);
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const success = await auth.linkAuth0Account?.();
|
|
38
|
+
if (success) {
|
|
39
|
+
setMessage({ type: 'success', text: 'Auth0 account linked successfully!' });
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
setMessage({
|
|
43
|
+
type: 'error',
|
|
44
|
+
text: error instanceof Error ? error.message : 'Failed to link Auth0 account',
|
|
45
|
+
});
|
|
46
|
+
} finally {
|
|
47
|
+
setLoading(false);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleUnlinkAccount = async () => {
|
|
52
|
+
setLoading(true);
|
|
53
|
+
setMessage(null);
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const success = await auth.unlinkAuth0Account?.();
|
|
57
|
+
if (success) {
|
|
58
|
+
setMessage({ type: 'success', text: 'Auth0 account unlinked successfully!' });
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
setMessage({
|
|
62
|
+
type: 'error',
|
|
63
|
+
text: error instanceof Error ? error.message : 'Failed to unlink Auth0 account',
|
|
64
|
+
});
|
|
65
|
+
} finally {
|
|
66
|
+
setLoading(false);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Card>
|
|
72
|
+
<CardContent>
|
|
73
|
+
<Typography variant="h6" gutterBottom>
|
|
74
|
+
Auth0 Integration
|
|
75
|
+
</Typography>
|
|
76
|
+
|
|
77
|
+
{message && (
|
|
78
|
+
<Alert severity={message.type} sx={{ mb: 2 }}>
|
|
79
|
+
{message.text}
|
|
80
|
+
</Alert>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{hasAuth0Account ? (
|
|
84
|
+
<Box>
|
|
85
|
+
<Alert severity="success" sx={{ mb: 2 }}>
|
|
86
|
+
Your account is linked with Auth0
|
|
87
|
+
</Alert>
|
|
88
|
+
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
89
|
+
Email: {user?.services?.auth0?.email || 'N/A'}
|
|
90
|
+
</Typography>
|
|
91
|
+
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
92
|
+
Name: {user?.services?.auth0?.name || 'N/A'}
|
|
93
|
+
</Typography>
|
|
94
|
+
<Button
|
|
95
|
+
variant="outlined"
|
|
96
|
+
color="error"
|
|
97
|
+
onClick={handleUnlinkAccount}
|
|
98
|
+
disabled={loading}
|
|
99
|
+
sx={{ mt: 2 }}
|
|
100
|
+
startIcon={loading ? <CircularProgress size={20} /> : undefined}
|
|
101
|
+
>
|
|
102
|
+
{loading ? 'Unlinking...' : 'Unlink Auth0 Account'}
|
|
103
|
+
</Button>
|
|
104
|
+
</Box>
|
|
105
|
+
) : (
|
|
106
|
+
<Box>
|
|
107
|
+
<Alert severity="info" sx={{ mb: 2 }}>
|
|
108
|
+
Link your account with Auth0 for easier login
|
|
109
|
+
</Alert>
|
|
110
|
+
<Button
|
|
111
|
+
variant="contained"
|
|
112
|
+
onClick={handleLinkAccount}
|
|
113
|
+
disabled={loading}
|
|
114
|
+
startIcon={loading ? <CircularProgress size={20} /> : undefined}
|
|
115
|
+
>
|
|
116
|
+
{loading ? 'Linking...' : 'Link Auth0 Account'}
|
|
117
|
+
</Button>
|
|
118
|
+
</Box>
|
|
119
|
+
)}
|
|
120
|
+
</CardContent>
|
|
121
|
+
</Card>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React, { useContext, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
Card,
|
|
5
|
+
CardContent,
|
|
6
|
+
Typography,
|
|
7
|
+
Button,
|
|
8
|
+
Alert,
|
|
9
|
+
CircularProgress,
|
|
10
|
+
} from '@mui/material';
|
|
11
|
+
import { Meteor } from 'meteor/meteor';
|
|
12
|
+
import { AuthContext } from '<%= authContextPath %>';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Auth0 Account Settings Component
|
|
16
|
+
*
|
|
17
|
+
* Displays Auth0 account status and allows linking/unlinking
|
|
18
|
+
* Auth0 account to/from the current Meteor user.
|
|
19
|
+
*/
|
|
20
|
+
export const Auth0AccountSettings: React.FC = () => {
|
|
21
|
+
const auth = useContext(AuthContext);
|
|
22
|
+
const [loading, setLoading] = useState(false);
|
|
23
|
+
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
|
|
24
|
+
|
|
25
|
+
if (!auth?.currentUser) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const user = Meteor.user();
|
|
30
|
+
const hasAuth0Account = Boolean(user?.services?.auth0);
|
|
31
|
+
|
|
32
|
+
const handleLinkAccount = async () => {
|
|
33
|
+
setLoading(true);
|
|
34
|
+
setMessage(null);
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const success = await auth.linkAuth0Account?.();
|
|
38
|
+
if (success) {
|
|
39
|
+
setMessage({ type: 'success', text: 'Auth0 account linked successfully!' });
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
setMessage({
|
|
43
|
+
type: 'error',
|
|
44
|
+
text: error instanceof Error ? error.message : 'Failed to link Auth0 account',
|
|
45
|
+
});
|
|
46
|
+
} finally {
|
|
47
|
+
setLoading(false);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleUnlinkAccount = async () => {
|
|
52
|
+
setLoading(true);
|
|
53
|
+
setMessage(null);
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const success = await auth.unlinkAuth0Account?.();
|
|
57
|
+
if (success) {
|
|
58
|
+
setMessage({ type: 'success', text: 'Auth0 account unlinked successfully!' });
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
setMessage({
|
|
62
|
+
type: 'error',
|
|
63
|
+
text: error instanceof Error ? error.message : 'Failed to unlink Auth0 account',
|
|
64
|
+
});
|
|
65
|
+
} finally {
|
|
66
|
+
setLoading(false);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Card>
|
|
72
|
+
<CardContent>
|
|
73
|
+
<Typography variant="h6" gutterBottom>
|
|
74
|
+
Auth0 Integration
|
|
75
|
+
</Typography>
|
|
76
|
+
|
|
77
|
+
{message && (
|
|
78
|
+
<Alert severity={message.type} sx={{ mb: 2 }}>
|
|
79
|
+
{message.text}
|
|
80
|
+
</Alert>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{hasAuth0Account ? (
|
|
84
|
+
<Box>
|
|
85
|
+
<Alert severity="success" sx={{ mb: 2 }}>
|
|
86
|
+
Your account is linked with Auth0
|
|
87
|
+
</Alert>
|
|
88
|
+
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
89
|
+
Email: {user?.services?.auth0?.email || 'N/A'}
|
|
90
|
+
</Typography>
|
|
91
|
+
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
92
|
+
Name: {user?.services?.auth0?.name || 'N/A'}
|
|
93
|
+
</Typography>
|
|
94
|
+
<Button
|
|
95
|
+
variant="outlined"
|
|
96
|
+
color="error"
|
|
97
|
+
onClick={handleUnlinkAccount}
|
|
98
|
+
disabled={loading}
|
|
99
|
+
sx={{ mt: 2 }}
|
|
100
|
+
startIcon={loading ? <CircularProgress size={20} /> : undefined}
|
|
101
|
+
>
|
|
102
|
+
{loading ? 'Unlinking...' : 'Unlink Auth0 Account'}
|
|
103
|
+
</Button>
|
|
104
|
+
</Box>
|
|
105
|
+
) : (
|
|
106
|
+
<Box>
|
|
107
|
+
<Alert severity="info" sx={{ mb: 2 }}>
|
|
108
|
+
Link your account with Auth0 for easier login
|
|
109
|
+
</Alert>
|
|
110
|
+
<Button
|
|
111
|
+
variant="contained"
|
|
112
|
+
onClick={handleLinkAccount}
|
|
113
|
+
disabled={loading}
|
|
114
|
+
startIcon={loading ? <CircularProgress size={20} /> : undefined}
|
|
115
|
+
>
|
|
116
|
+
{loading ? 'Linking...' : 'Link Auth0 Account'}
|
|
117
|
+
</Button>
|
|
118
|
+
</Box>
|
|
119
|
+
)}
|
|
120
|
+
</CardContent>
|
|
121
|
+
</Card>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useContext, useState } from 'react';
|
|
2
|
+
import { Button, CircularProgress } from '@mui/material';
|
|
3
|
+
import { AuthContext } from '<%= authContextPath %>';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Auth0 Login Button Component
|
|
7
|
+
*
|
|
8
|
+
* A reusable button component for Auth0 authentication.
|
|
9
|
+
* Supports both redirect and popup login flows.
|
|
10
|
+
*/
|
|
11
|
+
export const Auth0LoginButton = ({
|
|
12
|
+
method = 'redirect',
|
|
13
|
+
onLoginSuccess,
|
|
14
|
+
onLoginError,
|
|
15
|
+
children = 'Login with Auth0',
|
|
16
|
+
...buttonProps
|
|
17
|
+
}) => {
|
|
18
|
+
const auth = useContext(AuthContext);
|
|
19
|
+
const [loading, setLoading] = useState(false);
|
|
20
|
+
|
|
21
|
+
const handleLogin = async () => {
|
|
22
|
+
if (!auth) {
|
|
23
|
+
console.error('Auth context not available');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
setLoading(true);
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
if (method === 'popup') {
|
|
31
|
+
await auth.loginWithAuth0Popup?.();
|
|
32
|
+
onLoginSuccess?.();
|
|
33
|
+
} else {
|
|
34
|
+
// Redirect method - user will leave the page
|
|
35
|
+
await auth.loginWithAuth0Redirect?.();
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error('Auth0 login error:', error);
|
|
39
|
+
onLoginError?.(error);
|
|
40
|
+
} finally {
|
|
41
|
+
setLoading(false);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Button
|
|
47
|
+
variant="contained"
|
|
48
|
+
onClick={handleLogin}
|
|
49
|
+
disabled={loading || auth?.loading}
|
|
50
|
+
startIcon={loading ? <CircularProgress size={20} /> : undefined}
|
|
51
|
+
{...buttonProps}
|
|
52
|
+
>
|
|
53
|
+
{loading ? 'Logging in...' : children}
|
|
54
|
+
</Button>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React, { useContext, useState } from 'react';
|
|
2
|
+
import { Button, ButtonProps, CircularProgress } from '@mui/material';
|
|
3
|
+
import { AuthContext } from '<%= authContextPath %>';
|
|
4
|
+
|
|
5
|
+
interface Auth0LoginButtonProps extends ButtonProps {
|
|
6
|
+
/**
|
|
7
|
+
* Login method to use: 'redirect' or 'popup'
|
|
8
|
+
* Default is 'redirect'
|
|
9
|
+
*/
|
|
10
|
+
method?: 'redirect' | 'popup';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Optional callback after successful login (only for popup method)
|
|
14
|
+
*/
|
|
15
|
+
onLoginSuccess?: () => void;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Optional callback on error
|
|
19
|
+
*/
|
|
20
|
+
onLoginError?: (error: Error) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Auth0 Login Button Component
|
|
25
|
+
*
|
|
26
|
+
* A reusable button component for Auth0 authentication.
|
|
27
|
+
* Supports both redirect and popup login flows.
|
|
28
|
+
*/
|
|
29
|
+
export const Auth0LoginButton: React.FC<Auth0LoginButtonProps> = ({
|
|
30
|
+
method = 'redirect',
|
|
31
|
+
onLoginSuccess,
|
|
32
|
+
onLoginError,
|
|
33
|
+
children = 'Login with Auth0',
|
|
34
|
+
...buttonProps
|
|
35
|
+
}) => {
|
|
36
|
+
const auth = useContext(AuthContext);
|
|
37
|
+
const [loading, setLoading] = useState(false);
|
|
38
|
+
|
|
39
|
+
const handleLogin = async () => {
|
|
40
|
+
if (!auth) {
|
|
41
|
+
console.error('Auth context not available');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setLoading(true);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
if (method === 'popup') {
|
|
49
|
+
await auth.loginWithAuth0Popup?.();
|
|
50
|
+
onLoginSuccess?.();
|
|
51
|
+
} else {
|
|
52
|
+
// Redirect method - user will leave the page
|
|
53
|
+
await auth.loginWithAuth0Redirect?.();
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error('Auth0 login error:', error);
|
|
57
|
+
onLoginError?.(error as Error);
|
|
58
|
+
} finally {
|
|
59
|
+
setLoading(false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Button
|
|
65
|
+
variant="contained"
|
|
66
|
+
onClick={handleLogin}
|
|
67
|
+
disabled={loading || auth?.loading}
|
|
68
|
+
startIcon={loading ? <CircularProgress size={20} /> : undefined}
|
|
69
|
+
{...buttonProps}
|
|
70
|
+
>
|
|
71
|
+
{loading ? 'Logging in...' : children}
|
|
72
|
+
</Button>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
@@ -108,7 +108,8 @@ export declare class Scaffold {
|
|
|
108
108
|
copyTemplateDirectory({ srcPath, destPath, context, ignore, keep, framework, config }: ICopyTemplateDir): Promise<void>;
|
|
109
109
|
/**
|
|
110
110
|
* Inject content at the top of a file. If the file
|
|
111
|
-
* doesn't exist yet, create it.
|
|
111
|
+
* doesn't exist yet, create it. If the content already exists,
|
|
112
|
+
* do not inject it again.
|
|
112
113
|
*/
|
|
113
114
|
injectAtBeginningOfFile({ filePath, content, opts }: IInjectIntoFile): Promise<void>;
|
|
114
115
|
/**
|
|
@@ -429,7 +429,8 @@ export class Scaffold {
|
|
|
429
429
|
}
|
|
430
430
|
/**
|
|
431
431
|
* Inject content at the top of a file. If the file
|
|
432
|
-
* doesn't exist yet, create it.
|
|
432
|
+
* doesn't exist yet, create it. If the content already exists,
|
|
433
|
+
* do not inject it again.
|
|
433
434
|
*/
|
|
434
435
|
async injectAtBeginningOfFile({ filePath, content, opts }) {
|
|
435
436
|
if (!filePath) {
|
|
@@ -442,11 +443,16 @@ export class Scaffold {
|
|
|
442
443
|
data: content
|
|
443
444
|
});
|
|
444
445
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
446
|
+
else {
|
|
447
|
+
let fileContent = fs.readFileSync(filePath, 'utf8');
|
|
448
|
+
// Check if the content already exists in the file
|
|
449
|
+
if (!fileContent.includes(content)) {
|
|
450
|
+
fileContent = content + "\n" + fileContent;
|
|
451
|
+
fs.writeFileSync(filePath, fileContent);
|
|
452
|
+
}
|
|
453
|
+
if (opts?.verbose) {
|
|
454
|
+
Log.header(`${treeSymbol} updated file ${path.relative(process.cwd(), filePath)}`);
|
|
455
|
+
}
|
|
450
456
|
}
|
|
451
457
|
}
|
|
452
458
|
catch (e) {
|