@conexus-x/next-view 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/.env ADDED
@@ -0,0 +1,21 @@
1
+ # ---------------------------------------------------------------------------
2
+ # DEVELOPER MODE. None of this is used by your view in production.
3
+ #
4
+ # The values here power the dev preview at /dev, which frames your view the way
5
+ # a Conexus X board does and feeds it REAL data from your workspace.
6
+ # ---------------------------------------------------------------------------
7
+
8
+ # Your Conexus X API key. Find it in the app under Settings > API.
9
+ #
10
+ # It is read by the dev server ONLY and never reaches the browser: requests go
11
+ # to /api/conexus/* on this app, which attaches the key server-side and forwards
12
+ # them. A permanent credential in client-side JavaScript is a permanent
13
+ # credential in every browser devtools panel that ever opens your view.
14
+ API_KEY=Conexus_x_api_key
15
+
16
+ # Where the Conexus X API lives.
17
+ CONEXUS_API_URL=http://localhost:4040/api
18
+
19
+ # Which origins may frame this view, space or comma separated.
20
+ # Add your tunnel's CRM origin here when previewing inside the real app.
21
+ CONEXUS_ORIGINS=http://localhost:3000 https://conexus-x.vercel.app
package/.env.example ADDED
@@ -0,0 +1,21 @@
1
+ # ---------------------------------------------------------------------------
2
+ # DEVELOPER MODE. None of this is used by your view in production.
3
+ #
4
+ # The values here power the dev preview at /dev, which frames your view the way
5
+ # a Conexus X board does and feeds it REAL data from your workspace.
6
+ # ---------------------------------------------------------------------------
7
+
8
+ # Your Conexus X API key. Find it in the app under Settings > API.
9
+ #
10
+ # It is read by the dev server ONLY and never reaches the browser: requests go
11
+ # to /api/conexus/* on this app, which attaches the key server-side and forwards
12
+ # them. A permanent credential in client-side JavaScript is a permanent
13
+ # credential in every browser devtools panel that ever opens your view.
14
+ API_KEY=Conexus_x_api_key
15
+
16
+ # Where the Conexus X API lives.
17
+ CONEXUS_API_URL=http://localhost:4040/api
18
+
19
+ # Which origins may frame this view, space or comma separated.
20
+ # Add your tunnel's CRM origin here when previewing inside the real app.
21
+ CONEXUS_ORIGINS=http://localhost:3000 https://conexus-x.vercel.app
package/.gitignore CHANGED
@@ -3,5 +3,6 @@ node_modules/
3
3
  out/
4
4
  next-env.d.ts
5
5
  *.tsbuildinfo
6
+ .env
6
7
  .env*.local
7
8
  .vercel
package/README.md CHANGED
@@ -26,6 +26,61 @@ cd - && npm link @conexus-x/sdk
26
26
  Nothing in this repo records the link, which is the point.
27
27
 
28
28
 
29
+ ## Developer mode
30
+
31
+ You do not need a Conexus X board to start. Put your API key in `.env` and open
32
+ the developer page — it lists the workspaces that key can see, which is the
33
+ shortest proof that your key works and the API is reachable.
34
+
35
+ ```bash
36
+ # .env — already in your project, just fill it in
37
+ API_KEY=Conexus_x_api_key # <- replace with your key (Settings > API)
38
+ CONEXUS_API_URL=http://localhost:4040/api
39
+ ```
40
+
41
+ ```bash
42
+ npm run dev
43
+ ```
44
+
45
+ Then open **http://localhost:5173/dev**.
46
+
47
+ **The key never reaches the browser.** The page is a React Server Component: `API_KEY` is read in Node and only the finished list is sent to the browser.
48
+ A permanent credential in client-side JavaScript is one devtools panel away
49
+ from being somebody else's.
50
+
51
+ `.env` is gitignored, so your real key cannot be committed by accident. The
52
+ committed copy is `.env.example`.
53
+
54
+ The developer page is not part of a production build, and **your view never
55
+ uses the API key** — a deployed view receives data through the SDK, with the
56
+ permissions of whoever is looking at the board. The key is scaffolding for
57
+ development only.
58
+
59
+ ## Previewing inside the real CRM
60
+
61
+ To see your view running inside an actual Conexus X board while you develop,
62
+ give it a public URL:
63
+
64
+ ```bash
65
+ npm run dev # leave this running
66
+ npm run tunnel # in a second terminal
67
+ ```
68
+
69
+ That prints a `https://….trycloudflare.com` address. Then:
70
+
71
+ 1. Put it in `entry` in `conexus.manifest.json`.
72
+ 2. Add the CRM's origin to `CONEXUS_ORIGINS` in `.env` and restart the dev
73
+ server, so the view allows that origin to frame it.
74
+
75
+ The tunnel is Cloudflare's free quick tunnel — no account, no signup. It is
76
+ fetched on first use rather than installed with the project, because the binary
77
+ is large and most runs never need it. Quick tunnels have no uptime guarantee and
78
+ the URL changes every run; they are for previewing, not for shipping.
79
+
80
+ Chosen over the other free options because it serves your page directly. Some
81
+ tunnels show an interstitial "click to continue" page on first visit, which a
82
+ board cannot click through — the view would render as a blank frame.
83
+
29
84
  ## Tailwind is already set up
30
85
 
31
86
  Tailwind v4 is installed and configured. There is no `tailwind.config.js` to
@@ -0,0 +1,162 @@
1
+ import { notFound } from "next/navigation";
2
+
3
+ /**
4
+ * DEVELOPER MODE — http://localhost:5173/dev
5
+ *
6
+ * Put your API key in .env, reload, and this lists the workspaces that key can
7
+ * see. It is the shortest possible proof that your key works and the API is
8
+ * reachable, which is the thing that is actually wrong the first time nothing
9
+ * renders.
10
+ *
11
+ * A SERVER COMPONENT, deliberately. The key is read here, in Node, and only the
12
+ * finished list is sent to the browser — so the key never appears in a bundle,
13
+ * a network tab, or anybody's devtools. Fetching this from the browser would
14
+ * put a permanent credential in client-side JavaScript, and a permanent
15
+ * credential in client-side JavaScript is one screenshot away from being
16
+ * somebody else's.
17
+ *
18
+ * Your VIEW does not work this way and must not: it runs framed by a board and
19
+ * receives data through the SDK, with the signed-in person's own permissions.
20
+ * This page is scaffolding for development, nothing more.
21
+ */
22
+
23
+ // Never prerendered: it reads .env and the network at request time.
24
+ export const dynamic = "force-dynamic";
25
+
26
+ const PLACEHOLDER = "Conexus_x_api_key";
27
+
28
+ interface WorkspaceMembership {
29
+ _id: string;
30
+ role?: string;
31
+ status?: string;
32
+ workspace?: {
33
+ _id: string;
34
+ name?: string;
35
+ description?: string;
36
+ totalModules?: number;
37
+ } | null;
38
+ }
39
+
40
+ type LoadResult =
41
+ | { ok: true; workspaces: WorkspaceMembership[] }
42
+ | { ok: false; title: string; detail: string };
43
+
44
+ const loadWorkspaces = async (): Promise<LoadResult> => {
45
+ const apiKey = process.env.API_KEY;
46
+ const apiUrl = (process.env.CONEXUS_API_URL ?? "http://localhost:4040/api").replace(/\/$/, "");
47
+
48
+ if (!apiKey) {
49
+ return {
50
+ ok: false,
51
+ title: "No API key",
52
+ detail: "Add API_KEY to .env in this project, then reload."
53
+ };
54
+ }
55
+
56
+ if (apiKey === PLACEHOLDER) {
57
+ return {
58
+ ok: false,
59
+ title: "The API key is still the placeholder",
60
+ detail: `Replace API_KEY=${PLACEHOLDER} in .env with your own key from Conexus X (Settings › API), then reload.`
61
+ };
62
+ }
63
+
64
+ try {
65
+ const response = await fetch(`${apiUrl}/workspaces`, {
66
+ headers: { "x-api-key": apiKey },
67
+ cache: "no-store"
68
+ });
69
+
70
+ if (response.status === 401) {
71
+ return {
72
+ ok: false,
73
+ title: "That key was rejected",
74
+ detail: "The API answered 401. Check the key, and that the account it belongs to is still active."
75
+ };
76
+ }
77
+
78
+ if (!response.ok) {
79
+ return {
80
+ ok: false,
81
+ title: `The API answered ${response.status}`,
82
+ detail: (await response.text()).slice(0, 300)
83
+ };
84
+ }
85
+
86
+ const body = (await response.json()) as { workspaces?: WorkspaceMembership[] };
87
+
88
+ return { ok: true, workspaces: body.workspaces ?? [] };
89
+ } catch (error) {
90
+ return {
91
+ ok: false,
92
+ title: "Could not reach the API",
93
+ detail:
94
+ `Tried ${apiUrl}/workspaces — ${error instanceof Error ? error.message : String(error)}. ` +
95
+ "Is the Conexus X API running, and is CONEXUS_API_URL right?"
96
+ };
97
+ }
98
+ };
99
+
100
+ export default async function DevPage() {
101
+ // This page reads a secret and exists only to help you build. It has no
102
+ // business in a deployed view, so it is not there.
103
+ if (process.env.NODE_ENV === "production") notFound();
104
+
105
+ const result = await loadWorkspaces();
106
+
107
+ return (
108
+ <main className="mx-auto max-w-2xl p-6">
109
+ <header className="mb-5">
110
+ <h1 className="text-lg font-semibold">Developer mode</h1>
111
+ <p className="mt-1 text-[13px] text-muted">
112
+ Workspaces visible to the API key in <code>.env</code>.
113
+ </p>
114
+ </header>
115
+
116
+ {!result.ok ? (
117
+ <div className="rounded-lg border border-danger bg-danger-soft px-4 py-3 text-danger">
118
+ <strong className="font-semibold">{result.title}</strong>
119
+ <p className="mt-1.5 text-[13px] whitespace-pre-wrap">{result.detail}</p>
120
+ </div>
121
+ ) : result.workspaces.length === 0 ? (
122
+ <p className="text-[13px] text-muted">
123
+ The key works — this account is not a member of any workspace yet.
124
+ </p>
125
+ ) : (
126
+ <ul className="rounded-lg border border-line">
127
+ {result.workspaces.map((membership) => (
128
+ <li
129
+ key={membership._id}
130
+ className="flex items-center gap-3 border-b border-line px-4 py-3 last:border-b-0"
131
+ >
132
+ <div className="min-w-0 flex-1">
133
+ <div className="truncate font-medium">
134
+ {membership.workspace?.name ?? "Untitled workspace"}
135
+ </div>
136
+ <div className="mt-0.5 font-mono text-[12px] text-muted">
137
+ {membership.workspace?._id}
138
+ </div>
139
+ </div>
140
+
141
+ {membership.role ? (
142
+ <span className="shrink-0 rounded-md border border-line px-2 py-0.5 text-[12px] text-muted">
143
+ {membership.role}
144
+ </span>
145
+ ) : null}
146
+
147
+ <span className="shrink-0 text-[12px] text-muted">
148
+ {membership.workspace?.totalModules ?? 0} boards
149
+ </span>
150
+ </li>
151
+ ))}
152
+ </ul>
153
+ )}
154
+
155
+ <p className="mt-5 text-[12px] text-muted">
156
+ This page is development-only and returns 404 in a production build.
157
+ Your view itself never uses the API key — it receives data through
158
+ the SDK, with the permissions of whoever is looking at the board.
159
+ </p>
160
+ </main>
161
+ );
162
+ }
package/next.config.ts CHANGED
@@ -19,6 +19,22 @@ const conexusOrigins =
19
19
  "http://localhost:3000 https://conexus-x.vercel.app";
20
20
 
21
21
  const nextConfig: NextConfig = {
22
+ /**
23
+ * Let a tunnel reach the dev server.
24
+ *
25
+ * Next refuses cross-origin dev requests it was not told about, which is
26
+ * the reason `npm run tunnel` would otherwise serve a broken page through
27
+ * the public URL — the HTML arrives and the dev assets behind it do not.
28
+ *
29
+ * Development only; a production build ignores this entirely.
30
+ */
31
+ allowedDevOrigins: [
32
+ "*.trycloudflare.com",
33
+ "*.loca.lt",
34
+ "*.ngrok-free.app",
35
+ "*.ngrok.io"
36
+ ],
37
+
22
38
  /**
23
39
  * NOT pinning `turbopack.root` here, deliberately.
24
40
  *
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@conexus-x/next-view",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "private": false,
5
5
  "description": "Custom view for Conexus X — Next.js starter, wired to @conexus-x/sdk.",
6
6
  "scripts": {
7
7
  "dev": "next dev --port 5173",
8
8
  "build": "next build",
9
9
  "start": "next start --port 5173",
10
- "lint": "eslint"
10
+ "lint": "eslint",
11
+ "tunnel": "npx -y cloudflared tunnel --url http://localhost:5173"
11
12
  },
12
13
  "dependencies": {
13
14
  "@conexus-x/sdk": "^0.1.0",
@@ -33,6 +34,8 @@
33
34
  "postcss.config.mjs",
34
35
  "tsconfig.json",
35
36
  "conexus.manifest.json",
37
+ ".env",
38
+ ".env.example",
36
39
  "README.md",
37
40
  ".gitignore"
38
41
  ]