@reliverse/rse
Version:
@reliverse/rse is your all-in-one companion for bootstrapping and improving any kind of projects (especially web apps built with frameworks like Next.js) — whether you're kicking off something new or upgrading an existing app. It is also a little AI-power
213 lines (212 loc) • 7.09 kB
JavaScript
import { re } from "@reliverse/relico";
import { relinka } from "@reliverse/relinka";
import { useSpinner } from "@reliverse/rempts";
import { listen } from "async-listen";
import http from "http";
import { customAlphabet } from "nanoid";
import "dotenv/config";
import { setTimeout } from "node:timers";
import open from "open";
import url from "url";
import { cliDomainDocs, memoryPath } from "../constants.js";
import { showAnykeyPrompt } from "../init/use-template/cp-modules/cli-main-modules/modules/showAnykeyPrompt.js";
import { getOrCreateReliverseMemory } from "../utils/reliverseMemory.js";
import { updateReliverseMemory } from "../utils/reliverseMemory.js";
class UserCancellationError extends Error {
constructor(message) {
super(message);
this.name = "UserCancellationError";
}
}
const nanoid = customAlphabet("123456789QAZWSXEDCRFVTGBYHNUJMIKOLP", 5);
export async function auth({
isDev,
useLocalhost
}) {
relinka("info", "Let's authenticate you...");
const spinner = useSpinner({
text: "Waiting for user confirmation..."
}).start();
try {
const server = http.createServer();
let port;
try {
const serverListen = await listen(server, {
port: 0,
host: "localhost"
});
port = serverListen.port;
relinka("verbose", `Local server listening on http://localhost:${port}`);
} catch (listenError) {
relinka(
"error",
"Failed to start local server:",
listenError instanceof Error ? listenError.message : String(listenError)
);
throw listenError;
}
const authPromise = new Promise((resolve, reject) => {
server.on("request", (req, res) => {
relinka("verbose", `Received ${req.method} request on ${req.url}`);
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
res.setHeader(
"Access-Control-Allow-Headers",
"Content-Type, Authorization"
);
if (req.method === "OPTIONS") {
relinka("verbose", "Handling OPTIONS request");
res.writeHead(200);
res.end();
} else if (req.method === "GET") {
const parsedUrl = url.parse(req.url ?? "", true);
const queryParams = parsedUrl.query;
relinka(
"verbose",
`Parsed query parameters: ${JSON.stringify(queryParams)}`
);
if (queryParams.cancelled) {
relinka("verbose", "User cancelled the login process...");
relinka("verbose", "Sleep 2s to finish the fetch process...");
void new Promise((r) => setTimeout(r, 2e3)).then(() => {
res.writeHead(200);
res.end();
server.close();
reject(
new UserCancellationError("Login process cancelled by user.")
);
});
return;
} else {
relinka(
"verbose",
`Received authentication data: ${JSON.stringify(queryParams)}`
);
res.writeHead(200);
res.end();
resolve(queryParams);
}
} else {
relinka("error", `Unhandled request method: ${req.method}`);
res.writeHead(405);
res.end();
}
});
server.on("error", (error) => {
relinka(
"error",
"Local server encountered an error:",
error instanceof Error ? error.message : String(error)
);
reject(error);
});
});
const redirect = `http://localhost:${port}`;
const code = nanoid();
const clientUrl = isDev ? useLocalhost ? "http://localhost:3000" : "https://reliverse.org" : "https://reliverse.org";
relinka("verbose", `Using client URL: ${clientUrl}`);
const confirmationUrl = new URL(`${clientUrl}/auth/confirm`);
confirmationUrl.searchParams.append("code", code);
confirmationUrl.searchParams.append("redirect", redirect);
process.stdout.write("\x1B[2K\r");
relinka(
"log",
"The following URL will be opened in your default browser (use Ctrl+Click to open):",
confirmationUrl.toString()
);
try {
await open(confirmationUrl.toString());
relinka("verbose", "Opened browser with confirmation URL.");
} catch (error) {
relinka(
"error",
"Failed to open the browser automatically:",
error instanceof Error ? error.message : String(error)
);
relinka(
"error",
"Please manually open the following URL in your browser:",
confirmationUrl.toString()
);
}
spinner.setText(
` Please visit it and confirm there if you see the same code: ${re.bold(
code
)}`
);
const authTimeout = setTimeout(
() => {
relinka("error", "Authentication timed out.");
server.close(() => {
relinka("error", "Local server closed due to timeout.");
process.exit(1);
});
},
5 * 60 * 1e3
);
try {
const authData = await authPromise;
clearTimeout(authTimeout);
relinka(
"verbose",
`Authentication data received: ${JSON.stringify(authData)}`
);
if (authData.cancelled) {
throw new UserCancellationError("Login process cancelled by user.");
}
await updateReliverseMemory({
code: authData.code,
key: authData.key
});
server.close(() => {
relinka(
"verbose",
"Wrote auth data to memory. To view it, type:",
`code ${memoryPath}`
);
relinka(
"verbose",
"Local server closed after successful authentication."
);
});
spinner.stop();
relinka("log", cliDomainDocs);
return;
} catch (error) {
clearTimeout(authTimeout);
if (error instanceof UserCancellationError) {
spinner.setText("Login cancelled. See you next time \u{1F44B}");
server.close(() => {
relinka("verbose", "Local server closed due to user cancellation.");
process.exit(0);
});
} else {
server.close(() => {
relinka(
"verbose",
"Local server closed due to authentication failure."
);
});
spinner.stop();
throw error;
}
}
} catch (error) {
spinner.stop();
relinka("error", "Authentication failed!");
throw error;
}
}
export async function authCheck(isDev, memory, useLocalhost) {
const isAuthenticated = memory.code && memory.code !== "" && memory.key && memory.key !== "";
if (!isAuthenticated) {
await showAnykeyPrompt();
await auth({ isDev, useLocalhost });
const updatedMemory = await getOrCreateReliverseMemory();
const authSuccess = updatedMemory.code && updatedMemory.code !== "" && updatedMemory.key && updatedMemory.key !== "";
if (!authSuccess) {
relinka("error", "Authentication failed. Please try again.");
process.exit(1);
}
}
}