subaya 1.0.0 → 1.0.2
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/README.md +54 -118
- package/dist/commands/axios/AxiosGeneratorCommand.d.ts.map +1 -1
- package/dist/commands/axios/AxiosGeneratorCommand.js +97 -17
- package/dist/commands/axios/AxiosGeneratorCommand.js.map +1 -1
- package/dist/commands/provider/ProviderGeneratorCommand.d.ts +2 -1
- package/dist/commands/provider/ProviderGeneratorCommand.d.ts.map +1 -1
- package/dist/commands/provider/ProviderGeneratorCommand.js.map +1 -1
- package/dist/commands/route/NextRouteGeneratorCommand.js +1 -1
- package/dist/commands/route/NextRouteGeneratorCommand.js.map +1 -1
- package/dist/commands/supabase/SupabaseCommand.d.ts +2 -1
- package/dist/commands/supabase/SupabaseCommand.d.ts.map +1 -1
- package/dist/commands/supabase/SupabaseCommand.js +97 -25
- package/dist/commands/supabase/SupabaseCommand.js.map +1 -1
- package/dist/core/EnsureSystem.d.ts +3 -0
- package/dist/core/EnsureSystem.d.ts.map +1 -1
- package/dist/core/EnsureSystem.js +16 -0
- package/dist/core/EnsureSystem.js.map +1 -1
- package/dist/core/FileSystemService.d.ts +1 -1
- package/dist/core/FileSystemService.d.ts.map +1 -1
- package/dist/core/FileSystemService.js +8 -0
- package/dist/core/FileSystemService.js.map +1 -1
- package/dist/template/axios/instance.d.ts +3 -1
- package/dist/template/axios/instance.d.ts.map +1 -1
- package/dist/template/axios/instance.js +204 -19
- package/dist/template/axios/instance.js.map +1 -1
- package/dist/template/axios/nextServerJwt.d.ts +4 -0
- package/dist/template/axios/nextServerJwt.d.ts.map +1 -0
- package/dist/template/axios/nextServerJwt.js +60 -0
- package/dist/template/axios/nextServerJwt.js.map +1 -0
- package/dist/template/axios/protectedProxy.d.ts +2 -0
- package/dist/template/axios/protectedProxy.d.ts.map +1 -0
- package/dist/template/axios/protectedProxy.js +40 -0
- package/dist/template/axios/protectedProxy.js.map +1 -0
- package/dist/template/axios/useLoginHook.d.ts +2 -0
- package/dist/template/axios/useLoginHook.d.ts.map +1 -0
- package/dist/template/axios/useLoginHook.js +16 -0
- package/dist/template/axios/useLoginHook.js.map +1 -0
- package/dist/template/axios/useLogoutHook.d.ts +2 -0
- package/dist/template/axios/useLogoutHook.d.ts.map +1 -0
- package/dist/template/axios/useLogoutHook.js +15 -0
- package/dist/template/axios/useLogoutHook.js.map +1 -0
- package/dist/template/axios/utilJwt.d.ts +2 -0
- package/dist/template/axios/utilJwt.d.ts.map +1 -0
- package/dist/template/axios/utilJwt.js +52 -0
- package/dist/template/axios/utilJwt.js.map +1 -0
- package/dist/template/supabase/actions/auth.d.ts +2 -0
- package/dist/template/supabase/actions/auth.d.ts.map +1 -0
- package/dist/template/supabase/actions/auth.js +61 -0
- package/dist/template/supabase/actions/auth.js.map +1 -0
- package/dist/template/supabase/actions/crud.d.ts +3 -0
- package/dist/template/supabase/actions/crud.d.ts.map +1 -0
- package/dist/template/supabase/actions/crud.js +72 -0
- package/dist/template/supabase/actions/crud.js.map +1 -0
- package/dist/template/supabase/actions/user.d.ts +2 -0
- package/dist/template/supabase/actions/user.d.ts.map +1 -0
- package/dist/template/supabase/actions/user.js +36 -0
- package/dist/template/supabase/actions/user.js.map +1 -0
- package/dist/template/supabase/admin/supabaseAdminLayout.d.ts +2 -0
- package/dist/template/supabase/admin/supabaseAdminLayout.d.ts.map +1 -0
- package/dist/template/supabase/admin/supabaseAdminLayout.js +7 -0
- package/dist/template/supabase/admin/supabaseAdminLayout.js.map +1 -0
- package/dist/template/supabase/admin/supabaseAdminLoginLayout.d.ts +2 -0
- package/dist/template/supabase/admin/supabaseAdminLoginLayout.d.ts.map +1 -0
- package/dist/template/supabase/admin/supabaseAdminLoginLayout.js +114 -0
- package/dist/template/supabase/admin/supabaseAdminLoginLayout.js.map +1 -0
- package/dist/template/supabase/proxy/adminProxy.d.ts +2 -0
- package/dist/template/supabase/proxy/adminProxy.d.ts.map +1 -0
- package/dist/template/supabase/proxy/adminProxy.js +45 -0
- package/dist/template/supabase/proxy/adminProxy.js.map +1 -0
- package/dist/template/supabase/proxy/authAdminProxy.d.ts +2 -0
- package/dist/template/supabase/proxy/authAdminProxy.d.ts.map +1 -0
- package/dist/template/supabase/proxy/authAdminProxy.js +61 -0
- package/dist/template/supabase/proxy/authAdminProxy.js.map +1 -0
- package/dist/template/supabase/proxy/authProxy.d.ts +2 -0
- package/dist/template/supabase/proxy/authProxy.d.ts.map +1 -0
- package/dist/template/supabase/proxy/authProxy.js +49 -0
- package/dist/template/supabase/proxy/authProxy.js.map +1 -0
- package/dist/template/supabase/route/googleCallback.d.ts +2 -0
- package/dist/template/supabase/route/googleCallback.d.ts.map +1 -0
- package/dist/template/supabase/route/googleCallback.js +25 -0
- package/dist/template/supabase/route/googleCallback.js.map +1 -0
- package/dist/template/supabase/schemas/user.d.ts +2 -0
- package/dist/template/supabase/schemas/user.d.ts.map +1 -0
- package/dist/template/supabase/schemas/user.js +14 -0
- package/dist/template/supabase/schemas/user.js.map +1 -0
- package/dist/template/supabase/utilBase.d.ts +1 -1
- package/dist/template/supabase/utilBase.d.ts.map +1 -1
- package/dist/template/supabase/utilBase.js +2 -2
- package/dist/types/BaseExtensionCommands.d.ts +2 -1
- package/dist/types/BaseExtensionCommands.d.ts.map +1 -1
- package/dist/types/BaseExtensionCommands.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const nextServerAuthLoginRoute = () => `import { instance } from "@/configs/axios/instance"
|
|
2
|
+
import { AUTH_ENDPOINTS, setTokens } from "@/utils/jwt"
|
|
3
|
+
import { NextResponse } from "next/server"
|
|
4
|
+
|
|
5
|
+
export async function POST(request: Request) {
|
|
6
|
+
try {
|
|
7
|
+
const body = await request.json()
|
|
8
|
+
|
|
9
|
+
const response = await instance.post(AUTH_ENDPOINTS.LOGIN, body)
|
|
10
|
+
|
|
11
|
+
const { accessToken, refreshToken, ...userData } = response.data
|
|
12
|
+
|
|
13
|
+
await setTokens(accessToken, refreshToken)
|
|
14
|
+
|
|
15
|
+
return NextResponse.json({ success: true, user: userData })
|
|
16
|
+
|
|
17
|
+
} catch (error: any) {
|
|
18
|
+
const status = error.response?.status || 500
|
|
19
|
+
const errorData = error.response?.data || { message: 'Login failed' }
|
|
20
|
+
|
|
21
|
+
return NextResponse.json(errorData, { status })
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
export const nextServerAuthRefreshRoute = () => `import { instance } from "@/configs/axios/instance"
|
|
26
|
+
import { AUTH_ENDPOINTS, getTokens, setTokens, clearTokens } from "@/utils/jwt"
|
|
27
|
+
import { NextResponse } from "next/server"
|
|
28
|
+
|
|
29
|
+
export async function POST() {
|
|
30
|
+
const { refreshToken } = await getTokens()
|
|
31
|
+
|
|
32
|
+
if (!refreshToken) {
|
|
33
|
+
return NextResponse.json({ message: 'No refresh token' }, { status: 401 })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const response = await instance.post(AUTH_ENDPOINTS.REFRESH, {
|
|
38
|
+
refresh: refreshToken
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const { accessToken, refreshToken: newRefreshToken } = response.data
|
|
42
|
+
|
|
43
|
+
await setTokens(accessToken, newRefreshToken)
|
|
44
|
+
|
|
45
|
+
return NextResponse.json({ success: true })
|
|
46
|
+
} catch {
|
|
47
|
+
await clearTokens()
|
|
48
|
+
return NextResponse.json({ message: 'Refresh failed' }, { status: 401 })
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
export const nextServerAuthLogoutRoute = () => `import { NextResponse } from 'next/server'
|
|
53
|
+
import { clearTokens } from '@/utils/jwt'
|
|
54
|
+
|
|
55
|
+
export async function POST() {
|
|
56
|
+
await clearTokens()
|
|
57
|
+
return NextResponse.json({ success: true })
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
//# sourceMappingURL=nextServerJwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextServerJwt.js","sourceRoot":"","sources":["../../../src/template/axios/nextServerJwt.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;CAuB7C,CAAA;AAED,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B/C,CAAA;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,EAAE,CAAC;;;;;;;CAO9C,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protectedProxy.d.ts","sourceRoot":"","sources":["../../../src/template/axios/protectedProxy.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,cAsC1B,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const protectedProxy = () => `import { NextResponse } from 'next/server';
|
|
2
|
+
import type { NextRequest } from 'next/server';
|
|
3
|
+
import { TOKEN_KEYS } from '@/utils/jwt';
|
|
4
|
+
import { PROTECTED_ROUTES, ROUTES } from '@/generated/path/routes';
|
|
5
|
+
|
|
6
|
+
export function proxy(request: NextRequest) {
|
|
7
|
+
const { pathname } = request.nextUrl;
|
|
8
|
+
|
|
9
|
+
const isProtectedRoute = PROTECTED_ROUTES.some((route) =>
|
|
10
|
+
pathname.startsWith(route)
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
if (isProtectedRoute) {
|
|
14
|
+
const hasAccessToken = request.cookies.has(TOKEN_KEYS.ACCESS);
|
|
15
|
+
const hasRefreshToken = request.cookies.has(TOKEN_KEYS.REFRESH);
|
|
16
|
+
|
|
17
|
+
if (!hasAccessToken && !hasRefreshToken) {
|
|
18
|
+
const loginUrl = new URL(ROUTES.LOGIN, request.url);
|
|
19
|
+
loginUrl.searchParams.set('redirect', pathname);
|
|
20
|
+
return NextResponse.redirect(loginUrl);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (pathname === ROUTES.LOGIN) {
|
|
25
|
+
const hasRefreshToken = request.cookies.has(TOKEN_KEYS.REFRESH);
|
|
26
|
+
if (hasRefreshToken) {
|
|
27
|
+
return NextResponse.redirect(new URL(ROUTES.HOME, request.url));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return NextResponse.next();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const config = {
|
|
35
|
+
matcher: [
|
|
36
|
+
'/((?!api|_next/static|_next/image|favicon.ico).*)',
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
`;
|
|
40
|
+
//# sourceMappingURL=protectedProxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protectedProxy.js","sourceRoot":"","sources":["../../../src/template/axios/protectedProxy.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCnC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLoginHook.d.ts","sourceRoot":"","sources":["../../../src/template/axios/useLoginHook.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,cAexB,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const useLoginHook = () => `import { CommonErrorType, UserLoginRequestType, UserLoginType } from "@/generated/api/@types/data-contracts"
|
|
2
|
+
import { useMutation } from "@tanstack/react-query"
|
|
3
|
+
import axios, { AxiosError } from "axios"
|
|
4
|
+
|
|
5
|
+
export const useLoginMutation = () => {
|
|
6
|
+
// TODO: Change to the actual login type from the API
|
|
7
|
+
return useMutation<UserLoginType, AxiosError<CommonErrorType>, UserLoginRequestType>({
|
|
8
|
+
mutationKey: ["AUTH_JWT_LOGIN_CREATE"],
|
|
9
|
+
mutationFn: async (data) => {
|
|
10
|
+
const res = await axios.post("/api/auth/login", data)
|
|
11
|
+
return res.data
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
//# sourceMappingURL=useLoginHook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLoginHook.js","sourceRoot":"","sources":["../../../src/template/axios/useLoginHook.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GACvB,GAAG,EAAE,CAAC;;;;;;;;;;;;;;CAcP,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLogoutHook.d.ts","sourceRoot":"","sources":["../../../src/template/axios/useLogoutHook.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,cAazB,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const useLogoutHook = () => `import { CommonErrorType } from "@/generated/api/@types/data-contracts"
|
|
2
|
+
import { useMutation } from "@tanstack/react-query"
|
|
3
|
+
import axios, { AxiosError } from "axios"
|
|
4
|
+
|
|
5
|
+
export const useLogoutMutation = () => {
|
|
6
|
+
return useMutation<void, AxiosError<CommonErrorType>, void>({
|
|
7
|
+
mutationKey: ["AUTH_JWT_LOGOUT_CREATE"],
|
|
8
|
+
mutationFn: async () => {
|
|
9
|
+
const res = await axios.post("/api/auth/logout")
|
|
10
|
+
return res.data
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
//# sourceMappingURL=useLogoutHook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLogoutHook.js","sourceRoot":"","sources":["../../../src/template/axios/useLogoutHook.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC;;;;;;;;;;;;;CAalC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilJwt.d.ts","sourceRoot":"","sources":["../../../src/template/axios/utilJwt.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,cAkDnB,CAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const utilJwt = () => `import { cookies } from "next/headers"
|
|
2
|
+
|
|
3
|
+
export const AUTH_ENDPOINTS = {
|
|
4
|
+
LOGIN: "/v1/user/login/", // TODO: Change to the actual login endpoint
|
|
5
|
+
REFRESH: "/v1/user/refresh/",
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const TOKEN_KEYS = {
|
|
9
|
+
ACCESS: "accessToken",
|
|
10
|
+
REFRESH: "refreshToken",
|
|
11
|
+
} as const
|
|
12
|
+
|
|
13
|
+
const cookieOptions = {
|
|
14
|
+
httpOnly: true,
|
|
15
|
+
secure: process.env.NODE_ENV === "production",
|
|
16
|
+
sameSite: process.env.NODE_ENV === "production" ? "lax" : "none",
|
|
17
|
+
path: "/",
|
|
18
|
+
} as const
|
|
19
|
+
|
|
20
|
+
export async function setTokens(accessToken: string, refreshToken?: string) {
|
|
21
|
+
const cookieStore = await cookies()
|
|
22
|
+
|
|
23
|
+
cookieStore.set(TOKEN_KEYS.ACCESS, accessToken, {
|
|
24
|
+
...cookieOptions,
|
|
25
|
+
maxAge: 60 * 15, // 15min
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
if (refreshToken) {
|
|
29
|
+
cookieStore.set(TOKEN_KEYS.REFRESH, refreshToken, {
|
|
30
|
+
...cookieOptions,
|
|
31
|
+
maxAge: 60 * 60 * 24 * 7, // 7days
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function clearTokens() {
|
|
37
|
+
const cookieStore = await cookies()
|
|
38
|
+
|
|
39
|
+
cookieStore.delete(TOKEN_KEYS.ACCESS)
|
|
40
|
+
cookieStore.delete(TOKEN_KEYS.REFRESH)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function getTokens() {
|
|
44
|
+
const cookieStore = await cookies()
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
accessToken: cookieStore.get(TOKEN_KEYS.ACCESS)?.value,
|
|
48
|
+
refreshToken: cookieStore.get(TOKEN_KEYS.REFRESH)?.value,
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
//# sourceMappingURL=utilJwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilJwt.js","sourceRoot":"","sources":["../../../src/template/axios/utilJwt.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkD5B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../src/template/supabase/actions/auth.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,GAAI,YAAY,OAAO,WA6DtD,CAAA"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export const supabaseAuthActions = (isNeedAuth) => `"use server"
|
|
2
|
+
|
|
3
|
+
${!isNeedAuth ? `import { ROUTES } from "@/generated/path/routes"` : 'import { ENV } from "@/configs/env"'}
|
|
4
|
+
import { createClient } from "@/lib/supabase/server"
|
|
5
|
+
import { redirect } from "next/navigation"
|
|
6
|
+
|
|
7
|
+
export async function login(formData: FormData) {
|
|
8
|
+
const supabase = await createClient()
|
|
9
|
+
const email = formData.get("email") as string
|
|
10
|
+
const password = formData.get("password") as string
|
|
11
|
+
|
|
12
|
+
const { error } = await supabase.auth.signInWithPassword({
|
|
13
|
+
email,
|
|
14
|
+
password,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
if (error) {
|
|
18
|
+
redirect(${isNeedAuth ? '"/login?message=login_failed"' : '"/"'})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
redirect(${isNeedAuth ? '"/"' : "ROUTES.ADMIN"})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
${isNeedAuth
|
|
25
|
+
? `export async function signup(formData: FormData) {
|
|
26
|
+
const supabase = await createClient()
|
|
27
|
+
const email = formData.get("email") as string
|
|
28
|
+
const password = formData.get("password") as string
|
|
29
|
+
|
|
30
|
+
const { error } = await supabase.auth.signUp({
|
|
31
|
+
email,
|
|
32
|
+
password,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
if (error) {
|
|
36
|
+
redirect("/login?message=signup_failed")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
redirect("/login?message=signup_success")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function signInWithGoogle() {
|
|
43
|
+
const supabase = await createClient()
|
|
44
|
+
const { data, error } = await supabase.auth.signInWithOAuth({
|
|
45
|
+
provider: "google",
|
|
46
|
+
options: {
|
|
47
|
+
redirectTo: \`\${ENV.DOMAIN}/api/auth/google/callback\`,
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
if (error) {
|
|
52
|
+
redirect("/login?message=signin_with_failed&provider=google")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (data.url) {
|
|
56
|
+
redirect(data.url)
|
|
57
|
+
}
|
|
58
|
+
}`
|
|
59
|
+
: ""}
|
|
60
|
+
`;
|
|
61
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/template/supabase/actions/auth.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,UAAmB,EAAE,EAAE,CAAC;;EAE1D,CAAC,UAAU,CAAC,CAAC,CAAC,kDAAkD,CAAC,CAAC,CAAC,qCAAqC;;;;;;;;;;;;;;;eAe3F,UAAU,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,KAAK;;;aAGtD,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc;;;EAI9C,UAAU;IACR,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCJ;IACE,CAAC,CAAC,EACN;CACC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../../../src/template/supabase/actions/crud.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB,GAAI,WAAW,MAAM,WAsCpD,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,WAAW,MAAM,WA8BpD,CAAA"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { capitalCase } from "change-case";
|
|
2
|
+
export const supabaseCrudService = (tableName) => `import "server-only"
|
|
3
|
+
|
|
4
|
+
import { createClient } from "@/lib/supabase/server"
|
|
5
|
+
|
|
6
|
+
const TABLE_NAME = "${tableName}"
|
|
7
|
+
|
|
8
|
+
export async function insert${capitalCase(tableName)}(title: string) {
|
|
9
|
+
const supabase = await createClient()
|
|
10
|
+
const { data, error } = await supabase
|
|
11
|
+
.from(TABLE_NAME)
|
|
12
|
+
.insert([{ title, is_completed: false }])
|
|
13
|
+
.select()
|
|
14
|
+
.single()
|
|
15
|
+
|
|
16
|
+
if (error) throw new Error(error.message)
|
|
17
|
+
return data
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function modify${capitalCase(tableName)}(id: string, isCompleted: boolean) {
|
|
21
|
+
const supabase = await createClient()
|
|
22
|
+
const { data, error } = await supabase
|
|
23
|
+
.from(TABLE_NAME)
|
|
24
|
+
.update({ is_completed: isCompleted })
|
|
25
|
+
.eq("id", id)
|
|
26
|
+
.select()
|
|
27
|
+
.single()
|
|
28
|
+
|
|
29
|
+
if (error) throw new Error(error.message)
|
|
30
|
+
return data
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function remove${capitalCase(tableName)}(id: string) {
|
|
34
|
+
const supabase = await createClient()
|
|
35
|
+
const { error } = await supabase.from(TABLE_NAME).delete().eq("id", id)
|
|
36
|
+
|
|
37
|
+
if (error) throw new Error(error.message)
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
export const supabaseCrudActions = (tableName) => `"use server"
|
|
42
|
+
|
|
43
|
+
import { insert${capitalCase(tableName)}, modify${capitalCase(tableName)}, remove${capitalCase(tableName)} } from "@/services/${tableName}.service"
|
|
44
|
+
|
|
45
|
+
export async function add${capitalCase(tableName)}Action(title: string) {
|
|
46
|
+
try {
|
|
47
|
+
const data = await insert${capitalCase(tableName)}(title)
|
|
48
|
+
return { success: true, data }
|
|
49
|
+
} catch (error: any) {
|
|
50
|
+
return { success: false, error: error.message }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function update${capitalCase(tableName)}Action(id: string, isCompleted: boolean) {
|
|
55
|
+
try {
|
|
56
|
+
const data = await modify${capitalCase(tableName)}(id, isCompleted)
|
|
57
|
+
return { success: true, data }
|
|
58
|
+
} catch (error: any) {
|
|
59
|
+
return { success: false, error: error.message }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function delete${capitalCase(tableName)}Action(id: string) {
|
|
64
|
+
try {
|
|
65
|
+
await remove${capitalCase(tableName)}(id)
|
|
66
|
+
return { success: true }
|
|
67
|
+
} catch (error: any) {
|
|
68
|
+
return { success: false, error: error.message }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
//# sourceMappingURL=crud.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud.js","sourceRoot":"","sources":["../../../../src/template/supabase/actions/crud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,SAAiB,EAAE,EAAE,CAAC;;;;sBAIpC,SAAS;;8BAED,WAAW,CAAC,SAAS,CAAC;;;;;;;;;;;;8BAYtB,WAAW,CAAC,SAAS,CAAC;;;;;;;;;;;;;8BAatB,WAAW,CAAC,SAAS,CAAC;;;;;;;CAOnD,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,SAAiB,EAAE,EAAE,CAAC;;iBAEzC,WAAW,CAAC,SAAS,CAAC,WAAW,WAAW,CAAC,SAAS,CAAC,WAAW,WAAW,CAAC,SAAS,CAAC,uBAAuB,SAAS;;2BAE9G,WAAW,CAAC,SAAS,CAAC;;+BAElB,WAAW,CAAC,SAAS,CAAC;;;;;;;8BAOvB,WAAW,CAAC,SAAS,CAAC;;+BAErB,WAAW,CAAC,SAAS,CAAC;;;;;;;8BAOvB,WAAW,CAAC,SAAS,CAAC;;kBAElC,WAAW,CAAC,SAAS,CAAC;;;;;;CAMvC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../../src/template/supabase/actions/user.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,cAkC/B,CAAA"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const supabaseUserActions = () => `"use server"
|
|
2
|
+
|
|
3
|
+
import { createClient } from "@/lib/supabase/server"
|
|
4
|
+
import { SafeUserDefaultSchema, SafeUserDTO } from "@/schemas/user"
|
|
5
|
+
import { experimental_taintObjectReference as taintObjectReference } from "react"
|
|
6
|
+
|
|
7
|
+
export async function getUserAction(): Promise<SafeUserDTO> {
|
|
8
|
+
const supabase = await createClient()
|
|
9
|
+
const { data, error } = await supabase.auth.getUser()
|
|
10
|
+
|
|
11
|
+
if (error || !data.user) {
|
|
12
|
+
throw new Error("Not authenticated user")
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
taintObjectReference(
|
|
16
|
+
"Warning: Supabase original User object is not allowed to be directly returned to the client. Must pass through DTO.",
|
|
17
|
+
data.user,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
if (data.user.user_metadata) {
|
|
21
|
+
taintObjectReference("Warning: user_metadata original object exposure risk", data.user.user_metadata)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const safeData = SafeUserDefaultSchema.parse({
|
|
25
|
+
id: data.user.id,
|
|
26
|
+
email: data.user.email,
|
|
27
|
+
phone: data.user.phone,
|
|
28
|
+
name: data.user.user_metadata?.full_name,
|
|
29
|
+
providers: data.user.app_metadata?.providers ?? ["email"],
|
|
30
|
+
avatarUrl: data.user.user_metadata?.avatar_url,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
return safeData
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
//# sourceMappingURL=user.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.js","sourceRoot":"","sources":["../../../../src/template/supabase/actions/user.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCxC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabaseAdminLayout.d.ts","sourceRoot":"","sources":["../../../../src/template/supabase/admin/supabaseAdminLayout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,gGAK/B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabaseAdminLayout.js","sourceRoot":"","sources":["../../../../src/template/supabase/admin/supabaseAdminLayout.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;CAKlC,CAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const supabaseAdminLoginLayout = "\"use client\"\n\nimport { login } from \"@/actions/auth.action\"\nimport { useState, useTransition } from \"react\"\n\nexport default function AdminLoginPage() {\n const [email, setEmail] = useState(\"\")\n const [password, setPassword] = useState(\"\")\n const [isPending, startTransition] = useTransition()\n const [error, setError] = useState<string | null>(null)\n\n const handleLogin = async (e: React.SubmitEvent<HTMLFormElement>) => {\n e.preventDefault()\n setError(null)\n\n const formData = new FormData()\n formData.set(\"email\", email)\n formData.set(\"password\", password)\n\n startTransition(async () => {\n await login(formData)\n })\n }\n\n return (\n <div className=\"flex justify-center items-center min-h-screen bg-linear-to-br from-black to-zinc-900\">\n <div className=\"rounded-2xl shadow-xl bg-zinc-900 p-8 w-full max-w-md border border-gray-700\">\n <h1 className=\"text-3xl font-bold mb-1 text-center text-white\">Login</h1>\n <p className=\"mb-6 text-zinc-500 text-center\">Login as admin</p>\n <form onSubmit={handleLogin} className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor=\"admin-email\" className=\"text-sm font-medium text-zinc-700\">\n Email\n </label>\n <input\n id=\"admin-email\"\n type=\"email\"\n value={email}\n autoComplete=\"off\"\n autoFocus\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"Enter Email\"\n required\n disabled={isPending}\n className=\"border border-gray-700 rounded-lg px-4 py-2 transition focus:outline-none focus:ring-1 ring-gray-500 bg-zinc-900 placeholder-white text-white autofill:bg-zinc-900 autofill:text-white\"\n style={{\n WebkitBoxShadow: \"0 0 0 1000px #18181b inset\",\n boxShadow: \"0 0 0 1000px #18181b inset\",\n WebkitTextFillColor: \"#fff\",\n }}\n />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor=\"admin-password\" className=\"text-sm font-medium text-zinc-700\">\n Password\n </label>\n <input\n id=\"admin-password\"\n type=\"password\"\n value={password}\n autoComplete=\"off\"\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"Enter Password\"\n required\n disabled={isPending}\n className=\"border border-gray-700 rounded-lg px-4 py-2 transition focus:outline-none focus:ring-1 ring-gray-500 bg-zinc-900 placeholder-white text-white\"\n style={{\n WebkitBoxShadow: \"0 0 0 1000px #18181b inset\",\n boxShadow: \"0 0 0 1000px #18181b inset\",\n WebkitTextFillColor: \"#fff\",\n }}\n />\n </div>\n {error && (\n <div className=\"text-red-500 text-sm mt-1 rounded bg-red-50 p-2 border border-red-100 text-center animate-pulse\">\n {error}\n </div>\n )}\n <button\n type=\"submit\"\n disabled={isPending}\n className=\"mt-2 rounded-lg bg-blue-600 hover:bg-blue-700 transition text-white font-semibold py-2 shadow text-lg disabled:opacity-60 disabled:cursor-not-allowed\"\n >\n {isPending ? (\n <span className=\"flex justify-center items-center gap-2\">\n <svg className=\"animate-spin h-5 w-5 text-white\" viewBox=\"0 0 24 24\">\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n fill=\"none\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8v4l3-3-3-3v4a8 8 0 000 16v-4l-3 3 3 3v-4a8 8 0 01-8-8z\"\n />\n </svg>\n Logging in...\n </span>\n ) : (\n \"Submit\"\n )}\n </button>\n </form>\n </div>\n </div>\n )\n}\n";
|
|
2
|
+
//# sourceMappingURL=supabaseAdminLoginLayout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabaseAdminLoginLayout.d.ts","sourceRoot":"","sources":["../../../../src/template/supabase/admin/supabaseAdminLoginLayout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,85IAgHpC,CAAA"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const supabaseAdminLoginLayout = `"use client"
|
|
2
|
+
|
|
3
|
+
import { login } from "@/actions/auth.action"
|
|
4
|
+
import { useState, useTransition } from "react"
|
|
5
|
+
|
|
6
|
+
export default function AdminLoginPage() {
|
|
7
|
+
const [email, setEmail] = useState("")
|
|
8
|
+
const [password, setPassword] = useState("")
|
|
9
|
+
const [isPending, startTransition] = useTransition()
|
|
10
|
+
const [error, setError] = useState<string | null>(null)
|
|
11
|
+
|
|
12
|
+
const handleLogin = async (e: React.SubmitEvent<HTMLFormElement>) => {
|
|
13
|
+
e.preventDefault()
|
|
14
|
+
setError(null)
|
|
15
|
+
|
|
16
|
+
const formData = new FormData()
|
|
17
|
+
formData.set("email", email)
|
|
18
|
+
formData.set("password", password)
|
|
19
|
+
|
|
20
|
+
startTransition(async () => {
|
|
21
|
+
await login(formData)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="flex justify-center items-center min-h-screen bg-linear-to-br from-black to-zinc-900">
|
|
27
|
+
<div className="rounded-2xl shadow-xl bg-zinc-900 p-8 w-full max-w-md border border-gray-700">
|
|
28
|
+
<h1 className="text-3xl font-bold mb-1 text-center text-white">Login</h1>
|
|
29
|
+
<p className="mb-6 text-zinc-500 text-center">Login as admin</p>
|
|
30
|
+
<form onSubmit={handleLogin} className="flex flex-col gap-4">
|
|
31
|
+
<div className="flex flex-col gap-1.5">
|
|
32
|
+
<label htmlFor="admin-email" className="text-sm font-medium text-zinc-700">
|
|
33
|
+
Email
|
|
34
|
+
</label>
|
|
35
|
+
<input
|
|
36
|
+
id="admin-email"
|
|
37
|
+
type="email"
|
|
38
|
+
value={email}
|
|
39
|
+
autoComplete="off"
|
|
40
|
+
autoFocus
|
|
41
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
42
|
+
placeholder="Enter Email"
|
|
43
|
+
required
|
|
44
|
+
disabled={isPending}
|
|
45
|
+
className="border border-gray-700 rounded-lg px-4 py-2 transition focus:outline-none focus:ring-1 ring-gray-500 bg-zinc-900 placeholder-white text-white autofill:bg-zinc-900 autofill:text-white"
|
|
46
|
+
style={{
|
|
47
|
+
WebkitBoxShadow: "0 0 0 1000px #18181b inset",
|
|
48
|
+
boxShadow: "0 0 0 1000px #18181b inset",
|
|
49
|
+
WebkitTextFillColor: "#fff",
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
<div className="flex flex-col gap-1.5">
|
|
54
|
+
<label htmlFor="admin-password" className="text-sm font-medium text-zinc-700">
|
|
55
|
+
Password
|
|
56
|
+
</label>
|
|
57
|
+
<input
|
|
58
|
+
id="admin-password"
|
|
59
|
+
type="password"
|
|
60
|
+
value={password}
|
|
61
|
+
autoComplete="off"
|
|
62
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
63
|
+
placeholder="Enter Password"
|
|
64
|
+
required
|
|
65
|
+
disabled={isPending}
|
|
66
|
+
className="border border-gray-700 rounded-lg px-4 py-2 transition focus:outline-none focus:ring-1 ring-gray-500 bg-zinc-900 placeholder-white text-white"
|
|
67
|
+
style={{
|
|
68
|
+
WebkitBoxShadow: "0 0 0 1000px #18181b inset",
|
|
69
|
+
boxShadow: "0 0 0 1000px #18181b inset",
|
|
70
|
+
WebkitTextFillColor: "#fff",
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
{error && (
|
|
75
|
+
<div className="text-red-500 text-sm mt-1 rounded bg-red-50 p-2 border border-red-100 text-center animate-pulse">
|
|
76
|
+
{error}
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
<button
|
|
80
|
+
type="submit"
|
|
81
|
+
disabled={isPending}
|
|
82
|
+
className="mt-2 rounded-lg bg-blue-600 hover:bg-blue-700 transition text-white font-semibold py-2 shadow text-lg disabled:opacity-60 disabled:cursor-not-allowed"
|
|
83
|
+
>
|
|
84
|
+
{isPending ? (
|
|
85
|
+
<span className="flex justify-center items-center gap-2">
|
|
86
|
+
<svg className="animate-spin h-5 w-5 text-white" viewBox="0 0 24 24">
|
|
87
|
+
<circle
|
|
88
|
+
className="opacity-25"
|
|
89
|
+
cx="12"
|
|
90
|
+
cy="12"
|
|
91
|
+
r="10"
|
|
92
|
+
stroke="currentColor"
|
|
93
|
+
strokeWidth="4"
|
|
94
|
+
fill="none"
|
|
95
|
+
/>
|
|
96
|
+
<path
|
|
97
|
+
className="opacity-75"
|
|
98
|
+
fill="currentColor"
|
|
99
|
+
d="M4 12a8 8 0 018-8v4l3-3-3-3v4a8 8 0 000 16v-4l-3 3 3 3v-4a8 8 0 01-8-8z"
|
|
100
|
+
/>
|
|
101
|
+
</svg>
|
|
102
|
+
Logging in...
|
|
103
|
+
</span>
|
|
104
|
+
) : (
|
|
105
|
+
"Submit"
|
|
106
|
+
)}
|
|
107
|
+
</button>
|
|
108
|
+
</form>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
//# sourceMappingURL=supabaseAdminLoginLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabaseAdminLoginLayout.js","sourceRoot":"","sources":["../../../../src/template/supabase/admin/supabaseAdminLoginLayout.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgHvC,CAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const adminProxy = "import { ENV } from \"@/configs/env\"\nimport { createServerClient } from \"@supabase/ssr\"\nimport { NextResponse, type NextRequest } from \"next/server\"\n\nexport async function proxy(request: NextRequest) {\n let supabaseResponse = NextResponse.next({ request })\n const { pathname } = request.nextUrl\n\n const isAdminRoute = pathname.startsWith(\"/admin\")\n\n if (isAdminRoute) {\n if (pathname.startsWith(\"/admin/login\")) {\n return supabaseResponse\n }\n\n const supabase = createServerClient(ENV.SUPABASE_URL!, ENV.SUPABASE_ANON_KEY!, {\n cookies: {\n getAll() {\n return request.cookies.getAll()\n },\n setAll(cookiesToSet) {\n cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value))\n supabaseResponse = NextResponse.next({ request })\n cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options))\n },\n },\n })\n\n const {\n data: { user },\n } = await supabase.auth.getUser()\n\n if (!user || user.id !== ENV.SUPABASE_ADMIN_UID) {\n return NextResponse.redirect(new URL(\"/\", request.url))\n }\n }\n\n return supabaseResponse\n}\n\nexport const config = {\n matcher: [\"/((?!_next/static|_next/image|favicon.ico|.*\\\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)\"],\n}\n";
|
|
2
|
+
//# sourceMappingURL=adminProxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adminProxy.d.ts","sourceRoot":"","sources":["../../../../src/template/supabase/proxy/adminProxy.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,u1CA2CtB,CAAA"}
|