@solidstarters/create-solid-app 1.2.12 → 1.2.14

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.
Files changed (34) hide show
  1. package/helpers.js +7 -8
  2. package/index.js +21 -5
  3. package/package.json +1 -1
  4. package/templates/nest-template/package-lock.json +121 -8
  5. package/templates/nest-template/package.json +2 -2
  6. package/templates/nest-template/refresh.bat +15 -0
  7. package/templates/nest-template/src/main-cli.ts +22 -3
  8. package/templates/next-template/app/admin/core/[moduleName]/[modelName]/form/[id]/page.tsx +4 -79
  9. package/templates/next-template/app/admin/core/[moduleName]/[modelName]/kanban/page.tsx +1 -6
  10. package/templates/next-template/app/admin/core/[moduleName]/[modelName]/list/page.tsx +1 -6
  11. package/templates/next-template/app/admin/layout.tsx +5 -54
  12. package/templates/next-template/app/admin/page.tsx +3 -4
  13. package/templates/next-template/app/admin/settings/app-settings/page.tsx +11 -0
  14. package/templates/next-template/app/admin/settings/authentication-settings/page.tsx +10 -0
  15. package/templates/next-template/app/admin/settings/misc-settings/page.tsx +10 -0
  16. package/templates/next-template/app/api/auth/[...nextauth]/route.ts +3 -217
  17. package/templates/next-template/app/auth/confirm-forgot-password/page.tsx +1 -6
  18. package/templates/next-template/app/auth/initiate-forgot-password-thank-you/page.tsx +2 -37
  19. package/templates/next-template/app/auth/initiate-google-oauth/page.tsx +9 -0
  20. package/templates/next-template/app/auth/initiate-login/page.tsx +13 -0
  21. package/templates/next-template/app/auth/initiate-register/page.tsx +14 -0
  22. package/templates/next-template/app/auth/layout.tsx +6 -125
  23. package/templates/next-template/app/globals.css +277 -29
  24. package/templates/next-template/app/layout.tsx +4 -5
  25. package/templates/next-template/app/solid-global.css +2 -0
  26. package/templates/next-template/next-env.d.ts +1 -1
  27. package/templates/next-template/package-lock.json +6 -6
  28. package/templates/next-template/package.json +3 -3
  29. package/templates/next-template/public/favicon.ico +0 -0
  30. package/templates/next-template/public/images/Navigation/settings.png +0 -0
  31. package/templates/next-template/public/styles/layout/_main.scss +23 -4
  32. package/templates/next-template/public/themes/solid-dark-purple/theme.css +13 -3
  33. package/templates/next-template/public/themes/solid-light-purple/theme.css +68 -15
  34. package/templates/next-template/redux/store.ts +5 -3
@@ -1,220 +1,6 @@
1
1
  import NextAuth from "next-auth";
2
- import CredentialsProvider from "next-auth/providers/credentials";
3
- import GoogleProvider from "next-auth/providers/google";
4
- import { NextRequest } from "next/server";
5
- import { jwtDecode } from "jwt-decode";
6
- import { JWT } from "next-auth/jwt";
7
- import axios from "axios";
8
- import { signOut } from "next-auth/react";
2
+ import { authProviders } from "@solidstarters/solid-core-ui";
9
3
 
10
- let isRefreshing = false;
11
- let globalToken: JWT;
4
+ const auth = NextAuth(authProviders);
12
5
 
13
- type Credentials = {
14
- username: string,
15
- email: string,
16
- password: string,
17
- accessToken: string
18
- };
19
-
20
- async function refreshAccessToken(token: any) {
21
- try {
22
- const response = await axios.post(
23
- `${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/iam/refresh-tokens`,
24
- { refreshToken: token.refreshToken }
25
- );
26
-
27
- const { accessToken, refreshToken } = response.data.data;
28
- const { exp: accessTokenExpires } = jwtDecode<{ exp: number }>(accessToken);
29
-
30
- return {
31
- ...token,
32
- accessToken,
33
- refreshToken,
34
- accessTokenExpires: accessTokenExpires * 1000, // Convert seconds to milliseconds
35
- };
36
- } catch (error: any) {
37
- console.error("Failed to refresh access token:", error.message || error.response?.data);
38
- return {
39
- // ...token,
40
- error: "RefreshAccessTokenError",
41
- };
42
- }
43
- }
44
-
45
-
46
- async function auth(req: NextRequest, res: any) {
47
- return await NextAuth(req, res, {
48
- session: {
49
- strategy: "jwt",
50
- },
51
- providers: [
52
- GoogleProvider({
53
- clientId: process.env.GOOGLE_ID!,
54
- clientSecret: process.env.GOOGLE_CLIENT_SECRET!
55
- }),
56
- CredentialsProvider({
57
- // @ts-ignore
58
- async authorize(credentials: Credentials) {
59
- const { username, email, password, accessToken } = credentials;
60
-
61
- try {
62
- if (accessToken) {
63
- let data = JSON.stringify({
64
- username: email,
65
- email: email,
66
- password: password
67
- });
68
-
69
- let config = {
70
- method: 'get',
71
- url: `${process.env.API_URL}/api/iam/me`,
72
- headers: {
73
- 'accept': '*/*',
74
- 'Content-Type': 'application/json',
75
- 'Authorization': `Bearer ${accessToken}`
76
- }
77
- };
78
-
79
-
80
- const loginResponse = await axios.request(config);
81
-
82
- if (loginResponse.status == 400) {
83
- throw new Error(loginResponse.data.message);
84
- }
85
- if (loginResponse.status == 401) {
86
- throw new Error(loginResponse.data.message);
87
- }
88
-
89
- let base64decoded = jwtDecode(loginResponse.data.data.accessToken);
90
- let accessTokenExpires = base64decoded.exp;
91
-
92
- return {
93
- accessToken: loginResponse.data.data.accessToken,
94
- refreshToken: loginResponse.data.data.refreshToken,
95
- accessTokenExpires: accessTokenExpires,
96
- ...loginResponse.data.data,
97
- };
98
-
99
- } else {
100
-
101
- let data = JSON.stringify({
102
- username: email,
103
- email: email,
104
- password: password
105
- });
106
-
107
- let config = {
108
- method: 'post',
109
- maxBodyLength: Infinity,
110
- url: `${process.env.API_URL}/api/iam/authenticate`,
111
- headers: {
112
- 'accept': '*/*',
113
- 'Content-Type': 'application/json'
114
- },
115
- data: data
116
- };
117
-
118
-
119
- const loginResponse = await axios.request(config);
120
-
121
- // const authenticateResponse = await fetch(
122
- // `${process.env.API_URL}/api/iam/authenticate`,
123
- // {
124
- // method: "Post",
125
- // headers: {
126
- // 'accept': '*/*',
127
- // "Content-Type": "application/json"
128
- // },
129
- // body: JSON.stringify({
130
- // username: email,
131
- // email: email,
132
- // password: password
133
- // })
134
- // }
135
- // );
136
-
137
- // const loginResponse = await authenticateResponse.json();
138
-
139
- // const response = await fetch(
140
- // `${process.env.API_URL}/api/iam/me`,
141
- // {
142
- // method: "Get",
143
- // headers: {
144
- // "Content-Type": "application/json",
145
- // "Authorization": `Bearer ${loginResponse.data.accessToken}`,
146
- // },
147
- // }
148
- // );
149
-
150
- // const responseData = await response.json();
151
-
152
- if (loginResponse.status == 400) {
153
- throw new Error(loginResponse.data.message);
154
- }
155
- if (loginResponse.status == 401) {
156
- throw new Error(loginResponse.data.data.message);
157
- }
158
- let base64decoded = jwtDecode(loginResponse.data.data.accessToken);
159
- let accessTokenExpires = base64decoded.exp;
160
- const returnResponse = {
161
- accessToken: loginResponse.data.data.accessToken,
162
- refreshToken: loginResponse.data.data.refreshToken,
163
- accessTokenExpires: accessTokenExpires,
164
- ...loginResponse.data.data,
165
- }
166
- return returnResponse;
167
- }
168
- } catch (error: any) {
169
- throw new Error(error.message ? error.message : error?.response?.data?.message);
170
- }
171
- },
172
- }),
173
- ],
174
- callbacks: {
175
- jwt: async ({ token, user }) => {
176
- if (Date.now() >= (token.accessTokenExpires as number)) {
177
- return refreshAccessToken(token); // Call the refresh token function
178
- }
179
-
180
- // If there is no user (first time login or session), we return the user data
181
- if (user) {
182
- const base64decoded = jwtDecode(user.accessToken);
183
- const accessTokenExpires = base64decoded.exp && base64decoded.exp * 1000; // Convert from seconds to milliseconds
184
- return {
185
- ...token,
186
- accessToken: user.accessToken,
187
- refreshToken: user.refreshToken,
188
- accessTokenExpires: accessTokenExpires,
189
- user: user, // Include the user data here
190
- };
191
- }
192
-
193
- return token; // If no update needed, just return the existing token
194
- },
195
- session: async ({ session, token }) => {
196
- const user = token.user || {}; // Default to an empty object if user is undefined or null
197
- session.error = token.error ? token.error : null;
198
- // if (token.error) {
199
- // return null
200
- // }
201
- session.user = {
202
- ...user, // Include the user info from the token
203
- accessToken: token?.accessToken, // Add the access token to session for client use
204
- refreshToken: token?.refreshToken, // Add refresh token to session for future use
205
- accessTokenExpires: token?.accessTokenExpires && token?.accessTokenExpires, // Store the expiry time
206
- };
207
- return session;
208
-
209
- },
210
- },
211
- pages: {
212
- signIn: "/auth/login",
213
- },
214
- secret: process.env.NEXTAUTH_SECRET,
215
- });
216
- }
217
-
218
-
219
-
220
- export { auth as GET, auth as POST };
6
+ export { auth as GET, auth as POST };
@@ -1,15 +1,10 @@
1
1
  "use client"
2
2
  import {SolidResetPassword} from "@solidstarters/solid-core-ui";
3
- import { useSearchParams } from "next/navigation";
4
3
 
5
4
  const ForgotPasswordPage = () => {
6
- const searchParams = useSearchParams();
7
- const token = searchParams.get('token');
8
- const userName = searchParams.get('username');
9
- const decodedUsername = userName ? decodeURIComponent(userName) : '';
10
5
  return (
11
6
  <div>
12
- <SolidResetPassword verificationToken={token} username={decodedUsername} />
7
+ <SolidResetPassword />
13
8
  </div>
14
9
  );
15
10
  };
@@ -1,45 +1,10 @@
1
1
  "use client"
2
2
 
3
- import { LayoutContext } from "@solidstarters/solid-core-ui";
4
- import Link from "next/link";
5
- import { useSearchParams } from "next/navigation";
6
- import { useContext } from "react";
3
+ import { ForgotPasswordThankYou } from "@solidstarters/solid-core-ui";
7
4
 
8
5
  const InitiateForgotPasswordThankYou = () => {
9
- const { layoutConfig } = useContext(LayoutContext);
10
- const { authLayout } = layoutConfig;
11
- const searchParams = useSearchParams();
12
- const email = searchParams.get('email');
13
- const decodedEmail = email ? decodeURIComponent(email) : '';
14
6
  return (
15
- <div className="">
16
- <div className={`auth-container ${authLayout === 'Center' ? 'center' : 'side'}`}>
17
- {authLayout === 'Center' &&
18
- <div className="flex justify-content-center">
19
- <div className="solid-logo flex align-items-center gap-3">
20
- <img
21
- alt="solid logo"
22
- src={'/images/SS-Logo-1 1.png'}
23
- className="position-relative img-fluid"
24
- />
25
- <div>
26
- <p className="solid-logo-title">
27
- Solid<br />Starters
28
- </p>
29
- </div>
30
- </div>
31
- </div>
32
- }
33
- <h5 className='text-center font-bold'>We have sent an email to yourregistered email address</h5>
34
- <p className='font-bold text-center'>{decodedEmail}</p>
35
- <p className='text-center'>
36
- Please follow the instructions in the email
37
- </p>
38
- <div className='text-center'>
39
- <Link href={"/"} className='btn'>Back to Home</Link>
40
- </div>
41
- </div>
42
- </div>
7
+ <ForgotPasswordThankYou />
43
8
  );
44
9
  };
45
10
 
@@ -0,0 +1,9 @@
1
+ import { GoogleAuthChecking } from "@solidstarters/solid-core-ui";
2
+
3
+ const InitiateGoogleOAuth = () => {
4
+ return (
5
+ <GoogleAuthChecking />
6
+ )
7
+ }
8
+
9
+ export default InitiateGoogleOAuth
@@ -0,0 +1,13 @@
1
+ "use client"
2
+ import { SolidInitialLoginOtp } from "@solidstarters/solid-core-ui";
3
+ import { useSearchParams } from "next/navigation";
4
+
5
+ const InitiateLoginConfirm = () => {
6
+ return (
7
+ <div>
8
+ <SolidInitialLoginOtp />
9
+ </div>
10
+ );
11
+ };
12
+
13
+ export default InitiateLoginConfirm;
@@ -0,0 +1,14 @@
1
+ "use client"
2
+ import { SolidInitiateRegisterOtp } from "@solidstarters/solid-core-ui";
3
+
4
+ import { useSearchParams } from "next/navigation";
5
+
6
+ const InitiateRegisterConfirm = () => {
7
+ return (
8
+ <div>
9
+ <SolidInitiateRegisterOtp />
10
+ </div>
11
+ );
12
+ };
13
+
14
+ export default InitiateRegisterConfirm;
@@ -1,129 +1,10 @@
1
- "use client"
2
- import { LayoutContext, useLazyGetAuthSettingsQuery } from "@solidstarters/solid-core-ui";
3
- import { toggleTheme } from "@solidstarters/solid-core-ui";
4
- import { LayoutConfig } from "@solidstarters/solid-core-ui";
5
- import { PrimeReactContext } from "primereact/api";
6
- import { useContext, useEffect, useState } from "react";
7
- import { useDispatch, useSelector } from "react-redux";
8
- import { usePathname, useRouter } from "next/navigation";
9
- import Link from "next/link";
10
- import { Dialog } from "primereact/dialog";
11
- import { Button } from "primereact/button";
1
+ import { AuthLayout } from "@solidstarters/solid-core-ui";
12
2
 
13
- const AuthLayout = ({ children }: { children: React.ReactNode }) => {
14
- const [trigger, { data: solidSettingsData }] = useLazyGetAuthSettingsQuery()
15
- const [allowRegistration, setAllowRegistration] = useState<boolean | null>(null);
16
- const [isRestricted, setIsRestricted] = useState(false);
17
- const pathname = usePathname();
18
- const router = useRouter();
19
-
20
- const { changeTheme } = useContext(PrimeReactContext);
21
- const { layoutConfig, setLayoutConfig } = useContext(LayoutContext);
22
- const dispatch = useDispatch();
23
- const _changeTheme = (theme: string, colorScheme: string) => {
24
- changeTheme?.(layoutConfig.theme, theme, 'theme-css', () => {
25
- setLayoutConfig((prevState: LayoutConfig) => ({ ...prevState, theme, colorScheme }));
26
- });
27
- };
28
- useEffect(() => {
29
- const theme = solidSettingsData?.data?.authPagesTheme; // 'dark' or 'light'
30
- if (theme) {
31
- dispatch(toggleTheme()); // Dispatch Redux action
32
- _changeTheme(
33
- theme === "dark" ? "solid-dark-purple" : "solid-light-purple",
34
- theme
35
- );
36
- }
37
- }, [solidSettingsData]);
38
- useEffect(() => {
39
- trigger("") // Fetch settings on mount
40
- }, [trigger])
41
-
42
- useEffect(() => {
43
- // Hardcoded as false for demonstration
44
- const iamAllowPublicRegistration = solidSettingsData?.data?.iamAllowPublicRegistration;
45
-
46
- if (!iamAllowPublicRegistration) {
47
- setAllowRegistration(false);
48
- if (pathname === "/auth/register") {
49
- setIsRestricted(true);
50
- }
51
- } else {
52
- setAllowRegistration(true);
53
- }
54
- }, [pathname]);
55
-
56
- if (allowRegistration === null) return null;
57
-
58
- const authChildren = allowRegistration || pathname !== "/auth/register" ? children : null;
59
- const handleRegistration = () => {
60
- router.push("/auth/login");
61
- setIsRestricted(false);
62
- }
3
+ const AuthLayoutContainer = ({ children }: { children: React.ReactNode }) => {
63
4
  return (
64
- <div className={`solid-auth-theme-wrapper ${solidSettingsData?.data?.authPagesLayout || 'center'}`}>
65
- <div className={`${solidSettingsData?.data?.authPagesLayout !== 'center' ? 'grid w-full h-full m-0' : ''}`}>
66
- {solidSettingsData?.data?.authPagesLayout === 'left' &&
67
- <div className='col-6 flex align-items-center justify-content-center solid-login-dark-bg'>
68
- <div className="w-full">
69
- {authChildren}
70
- </div>
71
- </div>
72
- }
73
-
74
- <div
75
- className={`${solidSettingsData?.data?.authPagesLayout !== 'center' ? 'col-6 position-relative' : ''}
76
- ${solidSettingsData?.data?.authPagesLayout === 'left' ? 'solid-left-layout' : ''}
77
- ${solidSettingsData?.data?.authPagesLayout === 'right' ? 'solid-right-layout' : ''}`.trim()}
78
- >
79
- {solidSettingsData?.data?.authPagesLayout !== 'center' &&
80
- <div className="solid-logo flex align-items-center gap-3">
81
- <img
82
- alt="solid logo"
83
- src={'/images/SS-Logo-1 1.png'}
84
- className="position-relative img-fluid"
85
- />
86
- <div>
87
- <p className="solid-logo-title">
88
- Solid<br />Starters
89
- </p>
90
- </div>
91
- </div>
92
- }
93
- </div>
94
-
95
- {solidSettingsData?.data?.authPagesLayout === 'center' && <div className="solid-center-layout">
96
- {authChildren}
97
- </div>}
98
- {solidSettingsData?.data?.authPagesLayout === 'right' &&
99
- <div className='col-6 flex align-items-center justify-content-center solid-login-dark-bg'>
100
- <div className="w-full">
101
- {authChildren}
102
- </div>
103
- </div>
104
- }
105
- </div>
106
- <div className="absolute solid-auth-footer flex align-items-center justify-content-between">
107
- <div>
108
- <p className="solid-auth-input-label text-sm m-0">Made with <svg className="mx-1" width="12px" height="12px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M1.24264 8.24264L8 15L14.7574 8.24264C15.553 7.44699 16 6.36786 16 5.24264V5.05234C16 2.8143 14.1857 1 11.9477 1C10.7166 1 9.55233 1.55959 8.78331 2.52086L8 3.5L7.21669 2.52086C6.44767 1.55959 5.28338 1 4.05234 1C1.8143 1 0 2.8143 0 5.05234V5.24264C0 6.36786 0.44699 7.44699 1.24264 8.24264Z" fill="#ff0000"></path> </g></svg> in Mumbai</p>
109
- </div>
110
- <div className="flex align-items-center gap-5">
111
- <p className="solid-auth-subtitle m-0"> <Link href={'#'}>Terms of Service</Link></p>
112
- <p className="solid-auth-subtitle m-0"> <Link href={'#'}>Privacy Policy.</Link></p>
113
- </div>
114
- </div>
115
- <Dialog
116
- visible={isRestricted}
117
- onHide={handleRegistration}
118
- header="Access Restricted"
119
- footer={<Button label="Close" onClick={handleRegistration} size="small" />}
120
- draggable={false}
121
- >
122
- <p>Sign-up is not available. Please contact the admin.</p>
123
- </Dialog>
124
- </div>
5
+ <AuthLayout>
6
+ {children}
7
+ </AuthLayout>
125
8
  )
126
9
  }
127
-
128
-
129
- export default AuthLayout
10
+ export default AuthLayoutContainer