wiki-viewer 1.2.0 → 1.4.0
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/auth-config/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/system/auth-config/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/system/auth-config/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/system/auth-config/route.js +14 -0
- package/.next/standalone/.next/server/app/api/system/auth-config/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/system/auth-config/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/system/auth-config/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/signin/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/signin.html +1 -1
- package/.next/standalone/.next/server/app/signin.rsc +2 -2
- package/.next/standalone/.next/server/app/signin.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/signin.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/signin.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/signin.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/signin.segments/signin/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/signin.segments/signin.segment.rsc +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d89o98._.js +9 -9
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jhr.ex._.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_auth-config_route_actions_01am2cf.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_app_signin_page_tsx_1143y49._.js +1 -1
- package/.next/standalone/.next/server/functions-config-manifest.json +1 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/0mj_f8ncf2tks.js +1 -0
- package/.next/standalone/README.md +19 -2
- package/.next/standalone/bin/wiki-viewer.js +182 -0
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/src/app/api/system/auth-config/route.ts +22 -0
- package/.next/standalone/src/app/signin/page.tsx +27 -13
- package/.next/standalone/src/lib/auth/server.ts +17 -1
- package/README.md +19 -2
- package/bin/wiki-viewer.js +182 -0
- package/package.json +1 -1
- package/.next/standalone/.next/static/chunks/0jf_r0h67nbyh.js +0 -1
- /package/.next/standalone/.next/static/{Rg1RfvRWNzfcK3n1DDIQs → NrsmW_X8LPcbZaXnFEEgh}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{Rg1RfvRWNzfcK3n1DDIQs → NrsmW_X8LPcbZaXnFEEgh}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{Rg1RfvRWNzfcK3n1DDIQs → NrsmW_X8LPcbZaXnFEEgh}/_ssgManifest.js +0 -0
|
@@ -11,19 +11,20 @@ export default function SignInPage() {
|
|
|
11
11
|
const [error, setError] = useState<string | null>(null);
|
|
12
12
|
const [loading, setLoading] = useState(false);
|
|
13
13
|
const [hasGoogle, setHasGoogle] = useState(false);
|
|
14
|
+
const [passwordAuth, setPasswordAuth] = useState(true);
|
|
14
15
|
const [callbackURL, setCallbackURL] = useState("/");
|
|
15
16
|
|
|
16
17
|
useEffect(() => {
|
|
17
|
-
//
|
|
18
|
-
fetch("/api/auth
|
|
18
|
+
// Learn which auth methods to show. Public endpoint, no session needed.
|
|
19
|
+
fetch("/api/system/auth-config", { credentials: "include" })
|
|
19
20
|
.then((r) => r.json())
|
|
20
21
|
.then((data: unknown) => {
|
|
21
|
-
if (
|
|
22
|
-
data
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
if (data && typeof data === "object") {
|
|
23
|
+
const cfg = data as { google?: boolean; passwordAuth?: boolean };
|
|
24
|
+
setHasGoogle(Boolean(cfg.google));
|
|
25
|
+
// Default to showing the password form unless told otherwise, so a
|
|
26
|
+
// failed fetch never leaves the user with no way to sign in.
|
|
27
|
+
setPasswordAuth(cfg.passwordAuth !== false);
|
|
27
28
|
}
|
|
28
29
|
})
|
|
29
30
|
.catch(() => {});
|
|
@@ -119,14 +120,24 @@ export default function SignInPage() {
|
|
|
119
120
|
</svg>
|
|
120
121
|
Continue with Google
|
|
121
122
|
</button>
|
|
122
|
-
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
{passwordAuth && (
|
|
124
|
+
<div className="my-4 flex items-center gap-3">
|
|
125
|
+
<hr className="flex-1 border-border" />
|
|
126
|
+
<span className="text-xs text-muted-foreground">or</span>
|
|
127
|
+
<hr className="flex-1 border-border" />
|
|
128
|
+
</div>
|
|
129
|
+
)}
|
|
127
130
|
</>
|
|
128
131
|
)}
|
|
129
132
|
|
|
133
|
+
{!passwordAuth && !hasGoogle && (
|
|
134
|
+
<p className="rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
|
135
|
+
No sign-in method is configured. Set GOOGLE_CLIENT_ID and
|
|
136
|
+
GOOGLE_CLIENT_SECRET, or enable email/password auth.
|
|
137
|
+
</p>
|
|
138
|
+
)}
|
|
139
|
+
|
|
140
|
+
{passwordAuth && (
|
|
130
141
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
131
142
|
{mode === "signup" && (
|
|
132
143
|
<div>
|
|
@@ -197,7 +208,9 @@ export default function SignInPage() {
|
|
|
197
208
|
: "Sign in"}
|
|
198
209
|
</button>
|
|
199
210
|
</form>
|
|
211
|
+
)}
|
|
200
212
|
|
|
213
|
+
{passwordAuth && (
|
|
201
214
|
<p className="mt-4 text-center text-sm text-muted-foreground">
|
|
202
215
|
{mode === "signin" ? "No account?" : "Already have an account?"}{" "}
|
|
203
216
|
<button
|
|
@@ -211,6 +224,7 @@ export default function SignInPage() {
|
|
|
211
224
|
{mode === "signin" ? "Sign up" : "Sign in"}
|
|
212
225
|
</button>
|
|
213
226
|
</p>
|
|
227
|
+
)}
|
|
214
228
|
</div>
|
|
215
229
|
</div>
|
|
216
230
|
);
|
|
@@ -75,13 +75,29 @@ if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
|
|
|
75
75
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
+
const hasSocialProvider = Object.keys(socialProviders).length > 0;
|
|
79
|
+
|
|
80
|
+
// Email/password can be turned off (AUTH_DISABLE_PASSWORD=1) so an operator can
|
|
81
|
+
// require Google sign-in only. Guard against locking everyone out: if no social
|
|
82
|
+
// provider is configured, password auth stays on regardless, otherwise there
|
|
83
|
+
// would be no way to sign in at all.
|
|
84
|
+
const passwordDisabled =
|
|
85
|
+
process.env.AUTH_DISABLE_PASSWORD === "1" || process.env.AUTH_DISABLE_PASSWORD === "true";
|
|
86
|
+
export const passwordAuthEnabled = !(passwordDisabled && hasSocialProvider);
|
|
87
|
+
if (passwordDisabled && !hasSocialProvider) {
|
|
88
|
+
console.warn(
|
|
89
|
+
"[wiki-viewer] AUTH_DISABLE_PASSWORD is set but no social provider is configured. " +
|
|
90
|
+
"Keeping email/password enabled so you are not locked out. Configure GOOGLE_CLIENT_ID/" +
|
|
91
|
+
"GOOGLE_CLIENT_SECRET to disable password auth.",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
78
94
|
|
|
79
95
|
export const auth = betterAuth({
|
|
80
96
|
database: db,
|
|
81
97
|
secret: resolveSecret(),
|
|
82
98
|
baseURL: process.env.BETTER_AUTH_URL,
|
|
83
99
|
emailAndPassword: {
|
|
84
|
-
enabled:
|
|
100
|
+
enabled: passwordAuthEnabled,
|
|
85
101
|
requireEmailVerification: false,
|
|
86
102
|
autoSignIn: true,
|
|
87
103
|
},
|
package/README.md
CHANGED
|
@@ -47,7 +47,11 @@ Single-user, no-auth mode still works. Auth turns on automatically once anyone s
|
|
|
47
47
|
## Quick start
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
#
|
|
50
|
+
# Guided setup: directory, host/port, HTTPS, app settings, and optional
|
|
51
|
+
# install as a reboot-persistent service. Recommended for first run.
|
|
52
|
+
npx wiki-viewer init
|
|
53
|
+
|
|
54
|
+
# Or run directly. Point it at a directory
|
|
51
55
|
npx wiki-viewer ~/notes
|
|
52
56
|
|
|
53
57
|
# No directory? Pick one in the browser
|
|
@@ -57,6 +61,8 @@ npx wiki-viewer
|
|
|
57
61
|
npx wiki-viewer ~/notes --https
|
|
58
62
|
```
|
|
59
63
|
|
|
64
|
+
The wizard walks you through every option and writes `~/.wiki-viewer/config.json`, so you do not have to remember flags. You can re-run it any time, or edit the config with `wiki-viewer config set`.
|
|
65
|
+
|
|
60
66
|
Open **http://localhost:3000** (or **https://localhost:3000** with `--https`).
|
|
61
67
|
|
|
62
68
|
To switch the served directory later, click **Change** at the bottom of the sidebar and pick a new folder. No restart needed.
|
|
@@ -173,7 +179,18 @@ wiki-viewer uses [Better Auth](https://better-auth.com) with SQLite. State lives
|
|
|
173
179
|
Sign-in providers:
|
|
174
180
|
|
|
175
181
|
- **Google OAuth.** Enabled when both `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` are set.
|
|
176
|
-
- **Email + password.**
|
|
182
|
+
- **Email + password.** Enabled by default. No verification email is sent.
|
|
183
|
+
|
|
184
|
+
### Require Google sign-in only
|
|
185
|
+
|
|
186
|
+
To turn off email/password and force Google OAuth, set `AUTH_DISABLE_PASSWORD=1`:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
wiki-viewer config set AUTH_DISABLE_PASSWORD=1
|
|
190
|
+
wiki-viewer service restart
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The sign-in page then shows only the Google button. As a safety guard, this is ignored unless a Google provider is configured, so you cannot lock yourself out: if `GOOGLE_CLIENT_ID`/`GOOGLE_CLIENT_SECRET` are missing, email/password stays on and a warning is logged.
|
|
177
194
|
|
|
178
195
|
### Email allowlist
|
|
179
196
|
|
package/bin/wiki-viewer.js
CHANGED
|
@@ -7,6 +7,8 @@ import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from "node
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import os from "node:os";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { createInterface } from "node:readline";
|
|
11
|
+
import { stdin, stdout } from "node:process";
|
|
10
12
|
|
|
11
13
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
14
|
const appRoot = path.resolve(__dirname, "..");
|
|
@@ -34,6 +36,7 @@ function printUsage() {
|
|
|
34
36
|
console.error(" -e, --env <KEY=VALUE> Set an app env var (repeatable; persisted with service install)");
|
|
35
37
|
console.error("");
|
|
36
38
|
console.error("Commands:");
|
|
39
|
+
console.error(" init Interactive setup wizard (dir, host/port, https, env, service)");
|
|
37
40
|
console.error(" service install [dir] [options] Install as a user service (persists across reboot)");
|
|
38
41
|
console.error(" service uninstall Remove the user service");
|
|
39
42
|
console.error(" service status Show service status");
|
|
@@ -46,6 +49,7 @@ function printUsage() {
|
|
|
46
49
|
console.error(" update Update wiki-viewer to the latest version and restart");
|
|
47
50
|
console.error("");
|
|
48
51
|
console.error("Examples:");
|
|
52
|
+
console.error(" wiki-viewer init");
|
|
49
53
|
console.error(" wiki-viewer ~/notes");
|
|
50
54
|
console.error(" wiki-viewer ~/notes --https");
|
|
51
55
|
console.error(" wiki-viewer ~/notes -p 8080 -H 0.0.0.0");
|
|
@@ -689,6 +693,171 @@ function update() {
|
|
|
689
693
|
console.log("Update complete.");
|
|
690
694
|
}
|
|
691
695
|
|
|
696
|
+
// ── interactive setup wizard ──────────────────────────────────────────────
|
|
697
|
+
|
|
698
|
+
// Common app env vars worth offering during setup. Anything not listed can
|
|
699
|
+
// still be added later with `wiki-viewer config set KEY=VALUE`.
|
|
700
|
+
const WIZARD_ENV_VARS = [
|
|
701
|
+
{ key: "AUTH_ALLOWED_DOMAIN", hint: "Restrict signup to an email domain, e.g. example.com" },
|
|
702
|
+
{ key: "AUTH_ALLOWED_EMAILS", hint: "Restrict signup to specific emails (comma-separated)" },
|
|
703
|
+
{ key: "GOOGLE_CLIENT_ID", hint: "Google OAuth client ID (enables Google sign-in)" },
|
|
704
|
+
{ key: "GOOGLE_CLIENT_SECRET", hint: "Google OAuth client secret" },
|
|
705
|
+
{ key: "AUTH_DISABLE_PASSWORD", hint: "Set to 1 to allow Google sign-in only (requires Google keys)" },
|
|
706
|
+
{ key: "WIKI_OWNER_HOSTS", hint: "Extra hostnames trusted for the AI panel owner cookie" },
|
|
707
|
+
{ key: "AGENT_RATE_LIMIT", hint: "Per-minute agent API rate limit (default 60)" },
|
|
708
|
+
];
|
|
709
|
+
|
|
710
|
+
// A prompt helper that survives piped (non-TTY) stdin. node:readline/promises
|
|
711
|
+
// closes the stream between awaited questions when input is piped, so we buffer
|
|
712
|
+
// every line via the 'line' event and hand them out one at a time instead.
|
|
713
|
+
function makePrompter() {
|
|
714
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
715
|
+
const queue = [];
|
|
716
|
+
const waiters = [];
|
|
717
|
+
let closed = false;
|
|
718
|
+
|
|
719
|
+
rl.on("line", (line) => {
|
|
720
|
+
if (waiters.length) waiters.shift()(line);
|
|
721
|
+
else queue.push(line);
|
|
722
|
+
});
|
|
723
|
+
rl.on("close", () => {
|
|
724
|
+
closed = true;
|
|
725
|
+
while (waiters.length) waiters.shift()(null);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
const nextLine = () =>
|
|
729
|
+
new Promise((resolve) => {
|
|
730
|
+
if (queue.length) resolve(queue.shift());
|
|
731
|
+
else if (closed) resolve(null);
|
|
732
|
+
else waiters.push(resolve);
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
const prompt = async (text) => {
|
|
736
|
+
stdout.write(text);
|
|
737
|
+
const line = await nextLine();
|
|
738
|
+
return line == null ? "" : line;
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
return { prompt, close: () => rl.close() };
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
async function runWizard() {
|
|
745
|
+
const io = makePrompter();
|
|
746
|
+
const existing = loadConfig();
|
|
747
|
+
|
|
748
|
+
const ask = async (label, def) => {
|
|
749
|
+
const suffix = def ? ` [${def}]` : "";
|
|
750
|
+
const answer = (await io.prompt(`${label}${suffix}: `)).trim();
|
|
751
|
+
return answer || def || "";
|
|
752
|
+
};
|
|
753
|
+
const askYesNo = async (label, defYes) => {
|
|
754
|
+
const def = defYes ? "Y/n" : "y/N";
|
|
755
|
+
const answer = (await io.prompt(`${label} [${def}]: `)).trim().toLowerCase();
|
|
756
|
+
if (!answer) return defYes;
|
|
757
|
+
return answer === "y" || answer === "yes";
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
try {
|
|
761
|
+
console.log("\nwiki-viewer setup\n");
|
|
762
|
+
console.log("Answer a few questions. Press Enter to accept the [default].\n");
|
|
763
|
+
|
|
764
|
+
// 1. directory
|
|
765
|
+
const dirInput = await ask(
|
|
766
|
+
"Directory to serve (blank = choose later in the browser)",
|
|
767
|
+
existing.rootDir ?? process.cwd(),
|
|
768
|
+
);
|
|
769
|
+
const rootDir = dirInput ? path.resolve(dirInput) : null;
|
|
770
|
+
if (rootDir && !existsSync(rootDir)) {
|
|
771
|
+
console.log(` Note: ${rootDir} does not exist yet; it will be served once created.`);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// 2. host
|
|
775
|
+
const host = await ask(
|
|
776
|
+
"Host to bind (localhost for this machine only, 0.0.0.0 for the network)",
|
|
777
|
+
existing.host ?? "localhost",
|
|
778
|
+
);
|
|
779
|
+
|
|
780
|
+
// 3. port
|
|
781
|
+
const port = await ask("Port", String(existing.port ?? "3000"));
|
|
782
|
+
|
|
783
|
+
// 4. https
|
|
784
|
+
const httpsDefault = existing.https ?? (!isLocalHost(host) && host !== "0.0.0.0");
|
|
785
|
+
const useHttps = await askYesNo(
|
|
786
|
+
"Enable HTTPS? (recommended for any non-localhost access)",
|
|
787
|
+
Boolean(httpsDefault),
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
// 5. app env vars
|
|
791
|
+
const env = { ...(existing.env ?? {}) };
|
|
792
|
+
const wantEnv = await askYesNo(
|
|
793
|
+
"\nConfigure app settings now? (OAuth, signup allowlist, rate limit)",
|
|
794
|
+
false,
|
|
795
|
+
);
|
|
796
|
+
if (wantEnv) {
|
|
797
|
+
console.log("Leave blank to skip a setting.\n");
|
|
798
|
+
for (const { key, hint } of WIZARD_ENV_VARS) {
|
|
799
|
+
const cur = env[key];
|
|
800
|
+
const val = await ask(` ${key} — ${hint}`, cur);
|
|
801
|
+
if (val) env[key] = val;
|
|
802
|
+
else delete env[key];
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Build and preview the config.
|
|
807
|
+
const cfg = {
|
|
808
|
+
rootDir,
|
|
809
|
+
host,
|
|
810
|
+
port,
|
|
811
|
+
https: useHttps,
|
|
812
|
+
...(Object.keys(env).length ? { env } : {}),
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
console.log("\nConfiguration:");
|
|
816
|
+
console.log(` directory : ${rootDir ?? "(choose in browser)"}`);
|
|
817
|
+
console.log(` host : ${host}`);
|
|
818
|
+
console.log(` port : ${port}`);
|
|
819
|
+
console.log(` https : ${useHttps}`);
|
|
820
|
+
if (cfg.env) console.log(` app env : ${Object.keys(cfg.env).join(", ")}`);
|
|
821
|
+
|
|
822
|
+
// Secure-context guidance before committing.
|
|
823
|
+
const { warnings } = computeServerEnv({ host, port: String(port), useHttps, configEnv: cfg.env ?? {} });
|
|
824
|
+
for (const w of warnings) console.log(`\n⚠️ ${w}`);
|
|
825
|
+
|
|
826
|
+
// 6. what to do now
|
|
827
|
+
console.log("");
|
|
828
|
+
console.log("What next?");
|
|
829
|
+
console.log(" 1) Install as a service (starts now and on every reboot)");
|
|
830
|
+
console.log(" 2) Run once now (foreground)");
|
|
831
|
+
console.log(" 3) Save config only");
|
|
832
|
+
const choice = (await io.prompt("Choose [1/2/3]: ")).trim() || "1";
|
|
833
|
+
|
|
834
|
+
saveConfig(cfg);
|
|
835
|
+
console.log(`\nSaved ${configPath}`);
|
|
836
|
+
|
|
837
|
+
io.close();
|
|
838
|
+
|
|
839
|
+
if (choice === "1") {
|
|
840
|
+
const p = platform();
|
|
841
|
+
if (!p) {
|
|
842
|
+
console.log("Service install is only supported on Linux and macOS.");
|
|
843
|
+
console.log("Run it yourself with: wiki-viewer service run");
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (p === "linux") installSystemd();
|
|
847
|
+
else installLaunchd();
|
|
848
|
+
} else if (choice === "2") {
|
|
849
|
+
start(resolveRunOptions([]));
|
|
850
|
+
} else {
|
|
851
|
+
console.log("\nStart it any time with:");
|
|
852
|
+
console.log(" wiki-viewer service install # persistent service");
|
|
853
|
+
console.log(" wiki-viewer service run # run from this config");
|
|
854
|
+
}
|
|
855
|
+
} finally {
|
|
856
|
+
// io may already be closed; closing twice is a no-op.
|
|
857
|
+
io.close();
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
692
861
|
// ── dispatch ──────────────────────────────────────────────────────────────
|
|
693
862
|
|
|
694
863
|
const argv = process.argv.slice(2);
|
|
@@ -720,10 +889,23 @@ switch (cmd) {
|
|
|
720
889
|
case "config":
|
|
721
890
|
configCommand(rest);
|
|
722
891
|
break;
|
|
892
|
+
case "init":
|
|
893
|
+
case "setup":
|
|
894
|
+
runWizard();
|
|
895
|
+
break;
|
|
723
896
|
case "update":
|
|
724
897
|
update();
|
|
725
898
|
break;
|
|
726
899
|
default:
|
|
900
|
+
if (argv.includes("--setup") || argv.includes("--init")) {
|
|
901
|
+
runWizard();
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
727
904
|
// No recognized command → ad-hoc serve (directory + flags only).
|
|
905
|
+
// On a bare interactive run, point users at the guided setup first.
|
|
906
|
+
if (argv.length === 0 && stdin.isTTY) {
|
|
907
|
+
console.log("Tip: run `wiki-viewer init` for guided setup (directory, host/port, HTTPS,");
|
|
908
|
+
console.log(" app settings, and optional install as a reboot-persistent service).\n");
|
|
909
|
+
}
|
|
728
910
|
start(resolveServeOptions(argv));
|
|
729
911
|
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,897339,e=>{"use strict";var t=e.i(44501),r=e.i(101685),n=e.i(199075);e.s(["default",0,function(){let[e,s]=(0,r.useState)("signin"),[i,a]=(0,r.useState)(""),[o,l]=(0,r.useState)(""),[d,c]=(0,r.useState)(""),[u,m]=(0,r.useState)(null),[g,x]=(0,r.useState)(!1),[f,p]=(0,r.useState)(!1),[h,b]=(0,r.useState)("/");async function j(t){t.preventDefault(),m(null),x(!0);try{if("signup"===e){let e=await n.authClient.signUp.email({email:i,password:o,name:d,callbackURL:h});e.error?m(e.error.message??"Sign-up failed"):window.location.href=h}else{let e=await n.authClient.signIn.email({email:i,password:o,callbackURL:h});if(e.error){let t=e.error.status;429===t?m("Too many sign-in attempts. Wait a minute and try again."):m(e.error.message??"Sign-in failed")}else window.location.href=h}}catch(e){m(e instanceof Error?e.message:"Unexpected error")}finally{x(!1)}}async function y(){m(null),x(!0);try{await n.authClient.signIn.social({provider:"google",callbackURL:h})}catch(e){m(e instanceof Error?e.message:"Google sign-in failed"),x(!1)}}return(0,r.useEffect)(()=>{fetch("/api/auth/list-providers",{credentials:"include"}).then(e=>e.json()).then(e=>{e&&"object"==typeof e&&"google"in e&&p(!0)}).catch(()=>{});let e=new URLSearchParams(window.location.search).get("next");e&&e.startsWith("/")&&b(e)},[]),(0,t.jsx)("div",{className:"min-h-screen flex items-center justify-center bg-background",children:(0,t.jsxs)("div",{className:"w-full max-w-sm rounded-lg border border-border bg-card p-8 shadow-sm",children:[(0,t.jsx)("h1",{className:"mb-6 text-xl font-semibold text-foreground",children:"Sign in to wiki-viewer"}),f&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("button",{type:"button",onClick:y,disabled:g,className:"flex w-full items-center justify-center gap-2 rounded-md border border-border px-4 py-2 text-sm font-medium hover:bg-accent disabled:opacity-50",children:[(0,t.jsxs)("svg",{viewBox:"0 0 24 24",className:"h-4 w-4","aria-hidden":"true",children:[(0,t.jsx)("path",{fill:"#4285F4",d:"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"}),(0,t.jsx)("path",{fill:"#34A853",d:"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"}),(0,t.jsx)("path",{fill:"#FBBC05",d:"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z"}),(0,t.jsx)("path",{fill:"#EA4335",d:"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"})]}),"Continue with Google"]}),(0,t.jsxs)("div",{className:"my-4 flex items-center gap-3",children:[(0,t.jsx)("hr",{className:"flex-1 border-border"}),(0,t.jsx)("span",{className:"text-xs text-muted-foreground",children:"or"}),(0,t.jsx)("hr",{className:"flex-1 border-border"})]})]}),(0,t.jsxs)("form",{onSubmit:j,className:"space-y-4",children:["signup"===e&&(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"mb-1 block text-sm font-medium text-foreground",htmlFor:"name",children:"Name"}),(0,t.jsx)("input",{id:"name",type:"text",autoComplete:"name",value:d,onChange:e=>c(e.target.value),required:!0,className:"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"mb-1 block text-sm font-medium text-foreground",htmlFor:"email",children:"Email"}),(0,t.jsx)("input",{id:"email",type:"email",autoComplete:"email",value:i,onChange:e=>a(e.target.value),required:!0,className:"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"mb-1 block text-sm font-medium text-foreground",htmlFor:"password",children:"Password"}),(0,t.jsx)("input",{id:"password",type:"password",autoComplete:"signup"===e?"new-password":"current-password",value:o,onChange:e=>l(e.target.value),required:!0,minLength:8,className:"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"})]}),u&&(0,t.jsx)("p",{className:"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive",children:u}),(0,t.jsx)("button",{type:"submit",disabled:g,className:"w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50",children:g?"signup"===e?"Creating account...":"Signing in...":"signup"===e?"Create account":"Sign in"})]}),(0,t.jsxs)("p",{className:"mt-4 text-center text-sm text-muted-foreground",children:["signin"===e?"No account?":"Already have an account?"," ",(0,t.jsx)("button",{type:"button",onClick:()=>{s("signin"===e?"signup":"signin"),m(null)},className:"font-medium text-foreground underline-offset-4 hover:underline",children:"signin"===e?"Sign up":"Sign in"})]})]})})}])}]);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|