create-nscope-app 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/package.json +2 -2
- package/templates/next/.env.local.example +3 -1
- package/templates/next/README.md +6 -0
- package/templates/next/app/globals.css +12 -0
- package/templates/next/components/home-page.tsx +30 -19
- package/templates/next/components/providers.tsx +13 -2
- package/templates/next/lib/nscope.ts +4 -5
- package/templates/next/package.json +2 -2
- package/templates/vite/.env.example +6 -1
- package/templates/vite/README.md +8 -2
- package/templates/vite/package.json +2 -2
- package/templates/vite/src/App.tsx +30 -12
- package/templates/vite/src/index.css +12 -0
- package/templates/vite/src/main.tsx +9 -2
- package/templates/vite/src/nscope.ts +3 -4
- package/templates/vite/src/vite-env.d.ts +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nscope-app",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Scaffold a Nscope client app (Vite or Next.js)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,4 +19,4 @@
|
|
|
19
19
|
],
|
|
20
20
|
"license": "ISC",
|
|
21
21
|
"author": "Traton Digital"
|
|
22
|
-
}
|
|
22
|
+
}
|
package/templates/next/README.md
CHANGED
|
@@ -15,6 +15,12 @@ pnpm dev
|
|
|
15
15
|
|
|
16
16
|
Open [http://localhost:3000](http://localhost:3000).
|
|
17
17
|
|
|
18
|
+
## How it works
|
|
19
|
+
|
|
20
|
+
- `lib/nscope.ts` exports `nscopeConfig` for `NscopeProvider`.
|
|
21
|
+
- `Providers` wraps the app with `loadingFallback` while the session initializes.
|
|
22
|
+
- `HomePage` uses `useNscope()` — `authenticated`, `customer`, `login`, `logout`, and `<Can>`.
|
|
23
|
+
|
|
18
24
|
## Next steps
|
|
19
25
|
|
|
20
26
|
- Wrap protected pages with `<GuardedRoute>`
|
|
@@ -19,6 +19,18 @@ body {
|
|
|
19
19
|
padding: 0 1rem;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
.muted {
|
|
23
|
+
color: #71717a;
|
|
24
|
+
font-size: 0.95rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
code {
|
|
28
|
+
font-size: 0.9em;
|
|
29
|
+
background: #f4f4f5;
|
|
30
|
+
padding: 0.1em 0.35em;
|
|
31
|
+
border-radius: 4px;
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
button {
|
|
23
35
|
border: none;
|
|
24
36
|
border-radius: 8px;
|
|
@@ -3,37 +3,48 @@
|
|
|
3
3
|
import { Can, useNscope } from "@nscope/react";
|
|
4
4
|
|
|
5
5
|
export function HomePage() {
|
|
6
|
-
const {
|
|
7
|
-
|
|
8
|
-
if (isLoading) {
|
|
9
|
-
return <main className="page">Loading…</main>;
|
|
10
|
-
}
|
|
6
|
+
const { authenticated, customer, login, logout } = useNscope();
|
|
11
7
|
|
|
12
8
|
return (
|
|
13
9
|
<main className="page">
|
|
14
10
|
<h1>{{PROJECT_NAME}}</h1>
|
|
15
11
|
|
|
16
|
-
{
|
|
12
|
+
{authenticated && customer ? (
|
|
17
13
|
<>
|
|
18
|
-
<p>
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
<p>
|
|
15
|
+
Signed in as <strong>{customer.name || customer.email}</strong>
|
|
16
|
+
</p>
|
|
17
|
+
<p className="muted">Role: {customer.role.name}</p>
|
|
18
|
+
|
|
19
|
+
<Can
|
|
20
|
+
subject="profile"
|
|
21
|
+
action="list"
|
|
22
|
+
fallback={<p className="muted">Sem permissão de perfil.</p>}
|
|
23
|
+
>
|
|
24
|
+
<p>Permissão <code>profile:list</code> concedida.</p>
|
|
21
25
|
</Can>
|
|
26
|
+
|
|
22
27
|
<button type="button" onClick={() => logout()}>
|
|
23
28
|
Sign out
|
|
24
29
|
</button>
|
|
25
30
|
</>
|
|
26
31
|
) : (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
<>
|
|
33
|
+
<p className="muted">
|
|
34
|
+
Login hospedado via OAuth. A SDK troca <code>?code=</code> e
|
|
35
|
+
restaura o token automaticamente.
|
|
36
|
+
</p>
|
|
37
|
+
<button
|
|
38
|
+
type="button"
|
|
39
|
+
onClick={() =>
|
|
40
|
+
login({
|
|
41
|
+
redirectUri: process.env.NEXT_PUBLIC_NSCOPE_REDIRECT_URI,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
>
|
|
45
|
+
Sign in with Nscope
|
|
46
|
+
</button>
|
|
47
|
+
</>
|
|
37
48
|
)}
|
|
38
49
|
</main>
|
|
39
50
|
);
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { NscopeProvider } from "@nscope/react";
|
|
4
|
-
import {
|
|
4
|
+
import { nscopeConfig } from "@/lib/nscope";
|
|
5
5
|
|
|
6
6
|
export function Providers({ children }: { children: React.ReactNode }) {
|
|
7
|
-
return
|
|
7
|
+
return (
|
|
8
|
+
<NscopeProvider
|
|
9
|
+
config={nscopeConfig}
|
|
10
|
+
loadingFallback={
|
|
11
|
+
<main className="page">
|
|
12
|
+
<p className="muted">Carregando sessão…</p>
|
|
13
|
+
</main>
|
|
14
|
+
}
|
|
15
|
+
>
|
|
16
|
+
{children}
|
|
17
|
+
</NscopeProvider>
|
|
18
|
+
);
|
|
8
19
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SDKConfig } from "@nscope/sdk";
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
apiKey: process.env.NEXT_PUBLIC_NSCOPE_API_KEY!,
|
|
3
|
+
export const nscopeConfig: SDKConfig = {
|
|
4
|
+
apiKey: process.env.NEXT_PUBLIC_NSCOPE_API_KEY,
|
|
6
5
|
auth: {
|
|
7
6
|
autoHandleCallback: true,
|
|
8
7
|
storage: "localStorage",
|
|
9
8
|
},
|
|
10
|
-
}
|
|
9
|
+
};
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
# Chave pública do projeto (Configurações → API keys)
|
|
1
2
|
VITE_NSCOPE_API_KEY={{API_KEY}}
|
|
2
|
-
|
|
3
|
+
|
|
4
|
+
# URL de retorno após login OAuth (cadastre em Domínios permitidos)
|
|
3
5
|
VITE_NSCOPE_REDIRECT_URI={{REDIRECT_URI}}
|
|
6
|
+
|
|
7
|
+
# Referência local — a URL da API é definida no build do pacote @nscope/sdk
|
|
8
|
+
# VITE_NSCOPE_BASE_URL={{BASE_URL}}
|
package/templates/vite/README.md
CHANGED
|
@@ -13,8 +13,14 @@ pnpm install
|
|
|
13
13
|
pnpm dev
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
## How it works
|
|
17
|
+
|
|
18
|
+
- `src/nscope.ts` exports `nscopeConfig` (`SDKConfig`) — not a pre-initialized SDK instance.
|
|
19
|
+
- `NscopeProvider` receives `config` and shows `loadingFallback` while `sdk.ready` resolves (OAuth callback + token restore).
|
|
20
|
+
- `useNscope()` exposes `ready`, `authenticated`, `customer`, `permissions`, `login`, `logout`, `can()` and `refresh()`.
|
|
21
|
+
|
|
16
22
|
## Next steps
|
|
17
23
|
|
|
18
|
-
- Customize login
|
|
19
|
-
- Protect
|
|
24
|
+
- Customize login in `src/App.tsx` — `login()` accepts optional `redirectUri`
|
|
25
|
+
- Protect UI with `<Can>` or `<GuardedRoute>` from `@nscope/react`
|
|
20
26
|
- Read the SDK docs in your Nscope panel at `/docs/sdk`
|
|
@@ -1,30 +1,48 @@
|
|
|
1
1
|
import { Can, useNscope } from "@nscope/react";
|
|
2
2
|
|
|
3
3
|
export default function App() {
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
if (isLoading) {
|
|
7
|
-
return <main className="page">Loading…</main>;
|
|
8
|
-
}
|
|
4
|
+
const { authenticated, customer, login, logout } = useNscope();
|
|
9
5
|
|
|
10
6
|
return (
|
|
11
7
|
<main className="page">
|
|
12
8
|
<h1>{{PROJECT_NAME}}</h1>
|
|
13
9
|
|
|
14
|
-
{
|
|
10
|
+
{authenticated && customer ? (
|
|
15
11
|
<>
|
|
16
|
-
<p>
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
<p>
|
|
13
|
+
Signed in as <strong>{customer.name || customer.email}</strong>
|
|
14
|
+
</p>
|
|
15
|
+
<p className="muted">Role: {customer.role.name}</p>
|
|
16
|
+
|
|
17
|
+
<Can
|
|
18
|
+
subject="profile"
|
|
19
|
+
action="list"
|
|
20
|
+
fallback={<p className="muted">Sem permissão de perfil.</p>}
|
|
21
|
+
>
|
|
22
|
+
<p>Permissão <code>profile:list</code> concedida.</p>
|
|
19
23
|
</Can>
|
|
24
|
+
|
|
20
25
|
<button type="button" onClick={() => logout()}>
|
|
21
26
|
Sign out
|
|
22
27
|
</button>
|
|
23
28
|
</>
|
|
24
29
|
) : (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
<>
|
|
31
|
+
<p className="muted">
|
|
32
|
+
Login hospedado via OAuth. A SDK troca <code>?code=</code> e
|
|
33
|
+
restaura o token automaticamente.
|
|
34
|
+
</p>
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
onClick={() =>
|
|
38
|
+
login({
|
|
39
|
+
redirectUri: import.meta.env.VITE_NSCOPE_REDIRECT_URI,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
>
|
|
43
|
+
Sign in with Nscope
|
|
44
|
+
</button>
|
|
45
|
+
</>
|
|
28
46
|
)}
|
|
29
47
|
</main>
|
|
30
48
|
);
|
|
@@ -19,6 +19,18 @@ body {
|
|
|
19
19
|
padding: 0 1rem;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
.muted {
|
|
23
|
+
color: #71717a;
|
|
24
|
+
font-size: 0.95rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
code {
|
|
28
|
+
font-size: 0.9em;
|
|
29
|
+
background: #f4f4f5;
|
|
30
|
+
padding: 0.1em 0.35em;
|
|
31
|
+
border-radius: 4px;
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
button {
|
|
23
35
|
border: none;
|
|
24
36
|
border-radius: 8px;
|
|
@@ -2,12 +2,19 @@ import { StrictMode } from "react";
|
|
|
2
2
|
import { createRoot } from "react-dom/client";
|
|
3
3
|
import { NscopeProvider } from "@nscope/react";
|
|
4
4
|
import App from "./App";
|
|
5
|
-
import {
|
|
5
|
+
import { nscopeConfig } from "./nscope";
|
|
6
6
|
import "./index.css";
|
|
7
7
|
|
|
8
8
|
createRoot(document.getElementById("root")!).render(
|
|
9
9
|
<StrictMode>
|
|
10
|
-
<NscopeProvider
|
|
10
|
+
<NscopeProvider
|
|
11
|
+
config={nscopeConfig}
|
|
12
|
+
loadingFallback={
|
|
13
|
+
<main className="page">
|
|
14
|
+
<p className="muted">Carregando sessão…</p>
|
|
15
|
+
</main>
|
|
16
|
+
}
|
|
17
|
+
>
|
|
11
18
|
<App />
|
|
12
19
|
</NscopeProvider>
|
|
13
20
|
</StrictMode>,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SDKConfig } from "@nscope/sdk";
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
baseUrl: import.meta.env.VITE_NSCOPE_BASE_URL,
|
|
3
|
+
export const nscopeConfig: SDKConfig = {
|
|
5
4
|
apiKey: import.meta.env.VITE_NSCOPE_API_KEY,
|
|
6
5
|
auth: {
|
|
7
6
|
autoHandleCallback: true,
|
|
8
7
|
storage: "localStorage",
|
|
9
8
|
},
|
|
10
|
-
}
|
|
9
|
+
};
|