@dashai/cli 0.13.0 → 0.14.1
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/dist/bin.js +225 -97
- package/dist/bin.js.map +1 -1
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -865,8 +865,14 @@ npm run dev:next # next dev on :3000 (terminal 2)
|
|
|
865
865
|
- **\`module.json\`** \u2014 manifest (\`module_kind: 'custom'\`, \`runtime.kind: 'machine'\`).
|
|
866
866
|
- **\`middleware.ts\`** \u2014 \`withAuth\` from \`@dashai/sdk/auth/middleware\`. Redirects unauthenticated requests through the DashWise SSO flow.
|
|
867
867
|
- **\`app/layout.tsx\`** \u2014 wraps the tree in \`<AuthProvider>\` from \`@dashai/sdk/auth/client\`.
|
|
868
|
-
- **\`app/page.tsx\`** \u2014
|
|
869
|
-
- **\`app/dashboard/page.tsx\`** \u2014 Server Component
|
|
868
|
+
- **\`app/page.tsx\`** \u2014 the app's home at \`/\` (auth-gated; zero-click for platform users via \`autoSignIn\`). **This is where you build your app.**
|
|
869
|
+
- **\`app/dashboard/page.tsx\`** \u2014 an additional example Server Component route, also session-gated via \`getServerSession()\`.
|
|
870
|
+
|
|
871
|
+
The design is yours: edit the theme tokens in \`app/globals.css\` (they ship
|
|
872
|
+
colored on purpose, as a starting point) and use any Tailwind colors or
|
|
873
|
+
shadcn blocks you like. A manual "Sign in" landing page is only for a
|
|
874
|
+
\`public_access_allowed\` module \u2014 re-add \`'/'\` to \`middleware.ts\` publicPaths
|
|
875
|
+
and drop the home's session guard to build one.
|
|
870
876
|
- **\`app/api/auth/callback/route.ts\`** \u2014 exchanges the SSO one-time code for a session cookie (uses \`exchangeCode()\` from the SDK).
|
|
871
877
|
- **\`app/api/auth/sign-in/route.ts\`** \u2014 one entry point that calls the backend's \`POST /api/module-api/sso/start-url\` (authenticated with your \`dwk_\` key) and 302s the browser to the returned \`redirect_url\`. Same path in dev + prod.
|
|
872
878
|
- **\`app/api/auth/sign-out/route.ts\`** \u2014 clears the session cookie + tells the backend to revoke.
|
|
@@ -943,18 +949,28 @@ function middlewareTs() {
|
|
|
943
949
|
import { withAuth } from '@dashai/sdk/auth/middleware';
|
|
944
950
|
|
|
945
951
|
export default withAuth({
|
|
946
|
-
//
|
|
947
|
-
//
|
|
948
|
-
//
|
|
949
|
-
|
|
952
|
+
// Only the SSO error page is public. The app's home lives at \`/\`
|
|
953
|
+
// (app/page.tsx) and is auth-gated like the rest of the app \u2014 a
|
|
954
|
+
// signed-in platform user still lands there with ZERO clicks because
|
|
955
|
+
// \`autoSignIn\` (below) completes the handshake silently. There is no
|
|
956
|
+
// public "sign in" wall in front of your app by default.
|
|
957
|
+
//
|
|
958
|
+
// \`/sign-in-error\` stays public so a failed sign-in can render its
|
|
959
|
+
// error page instead of redirect-looping back into re-authentication.
|
|
960
|
+
//
|
|
961
|
+
// Building a \`public_access_allowed\` module that wants an ANONYMOUS
|
|
962
|
+
// landing page (a marketing / manual "Sign in with DashWise" screen)?
|
|
963
|
+
// Re-add \`'/'\` here AND make app/page.tsx public (drop its
|
|
964
|
+
// getServerSession guard). See the README's "Design & home" note.
|
|
965
|
+
publicPaths: ['/sign-in-error'],
|
|
950
966
|
loginPath: '/api/auth/sign-in',
|
|
951
967
|
// Silent sign-in: when a signed-in DashWise user opens the module through
|
|
952
968
|
// the platform (in-app embed or the module subdomain), the SSO handshake
|
|
953
|
-
// completes with zero clicks \u2014 they land straight
|
|
954
|
-
// included. It never touches:
|
|
969
|
+
// completes with zero clicks \u2014 they land straight on the page they asked
|
|
970
|
+
// for, the home at \`/\` included. It never touches:
|
|
955
971
|
// - anonymous visitors on public installs (no platform-session hint),
|
|
956
|
-
// - local \`dashwise dev\` (no platform proxy \u2192 no hint header) \u2014
|
|
957
|
-
//
|
|
972
|
+
// - local \`dashwise dev\` (no platform proxy \u2192 no hint header) \u2014 a
|
|
973
|
+
// manual sign-in through /api/auth/sign-in stays your dev flow,
|
|
958
974
|
// - anything within 60s of a failed silent attempt (no redirect loops).
|
|
959
975
|
autoSignIn: true,
|
|
960
976
|
});
|
|
@@ -996,84 +1012,118 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
996
1012
|
`;
|
|
997
1013
|
}
|
|
998
1014
|
function appPageCustomTsx(opts) {
|
|
999
|
-
return `//
|
|
1015
|
+
return `// app/page.tsx \u2014 the app's REAL home, served at \`/\`. THIS IS YOUR APP.
|
|
1016
|
+
// Build it here.
|
|
1000
1017
|
//
|
|
1001
|
-
//
|
|
1002
|
-
//
|
|
1003
|
-
//
|
|
1004
|
-
//
|
|
1005
|
-
//
|
|
1006
|
-
//
|
|
1007
|
-
// user lands on /dashboard (Server Component, session-validated).
|
|
1018
|
+
// A signed-in DashWise user who opens this module through the platform
|
|
1019
|
+
// (in-app embed or the module subdomain) lands on THIS page with zero
|
|
1020
|
+
// clicks: the middleware's \`autoSignIn\` completes the SSO handshake
|
|
1021
|
+
// silently, so there is no sign-in wall in front of your app. The guard
|
|
1022
|
+
// below mirrors app/dashboard/page.tsx \u2014 no session \u2192 bounce through the
|
|
1023
|
+
// SSO flow and come right back to \`/\`.
|
|
1008
1024
|
//
|
|
1009
|
-
//
|
|
1010
|
-
//
|
|
1011
|
-
//
|
|
1025
|
+
// A public, anonymous sign-in landing page is only for a
|
|
1026
|
+
// \`public_access_allowed\` module that wants an unauthenticated entry
|
|
1027
|
+
// point. To build one, re-add \`'/'\` to middleware.ts publicPaths and drop
|
|
1028
|
+
// the getServerSession guard here (see the README's "Design & home" note).
|
|
1029
|
+
//
|
|
1030
|
+
// The design is YOURS to own. These shadcn primitives sit on the theme
|
|
1031
|
+
// tokens in app/globals.css \u2014 edit the hues freely \u2014 and the full
|
|
1032
|
+
// Tailwind palette (bg-indigo-600, text-emerald-500, \u2026) is available too.
|
|
1033
|
+
// This colored starter look is a starting point, not a mandate.
|
|
1012
1034
|
import Link from 'next/link';
|
|
1013
|
-
import {
|
|
1035
|
+
import { getServerSession } from '@dashai/sdk/auth/server';
|
|
1036
|
+
import { redirect } from 'next/navigation';
|
|
1037
|
+
import { ArrowRight, Table2, Database, PenLine } from 'lucide-react';
|
|
1014
1038
|
import { Button } from '@/components/ui/button';
|
|
1015
1039
|
import {
|
|
1016
1040
|
Card,
|
|
1017
|
-
CardContent,
|
|
1018
1041
|
CardDescription,
|
|
1019
1042
|
CardHeader,
|
|
1020
1043
|
CardTitle,
|
|
1021
1044
|
} from '@/components/ui/card';
|
|
1022
1045
|
|
|
1023
|
-
|
|
1046
|
+
// force-dynamic: this page calls getServerSession() at render, which
|
|
1047
|
+
// reads DASHWISE_API_URL/KEY. \`next build\` statically PRERENDERS pages by
|
|
1048
|
+
// default and runs with NO DashWise env, so the SDK would throw
|
|
1049
|
+
// "missing apiBaseUrl" and the platform's first publish would fail with
|
|
1050
|
+
// an opaque BUILD_FAILED. Opting into dynamic rendering skips prerender.
|
|
1051
|
+
export const dynamic = 'force-dynamic';
|
|
1052
|
+
|
|
1053
|
+
export default async function Home() {
|
|
1054
|
+
const session = await getServerSession();
|
|
1055
|
+
if (!session) {
|
|
1056
|
+
redirect('/api/auth/sign-in?return_to=/');
|
|
1057
|
+
}
|
|
1024
1058
|
return (
|
|
1025
|
-
<main className="flex min-h-dvh
|
|
1026
|
-
|
|
1027
|
-
|
|
1059
|
+
<main className="mx-auto flex min-h-dvh max-w-4xl flex-col gap-10 px-6 py-12">
|
|
1060
|
+
{/* Primary-colored hero \u2014 the default look is colored on purpose,
|
|
1061
|
+
to signal the design is yours to change. */}
|
|
1062
|
+
<section className="flex flex-col gap-4 rounded-2xl bg-primary px-8 py-12 text-primary-foreground shadow-sm">
|
|
1063
|
+
<p className="text-sm font-medium uppercase tracking-wide opacity-80">
|
|
1028
1064
|
${escapeHtml(opts.name)}
|
|
1065
|
+
</p>
|
|
1066
|
+
<h1 className="text-balance text-4xl font-semibold tracking-tight sm:text-5xl">
|
|
1067
|
+
Your app starts here.
|
|
1029
1068
|
</h1>
|
|
1030
|
-
<p className="text-
|
|
1031
|
-
|
|
1032
|
-
|
|
1069
|
+
<p className="max-w-xl text-pretty text-lg opacity-90">
|
|
1070
|
+
Signed in as{' '}
|
|
1071
|
+
<span className="font-medium">{session.user.email}</span>. This is the
|
|
1072
|
+
real home of your module \u2014 edit{' '}
|
|
1073
|
+
<code className="rounded bg-primary-foreground/15 px-1.5 py-0.5 font-mono">
|
|
1074
|
+
app/page.tsx
|
|
1075
|
+
</code>{' '}
|
|
1076
|
+
to build it.
|
|
1033
1077
|
</p>
|
|
1034
|
-
<div className="flex flex-wrap
|
|
1035
|
-
<Button asChild size="lg">
|
|
1036
|
-
<Link href="/api/auth/sign-in?return_to=/dashboard">
|
|
1037
|
-
<LogIn aria-hidden="true" />
|
|
1038
|
-
Sign in with DashWise
|
|
1039
|
-
</Link>
|
|
1040
|
-
</Button>
|
|
1041
|
-
<Button asChild variant="ghost" size="lg">
|
|
1078
|
+
<div className="flex flex-wrap gap-3 pt-2">
|
|
1079
|
+
<Button asChild size="lg" variant="secondary">
|
|
1042
1080
|
<Link href="/dashboard">
|
|
1043
|
-
|
|
1044
|
-
|
|
1081
|
+
See the example route
|
|
1082
|
+
<ArrowRight aria-hidden="true" />
|
|
1045
1083
|
</Link>
|
|
1046
1084
|
</Button>
|
|
1047
1085
|
</div>
|
|
1048
1086
|
</section>
|
|
1049
1087
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
<
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1088
|
+
{/* A few colored pointers to get moving. */}
|
|
1089
|
+
<section className="grid gap-4 sm:grid-cols-3">
|
|
1090
|
+
<Card className="border-l-4 border-l-chart-1">
|
|
1091
|
+
<CardHeader>
|
|
1092
|
+
<Table2 className="size-5 text-chart-1" aria-hidden="true" />
|
|
1093
|
+
<CardTitle className="text-base">Declare your data</CardTitle>
|
|
1094
|
+
<CardDescription>
|
|
1095
|
+
Add tables to{' '}
|
|
1096
|
+
<code className="rounded bg-muted px-1 py-0.5 font-mono">module.json</code>{' '}
|
|
1097
|
+
(or run{' '}
|
|
1098
|
+
<code className="rounded bg-muted px-1 py-0.5 font-mono">dashwise table create</code>).
|
|
1099
|
+
</CardDescription>
|
|
1100
|
+
</CardHeader>
|
|
1101
|
+
</Card>
|
|
1102
|
+
<Card className="border-l-4 border-l-chart-2">
|
|
1103
|
+
<CardHeader>
|
|
1104
|
+
<Database className="size-5 text-chart-2" aria-hidden="true" />
|
|
1105
|
+
<CardTitle className="text-base">Read it, typed</CardTitle>
|
|
1106
|
+
<CardDescription>
|
|
1107
|
+
Import{' '}
|
|
1108
|
+
<code className="rounded bg-muted px-1 py-0.5 font-mono">db</code>{' '}
|
|
1109
|
+
from{' '}
|
|
1110
|
+
<code className="rounded bg-muted px-1 py-0.5 font-mono">@dashai/generated</code>{' '}
|
|
1111
|
+
\u2014 regenerated on every manifest save.
|
|
1112
|
+
</CardDescription>
|
|
1113
|
+
</CardHeader>
|
|
1114
|
+
</Card>
|
|
1115
|
+
<Card className="border-l-4 border-l-chart-3">
|
|
1116
|
+
<CardHeader>
|
|
1117
|
+
<PenLine className="size-5 text-chart-3" aria-hidden="true" />
|
|
1118
|
+
<CardTitle className="text-base">Write via server actions</CardTitle>
|
|
1119
|
+
<CardDescription>
|
|
1120
|
+
Mutate data from Server Actions / route handlers so the scoped{' '}
|
|
1121
|
+
<code className="rounded bg-muted px-1 py-0.5 font-mono">dwk_</code>{' '}
|
|
1122
|
+
key stays server-side.
|
|
1123
|
+
</CardDescription>
|
|
1124
|
+
</CardHeader>
|
|
1125
|
+
</Card>
|
|
1126
|
+
</section>
|
|
1077
1127
|
</main>
|
|
1078
1128
|
);
|
|
1079
1129
|
}
|
|
@@ -1093,18 +1143,25 @@ import {
|
|
|
1093
1143
|
} from '@/components/ui/card';
|
|
1094
1144
|
|
|
1095
1145
|
/**
|
|
1096
|
-
*
|
|
1097
|
-
*
|
|
1146
|
+
* An ADDITIONAL example route \u2014 your real home is app/page.tsx (\`/\`).
|
|
1147
|
+
* This \`/dashboard\` page is here to show a second auth-gated Server
|
|
1148
|
+
* Component: keep it, rename it, or delete it. The middleware ensures the
|
|
1149
|
+
* user has a session cookie before this page renders; \`getServerSession()\`
|
|
1098
1150
|
* validates the cookie against the backend's \`/api/auth/sessions/me\`
|
|
1099
1151
|
* endpoint, returning the decoded \`Session\` (or \`null\` if stale).
|
|
1100
1152
|
*
|
|
1101
1153
|
* On a successful render, this is the polished session card \u2014 replace
|
|
1102
1154
|
* the contents with whatever your module actually needs to show.
|
|
1103
1155
|
*/
|
|
1156
|
+
// force-dynamic: same reason as app/page.tsx \u2014 this route calls
|
|
1157
|
+
// getServerSession() at render, which needs DASHWISE_API_URL/KEY. Static
|
|
1158
|
+
// prerender during \`next build\` runs with no env \u2192 BUILD_FAILED. Opt out.
|
|
1159
|
+
export const dynamic = 'force-dynamic';
|
|
1160
|
+
|
|
1104
1161
|
export default async function Dashboard() {
|
|
1105
1162
|
const session = await getServerSession();
|
|
1106
1163
|
if (!session) {
|
|
1107
|
-
redirect('/api/auth/sign-in?return_to=/
|
|
1164
|
+
redirect('/api/auth/sign-in?return_to=/');
|
|
1108
1165
|
}
|
|
1109
1166
|
return (
|
|
1110
1167
|
<main className="mx-auto flex min-h-dvh max-w-3xl flex-col gap-6 px-6 py-12">
|
|
@@ -1157,7 +1214,9 @@ export default async function Dashboard() {
|
|
|
1157
1214
|
<CardHeader>
|
|
1158
1215
|
<CardTitle>Next steps</CardTitle>
|
|
1159
1216
|
<CardDescription>
|
|
1160
|
-
|
|
1217
|
+
This is an extra example route \u2014 your real home is{' '}
|
|
1218
|
+
<code className="rounded bg-muted px-1.5 py-0.5">app/page.tsx</code>.
|
|
1219
|
+
Keep, rename, or delete this one.
|
|
1161
1220
|
</CardDescription>
|
|
1162
1221
|
</CardHeader>
|
|
1163
1222
|
<CardContent className="space-y-2 text-sm text-muted-foreground">
|
|
@@ -1548,7 +1607,12 @@ export default config;
|
|
|
1548
1607
|
`;
|
|
1549
1608
|
}
|
|
1550
1609
|
function globalsCss() {
|
|
1551
|
-
return
|
|
1610
|
+
return `/* These theme tokens are a STARTING POINT \u2014 change the hues freely.
|
|
1611
|
+
The default \`--primary\` is a violet/indigo and the \`--chart-*\` ramp is
|
|
1612
|
+
a 5-hue set; swap them for your brand. The full Tailwind color palette
|
|
1613
|
+
(bg-indigo-600, text-emerald-500, \u2026) is also available on top of these
|
|
1614
|
+
tokens. This is YOUR design to own. */
|
|
1615
|
+
@import "tailwindcss";
|
|
1552
1616
|
@import "tw-animate-css";
|
|
1553
1617
|
@import "shadcn/tailwind.css";
|
|
1554
1618
|
|
|
@@ -1561,20 +1625,33 @@ function globalsCss() {
|
|
|
1561
1625
|
--card-foreground: oklch(0.14 0 0);
|
|
1562
1626
|
--popover: oklch(1 0 0);
|
|
1563
1627
|
--popover-foreground: oklch(0.14 0 0);
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
--
|
|
1567
|
-
--
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
--
|
|
1571
|
-
--
|
|
1628
|
+
/* Colored primary (violet/indigo, ~indigo-600). White foreground reads
|
|
1629
|
+
cleanly on it (~5:1). Change the hue (275) to re-brand. */
|
|
1630
|
+
--primary: oklch(0.55 0.22 275);
|
|
1631
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
1632
|
+
/* secondary + muted stay near-neutral (clean UI) but carry a faint
|
|
1633
|
+
violet tint so nothing is pure grayscale. */
|
|
1634
|
+
--secondary: oklch(0.97 0.005 275);
|
|
1635
|
+
--secondary-foreground: oklch(0.25 0.02 275);
|
|
1636
|
+
--muted: oklch(0.97 0.005 275);
|
|
1637
|
+
--muted-foreground: oklch(0.55 0.02 275);
|
|
1638
|
+
/* accent = a soft, low-chroma wash of the primary hue. */
|
|
1639
|
+
--accent: oklch(0.96 0.03 275);
|
|
1640
|
+
--accent-foreground: oklch(0.4 0.1 275);
|
|
1572
1641
|
--destructive: oklch(0.58 0.24 28.48);
|
|
1573
1642
|
--border: oklch(0.92 0 0);
|
|
1574
1643
|
--input: oklch(0.92 0 0);
|
|
1575
|
-
--ring: oklch(0.
|
|
1644
|
+
--ring: oklch(0.55 0.2 275);
|
|
1576
1645
|
--radius: 0.625rem;
|
|
1577
1646
|
|
|
1647
|
+
/* 5-hue chart ramp \u2014 distinct, readable hues for data viz + accents.
|
|
1648
|
+
Wired to \`bg-chart-1\` \u2026 \`text-chart-5\` via the @theme block below. */
|
|
1649
|
+
--chart-1: oklch(0.55 0.2 275);
|
|
1650
|
+
--chart-2: oklch(0.6 0.13 195);
|
|
1651
|
+
--chart-3: oklch(0.7 0.16 70);
|
|
1652
|
+
--chart-4: oklch(0.65 0.17 145);
|
|
1653
|
+
--chart-5: oklch(0.6 0.22 15);
|
|
1654
|
+
|
|
1578
1655
|
--font-sans:
|
|
1579
1656
|
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
|
1580
1657
|
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
@@ -1590,18 +1667,37 @@ function globalsCss() {
|
|
|
1590
1667
|
--card-foreground: oklch(0.99 0 0);
|
|
1591
1668
|
--popover: oklch(0.2 0 0);
|
|
1592
1669
|
--popover-foreground: oklch(0.99 0 0);
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
--
|
|
1670
|
+
/* Slightly lighter violet so it pops on the dark background; white
|
|
1671
|
+
foreground stays readable (~4.8:1). */
|
|
1672
|
+
--primary: oklch(0.58 0.21 275);
|
|
1673
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
1674
|
+
--secondary: oklch(0.27 0.01 275);
|
|
1596
1675
|
--secondary-foreground: oklch(0.99 0 0);
|
|
1597
|
-
--muted: oklch(0.27 0
|
|
1598
|
-
--muted-foreground: oklch(0.71 0
|
|
1599
|
-
--accent: oklch(0.
|
|
1600
|
-
--accent-foreground: oklch(0.
|
|
1676
|
+
--muted: oklch(0.27 0.01 275);
|
|
1677
|
+
--muted-foreground: oklch(0.71 0.02 275);
|
|
1678
|
+
--accent: oklch(0.3 0.04 275);
|
|
1679
|
+
--accent-foreground: oklch(0.97 0.01 275);
|
|
1601
1680
|
--destructive: oklch(0.7 0.19 22.23);
|
|
1602
1681
|
--border: oklch(0.27 0 0);
|
|
1603
1682
|
--input: oklch(0.27 0 0);
|
|
1604
|
-
--ring: oklch(0.
|
|
1683
|
+
--ring: oklch(0.58 0.19 275);
|
|
1684
|
+
|
|
1685
|
+
--chart-1: oklch(0.62 0.19 275);
|
|
1686
|
+
--chart-2: oklch(0.67 0.13 195);
|
|
1687
|
+
--chart-3: oklch(0.75 0.15 70);
|
|
1688
|
+
--chart-4: oklch(0.7 0.16 145);
|
|
1689
|
+
--chart-5: oklch(0.67 0.2 15);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
/* Wire the chart tokens into Tailwind's color namespace so \`bg-chart-1\`,
|
|
1693
|
+
\`text-chart-2\`, \`border-l-chart-3\`, \u2026 resolve. (\`@import "shadcn/tailwind.css"\`
|
|
1694
|
+
already maps the core tokens like \`--primary\`; this covers the ramp.) */
|
|
1695
|
+
@theme inline {
|
|
1696
|
+
--color-chart-1: var(--chart-1);
|
|
1697
|
+
--color-chart-2: var(--chart-2);
|
|
1698
|
+
--color-chart-3: var(--chart-3);
|
|
1699
|
+
--color-chart-4: var(--chart-4);
|
|
1700
|
+
--color-chart-5: var(--chart-5);
|
|
1605
1701
|
}
|
|
1606
1702
|
|
|
1607
1703
|
* {
|
|
@@ -1872,13 +1968,13 @@ export default async function SignInErrorPage({ searchParams }: SignInErrorPageP
|
|
|
1872
1968
|
)}
|
|
1873
1969
|
<div className="flex flex-wrap gap-2 pt-2">
|
|
1874
1970
|
<Button asChild>
|
|
1875
|
-
<Link href="/api/auth/sign-in?return_to=/
|
|
1971
|
+
<Link href="/api/auth/sign-in?return_to=/">
|
|
1876
1972
|
<RefreshCw aria-hidden="true" />
|
|
1877
1973
|
Try again
|
|
1878
1974
|
</Link>
|
|
1879
1975
|
</Button>
|
|
1880
1976
|
<Button asChild variant="ghost">
|
|
1881
|
-
<Link href="/">Back to
|
|
1977
|
+
<Link href="/">Back to home</Link>
|
|
1882
1978
|
</Button>
|
|
1883
1979
|
</div>
|
|
1884
1980
|
</CardContent>
|
|
@@ -1960,10 +2056,10 @@ dashwise field remove <table> <slug>
|
|
|
1960
2056
|
dashwise field set-type <table> <slug> <type>
|
|
1961
2057
|
\`\`\`
|
|
1962
2058
|
|
|
1963
|
-
Allowed field types: \`text\`, \`long_text\`, \`number\`, \`boolean\`,
|
|
2059
|
+
Allowed field types (18): \`text\`, \`long_text\`, \`number\`, \`boolean\`,
|
|
1964
2060
|
\`date\`, \`datetime\`, \`single_select\`, \`multi_select\`,
|
|
1965
2061
|
\`link_row\`, \`file\`, \`multi_file\`, \`email\`, \`url\`,
|
|
1966
|
-
\`phone\`, \`formula\`, \`ai\`.
|
|
2062
|
+
\`phone\`, \`formula\`, \`ai\`, \`uuid\`, \`jsonb\`.
|
|
1967
2063
|
|
|
1968
2064
|
## Read + write data via the generated SDK
|
|
1969
2065
|
|
|
@@ -1978,8 +2074,10 @@ import { db, qb } from '@dashai/generated';
|
|
|
1978
2074
|
const rows = await db.<table>.list();
|
|
1979
2075
|
await db.<table>.create({ /* fields */ });
|
|
1980
2076
|
|
|
1981
|
-
// Query builder (
|
|
1982
|
-
|
|
2077
|
+
// Query builder \u2014 a per-table getter (NOT a \`.from()\` call); \`.where()\`
|
|
2078
|
+
// takes a single clause object (field \u2192 value or field \u2192 operator), and
|
|
2079
|
+
// \`.execute()\` runs it. Chain \`.orderBy()\` / \`.limit()\` / \`.count()\` etc.
|
|
2080
|
+
const open = await qb.<table>.where({ status: { equal: 'open' } }).execute();
|
|
1983
2081
|
\`\`\`
|
|
1984
2082
|
|
|
1985
2083
|
The data plane authenticates via the per-module scoped \`dwk_\` API key
|
|
@@ -2025,7 +2123,37 @@ In Server Components: \`await getServerSession()\` (from
|
|
|
2025
2123
|
In Client Components: \`useSession()\` (from
|
|
2026
2124
|
\`@dashai/sdk/auth/client\`), wrapped in \`<AuthProvider>\` (already
|
|
2027
2125
|
in \`app/layout.tsx\`).
|
|
2028
|
-
|
|
2126
|
+
${isCustom ? `
|
|
2127
|
+
## Design & home
|
|
2128
|
+
|
|
2129
|
+
You OWN the UI \u2014 layout, components, and colors. Edit the theme tokens in
|
|
2130
|
+
\`app/globals.css\` freely (they're a starting point, not a mandate), reach
|
|
2131
|
+
for any Tailwind color (\`bg-indigo-600\`, \`text-emerald-500\`, \u2026), and pull
|
|
2132
|
+
in more shadcn blocks (see the README's "Adding UI blocks").
|
|
2133
|
+
|
|
2134
|
+
The app's **home is \`app/page.tsx\`, served at \`/\`** \u2014 build the real app
|
|
2135
|
+
there. It's auth-gated: a platform user lands on it zero-click (the
|
|
2136
|
+
middleware's \`autoSignIn\` signs them in silently), and \`app/dashboard/page.tsx\`
|
|
2137
|
+
is just an extra example route. A manual "Sign in with DashWise" landing
|
|
2138
|
+
page belongs ONLY to a \`public_access_allowed\` module that wants an
|
|
2139
|
+
anonymous entry point (re-add \`'/'\` to \`middleware.ts\` publicPaths + drop
|
|
2140
|
+
the home's session guard to build one).
|
|
2141
|
+
|
|
2142
|
+
## Before you publish
|
|
2143
|
+
|
|
2144
|
+
Add \`export const dynamic = 'force-dynamic'\` to the top of ANY page that
|
|
2145
|
+
reads the session or data at render (calls \`getServerSession()\` or a
|
|
2146
|
+
\`dwk_\`-authenticated data call). Without it, \`next build\` statically
|
|
2147
|
+
prerenders the page with NO DashWise env vars, the SDK throws
|
|
2148
|
+
\`missing apiBaseUrl\`, and the platform's first publish fails with an
|
|
2149
|
+
opaque \`BUILD_FAILED\`. The scaffolded \`app/page.tsx\` + \`app/dashboard/page.tsx\`
|
|
2150
|
+
already have it \u2014 any new session-gated page you add needs it too.
|
|
2151
|
+
|
|
2152
|
+
A passing local \`npm run build\` is NOT proof: your \`.env.local\` supplies
|
|
2153
|
+
the env the platform build won't have, so it masks the failure. \`GET\` route
|
|
2154
|
+
handlers under \`app/api/*\` are dynamic by default in Next 15, so they don't
|
|
2155
|
+
hit this trap.
|
|
2156
|
+
` : ""}
|
|
2029
2157
|
## Local dev lifecycle
|
|
2030
2158
|
|
|
2031
2159
|
\`\`\`
|
|
@@ -9943,7 +10071,7 @@ fieldCmd.command("set-type <table-slug> <field-slug> <new-type>").description(
|
|
|
9943
10071
|
}
|
|
9944
10072
|
})();
|
|
9945
10073
|
function getVersion() {
|
|
9946
|
-
return "0.
|
|
10074
|
+
return "0.14.1";
|
|
9947
10075
|
}
|
|
9948
10076
|
function parseIntOption(value) {
|
|
9949
10077
|
const n = Number.parseInt(value, 10);
|